From 05dd2335832e15367b54f994a65e75c25a2c8552 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Tue, 10 Aug 2021 16:42:09 -0700 Subject: [PATCH] fix(cmd-api-server): plugins interfere with API server deps #1192 Migrates to the lmify package to install plugins at runtime instead of doing it via vanilla npm which was causing problems with conflicting dependency versions where the API server would want semver 7.x and one of the plugins (through some transient dependency of the plugin itself) would install semver 5.x which would then cause the API server to break down at runtime due to the breaking changes between semver 7 and 5. The magic sauce is the --prefix option of npm which, when specified instructs npm to ignore the usual parent directory traversal algorithm when evaluating/resolving dependency trees and instead just do a full installation to the specified directory path as dictated by the --prefix option. This means that we can install each plugin in their own directory the code being isolated from the API server and also from other plugins that might also interfere. Fixes #1192 Depends on #1203 Signed-off-by: Peter Somogyvari --- .cspell.json | 5 +- package.json | 1 + packages/cactus-cmd-api-server/Dockerfile | 4 +- packages/cactus-cmd-api-server/package.json | 4 +- .../src/main/typescript/api-server.ts | 116 ++-- .../main/typescript/config/config-service.ts | 10 + .../benchmark/artillery-api-benchmark.test.ts | 20 +- .../jwt-endpoint-authorization.test.ts | 10 + .../integration/remote-plugin-imports.test.ts | 10 + ...ig-service-example-config-validity.test.ts | 14 +- ...ll-basic-plugin-consortium-manual.test.ts} | 71 ++- ...tall-basic-plugin-keychain-memory.test.ts} | 20 +- ...ugin-ledger-connector-fabric-0-7-0.test.ts | 98 ++++ ...ugin-ledger-connector-quorum-0-7-0.test.ts | 102 ++++ packages/cactus-cmd-api-server/tsconfig.json | 1 + .../plugin-import-with-npm-install.test.ts | 11 + .../runtime-plugin-imports.test.ts | 10 +- yarn.lock | 494 ++---------------- 18 files changed, 487 insertions(+), 514 deletions(-) rename packages/cactus-cmd-api-server/src/test/typescript/{integration/plugin-import-with-npm-install.test.ts => unit/plugins/install-basic-plugin-consortium-manual.test.ts} (66%) rename packages/cactus-cmd-api-server/src/test/typescript/{integration/runtime-plugin-imports.test.ts => unit/plugins/install-basic-plugin-keychain-memory.test.ts} (73%) create mode 100644 packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts create mode 100644 packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts diff --git a/.cspell.json b/.cspell.json index ebfbb6dcbb..57a0713224 100644 --- a/.cspell.json +++ b/.cspell.json @@ -50,6 +50,7 @@ "Keycloak", "Knetic", "LEDGERBLOCKACK", + "lmify", "LOCALMSPID", "miekg", "mitchellh", @@ -72,6 +73,7 @@ "OpenAPI", "openethereum", "organisation", + "parameterizable", "protos", "RUSTC", "Secp", @@ -91,8 +93,7 @@ "uuidv", "vscc", "wasm", - "Xdai", - "parameterizable" + "Xdai" ], "dictionaries": [ "typescript,node,npm,go,rust" diff --git a/package.json b/package.json index 5e92c57d33..4b588facc7 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@openapitools/openapi-generator-cli": "2.3.3", "@types/fs-extra": "9.0.11", "@types/jasminewd2": "2.0.10", + "@types/node": "15.14.7", "@types/node-fetch": "2.5.4", "@types/tape": "4.13.0", "@types/tape-promise": "4.0.1", diff --git a/packages/cactus-cmd-api-server/Dockerfile b/packages/cactus-cmd-api-server/Dockerfile index 39228aa64a..3dae23f114 100644 --- a/packages/cactus-cmd-api-server/Dockerfile +++ b/packages/cactus-cmd-api-server/Dockerfile @@ -50,8 +50,8 @@ EXPOSE 3000 4000 USER $APP_USER -RUN npm i @hyperledger/cactus-cmd-api-server@${NPM_PKG_VERSION} --production +RUN npm i @elenaizaguirre/cactus-cmd-api-server@${NPM_PKG_VERSION} ENTRYPOINT ["/sbin/tini", "--"] -CMD ["node", "node_modules/@hyperledger/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js"] +CMD ["node", "node_modules/@elenaizaguirre/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js"] HEALTHCHECK --interval=5s --timeout=5s --start-period=1s --retries=30 CMD /healthcheck.sh diff --git a/packages/cactus-cmd-api-server/package.json b/packages/cactus-cmd-api-server/package.json index 18de8646b5..076e776354 100644 --- a/packages/cactus-cmd-api-server/package.json +++ b/packages/cactus-cmd-api-server/package.json @@ -87,10 +87,11 @@ "express-jwt": "6.0.0", "express-jwt-authz": "2.4.1", "express-openapi-validator": "3.10.0", + "fs-extra": "10.0.0", "http-status-codes": "2.1.4", "jose": "1.28.1", + "lmify": "0.3.0", "node-forge": "0.10.0", - "npm": "7.19.1", "prom-client": "13.1.0", "rxjs": "7.1.0", "semver": "7.3.2", @@ -111,7 +112,6 @@ "@types/jsonwebtoken": "8.5.1", "@types/multer": "1.4.5", "@types/node-forge": "0.9.3", - "@types/npm": "2.0.32", "@types/passport": "1.0.6", "@types/passport-oauth2": "1.4.10", "@types/passport-saml": "1.1.2", diff --git a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts index c9f94af4cc..ec25467f3b 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts @@ -1,11 +1,13 @@ -import path from "path"; import type { AddressInfo } from "net"; +import type { Server as SecureServer } from "https"; +import os from "os"; +import path from "path"; import tls from "tls"; import { Server, createServer } from "http"; -import type { Server as SecureServer } from "https"; import { createServer as createSecureServer } from "https"; import { gte } from "semver"; -import npm from "npm"; +import lmify from "lmify"; +import fs from "fs-extra"; import expressHttpProxy from "express-http-proxy"; import type { Application, Request, Response, RequestHandler } from "express"; import express from "express"; @@ -41,7 +43,9 @@ import { PrometheusExporter } from "./prometheus-exporter/prometheus-exporter"; import { AuthorizerFactory } from "./authzn/authorizer-factory"; import { WatchHealthcheckV1 } from "./generated/openapi/typescript-axios"; import { WatchHealthcheckV1Endpoint } from "./web-services/watch-healthcheck-v1-endpoint"; +import { RuntimeError } from "run-time-error"; export interface IApiServerConstructorOptions { + pluginManagerOptions?: { pluginsPath: string }; pluginRegistry?: PluginRegistry; httpServerApi?: Server | SecureServer; wsServerApi?: SocketIoServer; @@ -71,6 +75,7 @@ export class ApiServer { private readonly wsApi: SocketIoServer; private readonly expressApi: Application; private readonly expressCockpit: Application; + private readonly pluginsPath: string; public prometheusExporter: PrometheusExporter; public get className(): string { @@ -127,6 +132,23 @@ export class ApiServer { label: "api-server", level: options.config.logLevel, }); + + const defaultPluginsPath = path.join( + os.tmpdir(), + "org", + "hyperledger", + "cactus", + "plugins", + ); + + const { pluginsPath } = { + ...{ pluginsPath: defaultPluginsPath }, + ...JSON.parse(this.options.config.pluginManagerOptionsJson), + ...this.options.pluginManagerOptions, + } as { pluginsPath: string }; + + this.pluginsPath = pluginsPath; + this.log.debug("pluginsPath: %o", pluginsPath); } public getPrometheusExporter(): PrometheusExporter { @@ -256,8 +278,16 @@ export class ApiServer { await this.installPluginPackage(pluginImport); + const packagePath = path.join( + this.pluginsPath, + options.instanceId, + "node_modules", + packageName, + ); + this.log.debug("Package path: %o", packagePath); + // eslint-disable-next-line @typescript-eslint/no-var-requires - const pluginPackage = require(/* webpackIgnore: true */ packageName); + const pluginPackage = require(/* webpackIgnore: true */ packagePath); const createPluginFactory = pluginPackage.createPluginFactory as PluginFactoryFactory; const pluginFactoryOptions: IPluginFactoryOptions = { @@ -276,54 +306,38 @@ export class ApiServer { const fnTag = `ApiServer#installPluginPackage()`; const { packageName: pkgName } = pluginImport; - const npmLogHandler = (message: unknown) => { - this.log.debug(`${fnTag} [npm-log]:`, message); - }; - - const cleanUpNpmLogHandler = () => { - npm.off("log", npmLogHandler); - }; - + const instanceId = pluginImport.options.instanceId; + const pluginPackageDir = path.join(this.pluginsPath, instanceId); try { - this.log.info(`Installing ${pkgName} for plugin import`, pluginImport); - npm.on("log", npmLogHandler); - - await new Promise((resolve, reject) => { - npm.load((err?: Error) => { - if (err) { - this.log.error(`${fnTag} npm load fail:`, err); - const { message, stack } = err; - reject(new Error(`${fnTag} npm load fail: ${message}: ${stack}`)); - } else { - // do not touch package.json - npm.config.set("save", false); - // do not touch package-lock.json - npm.config.set("package-lock", false); - // do not waste resources on running an audit - npm.config.set("audit", false); - // do not wast resources on rendering a progress bar - npm.config.set("progress", false); - resolve(); - } - }); - }); - - await new Promise((resolve, reject) => { - const npmInstallHandler = (errInstall?: Error, result?: unknown) => { - if (errInstall) { - this.log.error(`${fnTag} npm install failed:`, errInstall); - const { message: m, stack } = errInstall; - reject(new Error(`${fnTag} npm install fail: ${m}: ${stack}`)); - } else { - this.log.info(`Installed ${pkgName} OK`, result); - resolve(result); - } - }; - - npm.commands.install([pkgName], npmInstallHandler); - }); - } finally { - cleanUpNpmLogHandler(); + await fs.mkdirp(pluginPackageDir); + this.log.debug(`${pkgName} plugin package dir: %o`, pluginPackageDir); + } catch (ex) { + const errorMessage = + "Could not create plugin installation directory, check the file-system permissions."; + throw new RuntimeError(errorMessage, ex); + } + try { + lmify.setPackageManager("npm"); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + lmify.setRootDir(pluginPackageDir); + this.log.debug(`Installing ${pkgName} for plugin import`, pluginImport); + const out = await lmify.install([ + pkgName, + "--production", + "--audit=false", + "--progress=false", + "--fund=false", + `--prefix=${pluginPackageDir}`, + // "--ignore-workspace-root-check", + ]); + this.log.debug("%o install result: %o", pkgName, out); + if (out.exitCode !== 0) { + throw new RuntimeError("Non-zero exit code: ", JSON.stringify(out)); + } + this.log.info(`Installed ${pkgName} OK`); + } catch (ex) { + throw new RuntimeError(`${fnTag} plugin install fail: ${pkgName}`, ex); } } diff --git a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts index ebabfed631..25a6e632bf 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/config/config-service.ts @@ -26,6 +26,7 @@ convict.addFormat(FORMAT_PLUGIN_ARRAY); convict.addFormat(ipaddress); export interface ICactusApiServerOptions { + pluginManagerOptionsJson: string; authorizationProtocol: AuthorizationProtocol; authorizationConfigJson: IAuthorizationConfig; configFile: string; @@ -88,6 +89,14 @@ export class ConfigService { private static getConfigSchema(): Schema { return { + pluginManagerOptionsJson: { + doc: + "Can be used to override npm registry and authentication details for example. See https://www.npmjs.com/package/live-plugin-manager#pluginmanagerconstructoroptions-partialpluginmanageroptions for further details.", + format: "*", + default: "{}", + env: "PLUGIN_MANAGER_OPTIONS_JSON", + arg: "plugin-manager-options-json", + }, authorizationProtocol: { doc: "The name of the authorization protocol to use. Accepted values" + @@ -518,6 +527,7 @@ export class ConfigService { }; return { + pluginManagerOptionsJson: "{}", authorizationProtocol: AuthorizationProtocol.JSON_WEB_TOKEN, authorizationConfigJson, configFile: ".config.json", diff --git a/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts index cb2598037f..16e4393ab0 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/benchmark/artillery-api-benchmark.test.ts @@ -1,3 +1,9 @@ +import { promisify } from "util"; +import { unlinkSync, readFileSync } from "fs"; + +import { exec } from "child_process"; +import path from "path"; + import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; import { JWK } from "jose"; @@ -23,11 +29,6 @@ const log = LoggerProvider.getOrCreate({ label: "logger-test", }); -import { promisify } from "util"; -import { unlinkSync, readFileSync } from "fs"; - -import { exec } from "child_process"; - const shell_exec = promisify(exec); const artilleryScriptLocation = @@ -47,9 +48,18 @@ test("Start API server, and run Artillery benchmark test.", async (t: Test) => { log.info("Generating Config..."); + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/artillery-api-benchmark_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); const apiServerOptions = configService.newExampleConfig(); apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 4000; diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts index d28d3e9247..1bafbf5cd2 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/jwt-endpoint-authorization.test.ts @@ -1,3 +1,4 @@ +import path from "path"; import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; import { JWK, JWT } from "jose"; @@ -64,9 +65,18 @@ test(testCase, async (t: Test) => { }, }; + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/jwt-endpoint-authorization_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); const apiSrvOpts = configService.newExampleConfig(); apiSrvOpts.authorizationProtocol = AuthorizationProtocol.JSON_WEB_TOKEN; + apiSrvOpts.pluginManagerOptionsJson = pluginManagerOptionsJson; apiSrvOpts.authorizationConfigJson = authorizationConfig; apiSrvOpts.configFile = ""; apiSrvOpts.apiCorsDomainCsv = "*"; diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts index 9a186ea1ab..cf7935ead7 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/integration/remote-plugin-imports.test.ts @@ -17,6 +17,7 @@ import { import { DefaultApi } from "@hyperledger/cactus-plugin-keychain-vault"; import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; +import path from "path"; test("NodeJS API server + Rust plugin work together", async (t: Test) => { const vaultTestContainer = new VaultTestServer({}); @@ -57,9 +58,18 @@ test("NodeJS API server + Rust plugin work together", async (t: Test) => { }); const apiClient = new DefaultApi(configuration); + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/remote-plugin-imports_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); const apiServerOptions = configService.newExampleConfig(); apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/config/config-service-example-config-validity.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/config/config-service-example-config-validity.test.ts index e73417d289..a60f76f717 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/unit/config/config-service-example-config-validity.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/config/config-service-example-config-validity.test.ts @@ -1,17 +1,29 @@ -import { LoggerProvider } from "@hyperledger/cactus-common"; +import path from "path"; import test, { Test } from "tape-promise/tape"; +import { v4 as uuidv4 } from "uuid"; +import { LoggerProvider } from "@hyperledger/cactus-common"; import { IAuthorizationConfig } from "../../../../main/typescript/public-api"; import { ApiServer } from "../../../../main/typescript/public-api"; import { ConfigService } from "../../../../main/typescript/public-api"; test("Generates valid example config for the API server", async (t: Test) => { + const pluginsPath = path.join( + __dirname, + "../../../../../../", // walk back up to the project root + ".tmp/test/test-cmd-api-server/config-service-example-config-validity_test/", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); t.ok(configService, "Instantiated ConfigService truthy OK"); const exampleConfig = configService.newExampleConfig(); t.ok(exampleConfig, "configService.newExampleConfig() truthy OK"); + exampleConfig.pluginManagerOptionsJson = pluginManagerOptionsJson; + // FIXME - this hack should not be necessary, we need to re-think how we // do configuration parsing. The convict library may not be the path forward. exampleConfig.authorizationConfigJson = (JSON.stringify( diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts similarity index 66% rename from packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts rename to packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts index 72189e1653..94cdc17b5a 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-consortium-manual.test.ts @@ -1,3 +1,4 @@ +import path from "path"; import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; import { JWK } from "jose"; @@ -9,21 +10,25 @@ import { ConsortiumDatabase, ICactusPlugin, Configuration, + IPluginConsortium, } from "@hyperledger/cactus-core-api"; import { ApiServer, AuthorizationProtocol, ConfigService, -} from "../../../main/typescript/public-api"; +} from "../../../../main/typescript/public-api"; -import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../main/typescript/prometheus-exporter/metrics"; +import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../../main/typescript/prometheus-exporter/metrics"; -import { DefaultApi as ApiServerApi } from "../../../main/typescript/public-api"; +import { DefaultApi as ApiServerApi } from "../../../../main/typescript/public-api"; const logLevel: LogLevelDesc = "TRACE"; -test("can instal plugins at runtime based on imports", async (t: Test) => { +test("can install plugin-consortium-manual", async (t: Test) => { + const keychainId = uuidv4(); + const consortiumPluginInstanceId = uuidv4(); + // Adding a new plugin to update the prometheus metric K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS const keyPair = await JWK.generate("EC", "secp256k1", { use: "sig" }, true); const keyPairPem = keyPair.toPEM(true); @@ -35,8 +40,20 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { pluginInstance: [], }; + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/plugin-import-with-npm-install_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + const pluginManagerOptions = { pluginsPath }; + const pluginManagerOptionsJson = JSON.stringify(pluginManagerOptions); + + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; @@ -49,7 +66,7 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { type: PluginImportType.Local, options: { instanceId: uuidv4(), - keychainId: uuidv4(), + keychainId, logLevel, }, }, @@ -57,7 +74,7 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { packageName: "@hyperledger/cactus-plugin-consortium-manual", type: PluginImportType.Local, options: { - instanceId: uuidv4(), + instanceId: consortiumPluginInstanceId, keyPairPem: keyPairPem, consortiumDatabase: db, }, @@ -69,11 +86,10 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { config: config.getProperties(), }); + test.onFinish(() => apiServer.shutdown()); + const startResponse = apiServer.start(); - await t.doesNotReject( - startResponse, - "failed to start API server with dynamic plugin imports configured for it...", - ); + await t.doesNotReject(startResponse, "started API server OK"); t.ok(startResponse, "startResponse truthy OK"); const addressInfoApi = (await startResponse).addressInfoApi; @@ -141,5 +157,38 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { // e.g. remove the dummy plugin instance we just pushed pluginRegistry.plugins.pop(); - test.onFinish(() => apiServer.shutdown()); + const keychain = pluginRegistry.findOneByKeychainId(keychainId); + + const hasX1 = await keychain.has("x"); + t.false(hasX1, "hasX1 === false OK"); + + await keychain.set("x", "y"); + + const hasX2 = await keychain.has("x"); + t.true(hasX2, "hasX2 === true OK"); + + type DummyConsortiumPlugin = IPluginConsortium< + unknown, + unknown, + unknown, + unknown + >; + + // TODO - use the new getOneById implementation once + // https://github.com/hyperledger/cactus/issues/1197 + // has been resolved + const consortiumPlugin = pluginRegistry + .getPlugins() + .find( + (it) => it.getInstanceId() === consortiumPluginInstanceId, + ) as DummyConsortiumPlugin; + + t.ok(consortiumPlugin, "consortiumPlugin located via instance ID truthy OK"); + + // FIXME - uncomment this once https://github.com/hyperledger/cactus/issues/1199 + // has been resolved (and also published to npm) + // const nodeJwsRes = await consortiumPlugin.getNodeJws({}); + // t.ok(nodeJwsRes, "nodeJwsRes truthy OK"); + + t.end(); }); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts similarity index 73% rename from packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts rename to packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts index 18e5e3eea6..10faffb68e 100644 --- a/packages/cactus-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-keychain-memory.test.ts @@ -9,18 +9,32 @@ import { ApiServer, AuthorizationProtocol, ConfigService, -} from "../../../main/typescript/public-api"; +} from "../../../../main/typescript/public-api"; -import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../main/typescript/prometheus-exporter/metrics"; +import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../../main/typescript/prometheus-exporter/metrics"; -import { DefaultApi as ApiServerApi } from "../../../main/typescript/public-api"; +import { DefaultApi as ApiServerApi } from "../../../../main/typescript/public-api"; +import path from "path"; const logLevel: LogLevelDesc = "TRACE"; test("can import plugins at runtime (CLI)", async (t: Test) => { + // const pluginsPath = path.join( + // "/tmp/org/hyperledger/cactus/cmd-api-server/install-basic-plugin-keychain-memory_test", // the dir path from the root + // uuidv4(), // then a random directory to ensure proper isolation + // ); + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/runtime-plugin-imports_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); const apiServerOptions = configService.newExampleConfig(); apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts new file mode 100644 index 0000000000..5e0539d7a8 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-fabric-0-7-0.test.ts @@ -0,0 +1,98 @@ +import test, { Test } from "tape-promise/tape"; +import { v4 as uuidv4 } from "uuid"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; + +import { + ApiServer, + AuthorizationProtocol, + ConfigService, +} from "../../../../main/typescript/public-api"; + +import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../../main/typescript/prometheus-exporter/metrics"; + +import { DefaultApi as ApiServerApi } from "../../../../main/typescript/public-api"; +import path from "path"; + +const logLevel: LogLevelDesc = "TRACE"; + +test("can install plugin-ledger-connector-fabric", async (t: Test) => { + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/runtime-plugin-imports_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ + pluginsPath, + npmInstallMode: "noCache", + }); + + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; + apiServerOptions.configFile = ""; + apiServerOptions.apiCorsDomainCsv = "*"; + apiServerOptions.apiPort = 0; + apiServerOptions.cockpitPort = 0; + apiServerOptions.apiTlsEnabled = false; + apiServerOptions.plugins = [ + { + packageName: "@hyperledger/cactus-plugin-ledger-connector-fabric", + type: PluginImportType.Local, + options: { + instanceId: uuidv4(), + logLevel, + connectionProfile: {}, + peerBinary: "peer", + }, + }, + ]; + const config = configService.newExampleConfigConvict(apiServerOptions); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + + test.onFinish(() => apiServer.shutdown()); + + const startResponse = apiServer.start(); + await t.doesNotReject(startResponse, "started API server dynamic imports OK"); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoApi = (await startResponse).addressInfoApi; + const protocol = apiServerOptions.apiTlsEnabled ? "https" : "http"; + const { address, port } = addressInfoApi; + const apiHost = `${protocol}://${address}:${port}`; + t.comment( + `Metrics URL: ${apiHost}/api/v1/api-server/get-prometheus-exporter-metrics`, + ); + + const apiConfig = new Configuration({ basePath: apiHost }); + const apiClient = new ApiServerApi(apiConfig); + + { + const res = await apiClient.getPrometheusMetricsV1(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " Total number of plugins imported\n" + + "# TYPE " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " gauge\n" + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '{type="' + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '"} 1'; + t.ok(res); + t.ok(res.data); + t.equal(res.status, 200); + t.true( + res.data.includes(promMetricsOutput), + "Total 1 plugins imported as expected. RESULT OK", + ); + } +}); diff --git a/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts new file mode 100644 index 0000000000..b1ff528465 --- /dev/null +++ b/packages/cactus-cmd-api-server/src/test/typescript/unit/plugins/install-basic-plugin-ledger-connector-quorum-0-7-0.test.ts @@ -0,0 +1,102 @@ +import path from "path"; +import test, { Test } from "tape-promise/tape"; +import { v4 as uuidv4 } from "uuid"; + +import { LogLevelDesc } from "@hyperledger/cactus-common"; + +import { Configuration, PluginImportType } from "@hyperledger/cactus-core-api"; + +import { + ApiServer, + AuthorizationProtocol, + ConfigService, +} from "../../../../main/typescript/public-api"; + +import { K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS } from "../../../../main/typescript/prometheus-exporter/metrics"; + +import { DefaultApi as ApiServerApi } from "../../../../main/typescript/public-api"; + +const logLevel: LogLevelDesc = "TRACE"; + +test("can import plugins at runtime (CLI)", async (t: Test) => { + // const pluginsPath = path.join( + // "/tmp/org/hyperledger/cactus/cmd-api-server/runtime-plugin-imports_test", // the dir path from the root + // uuidv4(), // then a random directory to ensure proper isolation + // ); + + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/install-basic-plugin-ledger-connector-quorum-0-7-0_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ + pluginsPath, + npmInstallMode: "noCache", + }); + + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; + apiServerOptions.configFile = ""; + apiServerOptions.apiCorsDomainCsv = "*"; + apiServerOptions.apiPort = 0; + apiServerOptions.cockpitPort = 0; + apiServerOptions.apiTlsEnabled = false; + apiServerOptions.plugins = [ + { + packageName: "@hyperledger/cactus-plugin-ledger-connector-quorum", + type: PluginImportType.Local, + options: { + instanceId: uuidv4(), + logLevel, + rpcApiHttpHost: "localhost:8545", + }, + }, + ]; + const config = configService.newExampleConfigConvict(apiServerOptions); + + const apiServer = new ApiServer({ + config: config.getProperties(), + }); + + const startResponse = apiServer.start(); + await t.doesNotReject(startResponse, "started API server dynamic imports OK"); + t.ok(startResponse, "startResponse truthy OK"); + + const addressInfoApi = (await startResponse).addressInfoApi; + const protocol = apiServerOptions.apiTlsEnabled ? "https" : "http"; + const { address, port } = addressInfoApi; + const apiHost = `${protocol}://${address}:${port}`; + t.comment( + `Metrics URL: ${apiHost}/api/v1/api-server/get-prometheus-exporter-metrics`, + ); + + const apiConfig = new Configuration({ basePath: apiHost }); + const apiClient = new ApiServerApi(apiConfig); + + { + const res = await apiClient.getPrometheusMetricsV1(); + const promMetricsOutput = + "# HELP " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " Total number of plugins imported\n" + + "# TYPE " + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + " gauge\n" + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '{type="' + + K_CACTUS_API_SERVER_TOTAL_PLUGIN_IMPORTS + + '"} 1'; + t.ok(res); + t.ok(res.data); + t.equal(res.status, 200); + t.true( + res.data.includes(promMetricsOutput), + "Total 1 plugins imported as expected. RESULT OK", + ); + } + + test.onFinish(() => apiServer.shutdown()); +}); diff --git a/packages/cactus-cmd-api-server/tsconfig.json b/packages/cactus-cmd-api-server/tsconfig.json index 15de52f39d..59559e1334 100644 --- a/packages/cactus-cmd-api-server/tsconfig.json +++ b/packages/cactus-cmd-api-server/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "./dist/lib/", "declarationDir": "dist/types", "rootDir": "./src", + "skipLibCheck": true, "tsBuildInfoFile": "../../.build-cache/cactus-cmd-api-server.tsbuildinfo" }, "include": [ diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts index 21c3fdba68..d5fde64301 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/plugin-import-with-npm-install.test.ts @@ -1,3 +1,4 @@ +import path from "path"; import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; import { JWK } from "jose"; @@ -29,8 +30,18 @@ test("can instal plugins at runtime based on imports", async (t: Test) => { pluginInstance: [], }; + const pluginsPath = path.join( + __dirname, + "../../../../../../", // walk back up to the project root + ".tmp/test/test-cmd-api-server/plugin-import-with-npm-install_test/", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); + const configService = new ConfigService(); + const apiServerOptions = configService.newExampleConfig(); + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; diff --git a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts index 670887b0df..6879c9c0f0 100644 --- a/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts +++ b/packages/cactus-test-cmd-api-server/src/test/typescript/integration/runtime-plugin-imports.test.ts @@ -1,3 +1,4 @@ +import path from "path"; import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; @@ -13,11 +14,18 @@ import { PluginImportType } from "@hyperledger/cactus-core-api"; const logLevel: LogLevelDesc = "TRACE"; test("can import plugins at runtime (CLI)", async (t: Test) => { - // const pluginRegistry = new PluginRegistry({ plugins: [] }); + const pluginsPath = path.join( + __dirname, // start at the current file's path + "../../../../../../", // walk back up to the project root + ".tmp/test/cmd-api-server/runtime-plugin-imports_test", // the dir path from the root + uuidv4(), // then a random directory to ensure proper isolation + ); + const pluginManagerOptionsJson = JSON.stringify({ pluginsPath }); const configService = new ConfigService(); const apiServerOptions = configService.newExampleConfig(); apiServerOptions.authorizationProtocol = AuthorizationProtocol.NONE; + apiServerOptions.pluginManagerOptionsJson = pluginManagerOptionsJson; apiServerOptions.configFile = ""; apiServerOptions.apiCorsDomainCsv = "*"; apiServerOptions.apiPort = 0; diff --git a/yarn.lock b/yarn.lock index 328e064a9b..800224b916 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2780,68 +2780,12 @@ safe-buffer "^5.1.1" util.promisify "^1.0.0" -"@npmcli/arborist@^2.3.0", "@npmcli/arborist@^2.5.0", "@npmcli/arborist@^2.6.4": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-2.7.1.tgz#dc7b8a75d7469c26559675adbccae26cfcbe2d01" - integrity sha512-EGDHJs6dna/52BrStr/6aaRcMLrYxGbSjT4V3JzvoTBY9/w5i2+1KNepmsG80CAsGADdo6nuNnFwb7sDRm8ZAw== - dependencies: - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/map-workspaces" "^1.0.2" - "@npmcli/metavuln-calculator" "^1.1.0" - "@npmcli/move-file" "^1.1.0" - "@npmcli/name-from-folder" "^1.0.1" - "@npmcli/node-gyp" "^1.0.1" - "@npmcli/package-json" "^1.0.1" - "@npmcli/run-script" "^1.8.2" - bin-links "^2.2.1" - cacache "^15.0.3" - common-ancestor-path "^1.0.1" - json-parse-even-better-errors "^2.3.1" - json-stringify-nice "^1.1.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - npm-install-checks "^4.0.0" - npm-package-arg "^8.1.0" - npm-pick-manifest "^6.1.0" - npm-registry-fetch "^11.0.0" - pacote "^11.2.6" - parse-conflict-json "^1.1.1" - proc-log "^1.0.0" - promise-all-reject-late "^1.0.0" - promise-call-limit "^1.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" - semver "^7.3.5" - ssri "^8.0.1" - tar "^6.1.0" - treeverse "^1.0.4" - walk-up-path "^1.0.0" - -"@npmcli/ci-detect@^1.0.0", "@npmcli/ci-detect@^1.2.0", "@npmcli/ci-detect@^1.3.0": +"@npmcli/ci-detect@^1.0.0": version "1.3.0" resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz" integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== -"@npmcli/config@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@npmcli/config/-/config-2.2.0.tgz" - integrity sha512-y0V3F7RCWXy8kBOvKvKSRUNKRobLB6vL/UNchy/6+IUNIqu+UyrY3Z7jvj1ZA/AkYc/0WkCUtppCo+bPhMU8Aw== - dependencies: - ini "^2.0.0" - mkdirp-infer-owner "^2.0.0" - nopt "^5.0.0" - semver "^7.3.4" - walk-up-path "^1.0.0" - -"@npmcli/disparity-colors@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@npmcli/disparity-colors/-/disparity-colors-1.0.1.tgz" - integrity sha512-kQ1aCTTU45mPXN+pdAaRxlxr3OunkyztjbbxDY/aIcPS5CnCUrx+1+NvA6pTcYR7wmLZe37+Mi5v3nfbwPxq3A== - dependencies: - ansi-styles "^4.3.0" - -"@npmcli/git@^2.0.1", "@npmcli/git@^2.0.7", "@npmcli/git@^2.1.0": +"@npmcli/git@^2.0.1", "@npmcli/git@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== @@ -2855,7 +2799,7 @@ semver "^7.3.5" which "^2.0.2" -"@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7": +"@npmcli/installed-package-contents@^1.0.6": version "1.0.7" resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -2863,26 +2807,7 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -"@npmcli/map-workspaces@^1.0.2": - version "1.0.3" - resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-1.0.3.tgz" - integrity sha512-SdlRlOoQw4WKD4vtb/n5gUkobEABYBEOo8fRE4L8CtBkyWDSvIrReTfKvQ/Jc/LQqDaaZ5iv1iMSQzKCUr1n1A== - dependencies: - "@npmcli/name-from-folder" "^1.0.1" - glob "^7.1.6" - minimatch "^3.0.4" - read-package-json-fast "^2.0.1" - -"@npmcli/metavuln-calculator@^1.1.0": - version "1.1.1" - resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.1.1.tgz" - integrity sha512-9xe+ZZ1iGVaUovBVFI9h3qW+UuECUzhvZPxK9RaEA2mjU26o5D0JloGYWwLYvQELJNmBdQB6rrpuN8jni6LwzQ== - dependencies: - cacache "^15.0.5" - pacote "^11.1.11" - semver "^7.3.2" - -"@npmcli/move-file@^1.0.1", "@npmcli/move-file@^1.1.0": +"@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== @@ -2890,23 +2815,11 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/name-from-folder@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz" - integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== - -"@npmcli/node-gyp@^1.0.1", "@npmcli/node-gyp@^1.0.2": +"@npmcli/node-gyp@^1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz" integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== -"@npmcli/package-json@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz" - integrity sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg== - dependencies: - json-parse-even-better-errors "^2.3.1" - "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz" @@ -2914,7 +2827,7 @@ dependencies: infer-owner "^1.0.4" -"@npmcli/run-script@^1.8.2", "@npmcli/run-script@^1.8.3", "@npmcli/run-script@^1.8.4", "@npmcli/run-script@^1.8.5": +"@npmcli/run-script@^1.8.2": version "1.8.5" resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz" integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== @@ -3668,6 +3581,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz" integrity sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A== +"@types/node@15.14.7": + version "15.14.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.7.tgz#29fea9a5b14e2b75c19028e1c7a32edd1e89fe92" + integrity sha512-FA45p37/mLhpebgbPWWCKfOisTjxGK9lwcHlJ6XVLfu3NgfcazOJHdYUZCWPMK8QX4LhNZdmfo6iMz9FqpUbaw== + "@types/node@>=12.12.47": version "16.4.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272" @@ -3688,13 +3606,6 @@ resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== -"@types/npm@2.0.32": - version "2.0.32" - resolved "https://registry.npmjs.org/@types/npm/-/npm-2.0.32.tgz" - integrity sha512-9Lg4woNVzJCtac0lET91H65lbO+8YXfk0nmlmoPGhHXMdaVEDloH6zOPIYMy2n39z/aCXXQR0nax66EDekAyIQ== - dependencies: - "@types/node" "*" - "@types/oauth@*": version "0.9.1" resolved "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz" @@ -4236,7 +4147,7 @@ abab@^2.0.5: resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -abbrev@1, abbrev@~1.1.1: +abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -4486,7 +4397,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -4498,11 +4409,6 @@ ansicolors@~0.3.2: resolved "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= -ansistyles@~0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz" - integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk= - any-observable@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz" @@ -4564,7 +4470,7 @@ aproba@^2.0.0: resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -archy@^1.0.0, archy@~1.0.0: +archy@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= @@ -5104,24 +5010,12 @@ bignumber.js@^9.0.0: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== -bin-links@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/bin-links/-/bin-links-2.2.1.tgz" - integrity sha512-wFzVTqavpgCCYAh8SVBdnZdiQMxTkGR+T3b14CNpBXIBe2neJWaMGAZ55XWWHELJJ89dscuq0VCBqcVaIOgCMg== - dependencies: - cmd-shim "^4.0.1" - mkdirp "^1.0.3" - npm-normalize-package-bin "^1.0.0" - read-cmd-shim "^2.0.0" - rimraf "^3.0.0" - write-file-atomic "^3.0.3" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== -binary-extensions@^2.0.0, binary-extensions@^2.2.0: +binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== @@ -5575,7 +5469,7 @@ byline@^5.0.0: resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= -byte-size@^7.0.0, byte-size@^7.0.1: +byte-size@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== @@ -5590,7 +5484,7 @@ bytes@3.1.0: resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@15.2.0, cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: +cacache@15.2.0, cacache@^15.0.5, cacache@^15.2.0: version "15.2.0" resolved "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz" integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== @@ -5960,12 +5854,10 @@ ci-info@^2.0.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cidr-regex@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/cidr-regex/-/cidr-regex-3.1.1.tgz" - integrity sha512-RBqYd32aDwbCMFJRL6wHOlDNYJsPNTt8vC82ErHF5vKt8QQzxm1FrkW8s/R5pVrXMf17sba09Uoy91PKiddAsw== - dependencies: - ip-regex "^4.1.0" +ci-info@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== cids@^0.7.1: version "0.7.5" @@ -6039,14 +5931,6 @@ cli-boxes@^2.2.0: resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-columns@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz" - integrity sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4= - dependencies: - string-width "^2.0.0" - strip-ansi "^3.0.1" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" @@ -6071,16 +5955,6 @@ cli-spinners@^2.5.0: resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== -cli-table3@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz" - integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== - dependencies: - object-assign "^4.1.0" - string-width "^4.2.0" - optionalDependencies: - colors "^1.1.2" - cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" @@ -6151,7 +6025,7 @@ clone@^1.0.2: resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -cmd-shim@^4.0.1, cmd-shim@^4.1.0: +cmd-shim@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz" integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== @@ -6251,12 +6125,12 @@ colors@1.0.x: resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.1.2, colors@^1.4.0: +colors@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -columnify@^1.5.4, columnify@~1.5.4: +columnify@^1.5.4: version "1.5.4" resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= @@ -6336,11 +6210,6 @@ comment-json@^4.1.0: has-own-prop "^2.0.0" repeat-string "^1.6.1" -common-ancestor-path@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz" - integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" @@ -7693,11 +7562,6 @@ diff@^4.0.1, diff@^4.0.2: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" @@ -10505,7 +10369,7 @@ hosted-git-info@^2.1.4: resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -hosted-git-info@^4.0.0, hosted-git-info@^4.0.1, hosted-git-info@^4.0.2: +hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz" integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== @@ -10927,7 +10791,7 @@ ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -init-package-json@^2.0.2, init-package-json@^2.0.3: +init-package-json@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.3.tgz" integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== @@ -11103,7 +10967,7 @@ ip-regex@^2.1.0: resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip-regex@^4.0.0, ip-regex@^4.1.0: +ip-regex@^4.0.0: version "4.3.0" resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== @@ -11375,13 +11239,6 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-cidr@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/is-cidr/-/is-cidr-4.0.2.tgz" - integrity sha512-z4a1ENUajDbEl/Q6/pVBpTR1nBjjEE1X7qb7bmWYanNnPoKAvUCPFKeXV6Fe4mgTkWKBqiHIcwsI3SndiO5FeA== - dependencies: - cidr-regex "^3.1.1" - is-circular@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-circular/-/is-circular-1.0.2.tgz" @@ -12251,7 +12108,7 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: +json-parse-even-better-errors@^2.3.0: 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== @@ -12297,11 +12154,6 @@ json-stable-stringify@1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-nice@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz" - integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== - json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" @@ -12421,16 +12273,6 @@ jszip@^3.1.3: readable-stream "~2.3.6" set-immediate-shim "~1.0.1" -just-diff-apply@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-3.0.0.tgz" - integrity sha512-K2MLc+ZC2DVxX4V61bIKPeMUUfj1YYZ3h0myhchDXOW1cKoPZMnjIoNCqv9bF2n5Oob1PFxuR2gVJxkxz4e58w== - -just-diff@^3.0.1: - version "3.1.1" - resolved "https://registry.npmjs.org/just-diff/-/just-diff-3.1.1.tgz" - integrity sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ== - jwa@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" @@ -12881,11 +12723,6 @@ levelup@^3.0.0: level-iterator-stream "~3.0.0" xtend "~4.0.0" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" @@ -12902,7 +12739,7 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libnpmaccess@^4.0.1, libnpmaccess@^4.0.2: +libnpmaccess@^4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-4.0.3.tgz" integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== @@ -12912,70 +12749,7 @@ libnpmaccess@^4.0.1, libnpmaccess@^4.0.2: npm-package-arg "^8.1.2" npm-registry-fetch "^11.0.0" -libnpmdiff@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-2.0.4.tgz" - integrity sha512-q3zWePOJLHwsLEUjZw3Kyu/MJMYfl4tWCg78Vl6QGSfm4aXBUSVzMzjJ6jGiyarsT4d+1NH4B1gxfs62/+y9iQ== - dependencies: - "@npmcli/disparity-colors" "^1.0.1" - "@npmcli/installed-package-contents" "^1.0.7" - binary-extensions "^2.2.0" - diff "^5.0.0" - minimatch "^3.0.4" - npm-package-arg "^8.1.1" - pacote "^11.3.0" - tar "^6.1.0" - -libnpmexec@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/libnpmexec/-/libnpmexec-2.0.0.tgz" - integrity sha512-9zHswx//Lp2ao+huWF2aL+6v4haMncyxNusk6Us2fbLNnPh3+rgSkv38LJ2v8gmKS2kAnkUmQf8pHjcZ+7Z3NA== - dependencies: - "@npmcli/arborist" "^2.3.0" - "@npmcli/ci-detect" "^1.3.0" - "@npmcli/run-script" "^1.8.4" - chalk "^4.1.0" - mkdirp-infer-owner "^2.0.0" - npm-package-arg "^8.1.2" - pacote "^11.3.1" - proc-log "^1.0.0" - read "^1.0.7" - read-package-json-fast "^2.0.2" - walk-up-path "^1.0.0" - -libnpmfund@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/libnpmfund/-/libnpmfund-1.1.0.tgz" - integrity sha512-Kfmh3pLS5/RGKG5WXEig8mjahPVOxkik6lsbH4iX0si1xxNi6eeUh/+nF1MD+2cgalsQif3O5qyr6mNz2ryJrQ== - dependencies: - "@npmcli/arborist" "^2.5.0" - -libnpmhook@^6.0.2: - version "6.0.3" - resolved "https://registry.npmjs.org/libnpmhook/-/libnpmhook-6.0.3.tgz" - integrity sha512-3fmkZJibIybzmAvxJ65PeV3NzRc0m4xmYt6scui5msocThbEp4sKFT80FhgrCERYDjlUuFahU6zFNbJDHbQ++g== - dependencies: - aproba "^2.0.0" - npm-registry-fetch "^11.0.0" - -libnpmorg@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/libnpmorg/-/libnpmorg-2.0.3.tgz" - integrity sha512-JSGl3HFeiRFUZOUlGdiNcUZOsUqkSYrg6KMzvPZ1WVZ478i47OnKSS0vkPmX45Pai5mTKuwIqBMcGWG7O8HfdA== - dependencies: - aproba "^2.0.0" - npm-registry-fetch "^11.0.0" - -libnpmpack@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/libnpmpack/-/libnpmpack-2.0.1.tgz" - integrity sha512-He4/jxOwlaQ7YG7sIC1+yNeXeUDQt8RLBvpI68R3RzPMZPa4/VpxhlDo8GtBOBDYoU8eq6v1wKL38sq58u4ibQ== - dependencies: - "@npmcli/run-script" "^1.8.3" - npm-package-arg "^8.1.0" - pacote "^11.2.6" - -libnpmpublish@^4.0.0, libnpmpublish@^4.0.1: +libnpmpublish@^4.0.0: version "4.0.2" resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-4.0.2.tgz" integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== @@ -12986,32 +12760,6 @@ libnpmpublish@^4.0.0, libnpmpublish@^4.0.1: semver "^7.1.3" ssri "^8.0.1" -libnpmsearch@^3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-3.1.2.tgz" - integrity sha512-BaQHBjMNnsPYk3Bl6AiOeVuFgp72jviShNBw5aHaHNKWqZxNi38iVNoXbo6bG/Ccc/m1To8s0GtMdtn6xZ1HAw== - dependencies: - npm-registry-fetch "^11.0.0" - -libnpmteam@^2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/libnpmteam/-/libnpmteam-2.0.4.tgz" - integrity sha512-FPrVJWv820FZFXaflAEVTLRWZrerCvfe7ZHSMzJ/62EBlho2KFlYKjyNEsPW3JiV7TLSXi3vo8u0gMwIkXSMTw== - dependencies: - aproba "^2.0.0" - npm-registry-fetch "^11.0.0" - -libnpmversion@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/libnpmversion/-/libnpmversion-1.2.1.tgz" - integrity sha512-AA7x5CFgBFN+L4/JWobnY5t4OAHjQuPbAwUYJ7/NtHuyLut5meb+ne/aj0n7PWNiTGCJcRw/W6Zd2LoLT7EZuQ== - dependencies: - "@npmcli/git" "^2.0.7" - "@npmcli/run-script" "^1.8.4" - json-parse-even-better-errors "^2.3.1" - semver "^7.3.5" - stringify-package "^1.0.1" - libtap@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/libtap/-/libtap-1.1.1.tgz" @@ -13091,6 +12839,15 @@ listr2@1.3.8: through "^2.3.8" uuid "^7.0.2" +lmify@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/lmify/-/lmify-0.3.0.tgz#42a14f6258b97f51cd31b35f7f1a663212fe2da0" + integrity sha512-Yc4LT2QdkZJXTgwrTEV6T3RRpNIVIh0lMEuITUfj8e7SeWO8hq6Llyq7n588LUuX+rkoBnl2AyyrdYYJ4uxODA== + dependencies: + execa "^4.0.0" + fs-extra "^9.0.0" + std-env "^2.2.1" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" @@ -13488,7 +13245,7 @@ make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" -make-fetch-happen@^9.0.1, make-fetch-happen@^9.0.3: +make-fetch-happen@^9.0.1: version "9.0.4" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz" integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== @@ -14460,7 +14217,7 @@ node-gyp@^5.0.2: tar "^4.4.12" which "^1.3.1" -node-gyp@^7.1.0, node-gyp@^7.1.2: +node-gyp@^7.1.0: version "7.1.2" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz" integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== @@ -14645,13 +14402,6 @@ normalize-url@^6.0.1, normalize-url@^6.1.0: resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -npm-audit-report@^2.1.5: - version "2.1.5" - resolved "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-2.1.5.tgz" - integrity sha512-YB8qOoEmBhUH1UJgh1xFAv7Jg1d+xoNhsDYiFQlEFThEBui0W1vIz2ZK6FVg4WZjwEdl7uBQlm1jy3MUfyHeEw== - dependencies: - chalk "^4.0.0" - npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" @@ -14693,7 +14443,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@8.1.5, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.1, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: +npm-package-arg@8.1.5, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: version "8.1.5" resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz" integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== @@ -14712,7 +14462,7 @@ npm-packlist@^2.1.4: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -npm-pick-manifest@6.1.1, npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: +npm-pick-manifest@6.1.1, npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: version "6.1.1" resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz" integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== @@ -14722,13 +14472,6 @@ npm-pick-manifest@6.1.1, npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm npm-package-arg "^8.1.2" semver "^7.3.4" -npm-profile@^5.0.3: - version "5.0.4" - resolved "https://registry.npmjs.org/npm-profile/-/npm-profile-5.0.4.tgz" - integrity sha512-OKtU7yoAEBOnc8zJ+/uo5E4ugPp09sopo+6y1njPp+W99P8DvQon3BJYmpvyK2Bf1+3YV5LN1bvgXRoZ1LUJBA== - dependencies: - npm-registry-fetch "^11.0.0" - npm-registry-fetch@^11.0.0: version "11.0.0" resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz" @@ -14784,11 +14527,6 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npm-user-validate@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-1.0.1.tgz" - integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== - npm-watch@0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/npm-watch/-/npm-watch-0.7.0.tgz" @@ -14797,81 +14535,7 @@ npm-watch@0.7.0: nodemon "^2.0.3" through2 "^2.0.0" -npm@7.19.1: - version "7.19.1" - resolved "https://registry.npmjs.org/npm/-/npm-7.19.1.tgz" - integrity sha512-aN3hZzGkPzKOyhjXtOhnQTGumorFhgpOU6xfuQsF1nJKh4DhsgfOMG4s/SNx56r4xHPvM5m/sk914wzDgKba3A== - dependencies: - "@npmcli/arborist" "^2.6.4" - "@npmcli/ci-detect" "^1.2.0" - "@npmcli/config" "^2.2.0" - "@npmcli/package-json" "^1.0.1" - "@npmcli/run-script" "^1.8.5" - abbrev "~1.1.1" - ansicolors "~0.3.2" - ansistyles "~0.1.3" - archy "~1.0.0" - byte-size "^7.0.1" - cacache "^15.2.0" - chalk "^4.1.0" - chownr "^2.0.0" - cli-columns "^3.1.2" - cli-table3 "^0.6.0" - columnify "~1.5.4" - glob "^7.1.7" - graceful-fs "^4.2.6" - hosted-git-info "^4.0.2" - ini "^2.0.0" - init-package-json "^2.0.3" - is-cidr "^4.0.2" - json-parse-even-better-errors "^2.3.1" - leven "^3.1.0" - libnpmaccess "^4.0.2" - libnpmdiff "^2.0.4" - libnpmexec "^2.0.0" - libnpmfund "^1.1.0" - libnpmhook "^6.0.2" - libnpmorg "^2.0.2" - libnpmpack "^2.0.1" - libnpmpublish "^4.0.1" - libnpmsearch "^3.1.1" - libnpmteam "^2.0.3" - libnpmversion "^1.2.1" - make-fetch-happen "^9.0.3" - minipass "^3.1.3" - minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - ms "^2.1.2" - node-gyp "^7.1.2" - nopt "^5.0.0" - npm-audit-report "^2.1.5" - npm-package-arg "^8.1.5" - npm-pick-manifest "^6.1.1" - npm-profile "^5.0.3" - npm-registry-fetch "^11.0.0" - npm-user-validate "^1.0.1" - npmlog "~4.1.2" - opener "^1.5.2" - pacote "^11.3.3" - parse-conflict-json "^1.1.1" - qrcode-terminal "^0.12.0" - read "~1.0.7" - read-package-json "^3.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" - semver "^7.3.5" - ssri "^8.0.1" - tar "^6.1.0" - text-table "~0.2.0" - tiny-relative-date "^1.3.0" - treeverse "^1.0.4" - validate-npm-package-name "~3.0.0" - which "^2.0.2" - write-file-atomic "^3.0.3" - -npmlog@^4.0.1, npmlog@^4.1.2, npmlog@~4.1.2: +npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -15486,7 +15150,7 @@ pacote@11.3.4: ssri "^8.0.1" tar "^6.1.0" -pacote@^11.1.11, pacote@^11.2.6, pacote@^11.3.0, pacote@^11.3.1, pacote@^11.3.3: +pacote@^11.2.6: version "11.3.5" resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz" integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== @@ -15557,15 +15221,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-conflict-json@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-1.1.1.tgz" - integrity sha512-4gySviBiW5TRl7XHvp1agcS7SOe0KZOjC//71dzZVWJrY9hCrgtvl5v3SyIxCZ4fZF47TxD9nfzmxcx76xmbUw== - dependencies: - json-parse-even-better-errors "^2.3.0" - just-diff "^3.0.1" - just-diff-apply "^3.0.0" - parse-duration@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/parse-duration/-/parse-duration-1.0.0.tgz" @@ -16494,11 +16149,6 @@ pretty-bytes@^5.3.0: resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -proc-log@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz" - integrity sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -16535,16 +16185,6 @@ prom-client@13.1.0: dependencies: tdigest "^0.1.1" -promise-all-reject-late@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz" - integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== - -promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== - promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" @@ -16736,11 +16376,6 @@ qjobs@^1.2.0: resolved "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz" integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== -qrcode-terminal@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz" - integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== - qs@6.7.0: version "6.7.0" resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" @@ -16908,7 +16543,7 @@ read-cmd-shim@^2.0.0: resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz" integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== -read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2: +read-package-json-fast@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz" integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== @@ -16981,7 +16616,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -read@1, read@^1.0.7, read@~1.0.1, read@~1.0.7: +read@1, read@~1.0.1: version "1.0.7" resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= @@ -17030,7 +16665,7 @@ readable-stream@~1.0.15: isarray "0.0.1" string_decoder "~0.10.x" -readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: +readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -18595,6 +18230,13 @@ statsd-parser@~0.0.4: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +std-env@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.3.0.tgz#66d4a4a4d5224242ed8e43f5d65cfa9095216eee" + integrity sha512-4qT5B45+Kjef2Z6pE0BkskzsH0GO7GrND0wGlTM1ioUe3v0dGYx9ZJH0Aro/YyA8fqQ5EyIKDRjZojJYMFTflw== + dependencies: + ci-info "^3.0.0" + stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz" @@ -18784,11 +18426,6 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -stringify-package@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz" - integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" @@ -19267,7 +18904,7 @@ temp@0.9.1: temp@0.9.4: version "0.9.4" - resolved "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== dependencies: mkdirp "^0.5.1" @@ -19341,7 +18978,7 @@ text-extensions@^1.0.0: resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-table@0.2.0, text-table@^0.2.0, text-table@~0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -19396,11 +19033,6 @@ timsort@^0.3.0: resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tiny-relative-date@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz" - integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== - tmp@0.0.28: version "0.0.28" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz" @@ -19542,11 +19174,6 @@ tree-kill@1.2.2, tree-kill@^1.2.2: resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -treeverse@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz" - integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== - treport@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/treport/-/treport-2.0.2.tgz" @@ -20261,7 +19888,7 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0: +validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= @@ -20317,11 +19944,6 @@ vscode-uri@^3.0.2: resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz" integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA== -walk-up-path@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz" - integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== - watchpack@^2.0.0, watchpack@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz"