diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8ac209c57 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root=true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/add-on/src/lib/ipfs-client/index.js b/add-on/src/lib/ipfs-client/index.js index 028e4e746..81a4212ba 100644 --- a/add-on/src/lib/ipfs-client/index.js +++ b/add-on/src/lib/ipfs-client/index.js @@ -10,6 +10,7 @@ const external = require('./external') const embedded = require('./embedded') const brave = require('./brave') const { precache } = require('../precache') +const { prepareReloadExtensions, WebUiReloader, LocalGatewayReloader, InternalTabReloader } = require('./reloaders') // ensure single client at all times, and no overlap between init and destroy let client @@ -61,34 +62,32 @@ async function destroyIpfsClient (browser) { } } -function _isWebuiTab (url) { - const bundled = !url.startsWith('http') && url.includes('/webui/index.html#/') - const ipns = url.includes('/webui.ipfs.io/#/') - return bundled || ipns -} - -function _isInternalTab (url, extensionOrigin) { - return url.startsWith(extensionOrigin) -} - -async function _reloadIpfsClientDependents (browser, instance, opts) { +/** + * Reloads pages dependant on ipfs to be online + * + * @typedef {embedded|brave|external} Browser + * @param {Browser} browser + * @param {import('ipfs-http-client').default} instance + * @param {Object} opts + * @param {Array.[InternalTabReloader|LocalGatewayReloader|WebUiReloader]=} reloadExtensions + * @returns {void} + */ +async function _reloadIpfsClientDependents ( + browser, instance, opts, reloadExtensions = [WebUiReloader, LocalGatewayReloader, InternalTabReloader]) { // online || offline if (browser.tabs && browser.tabs.query) { const tabs = await browser.tabs.query({}) if (tabs) { - const extensionOrigin = browser.runtime.getURL('/') - tabs.forEach((tab) => { - // detect bundled webui in any of open tabs - if (_isWebuiTab(tab.url)) { - log(`reloading webui at ${tab.url}`) - browser.tabs.reload(tab.id) - } else if (_isInternalTab(tab.url, extensionOrigin)) { - log(`reloading internal extension page at ${tab.url}`) - browser.tabs.reload(tab.id) - } - }) + try { + const reloadExtensionInstances = await prepareReloadExtensions(reloadExtensions, browser, log) + // the reload process is async, fire and forget. + reloadExtensionInstances.forEach(ext => ext.reload(tabs)) + } catch (e) { + log('Failed to trigger reloaders') + } } } + // online only if (client && instance && opts) { // add important data to local ipfs repo for instant load @@ -96,5 +95,21 @@ async function _reloadIpfsClientDependents (browser, instance, opts) { } } -exports.initIpfsClient = initIpfsClient -exports.destroyIpfsClient = destroyIpfsClient +/** + * Reloads local gateway pages dependant on ipfs to be online + * + * @typedef {embedded|brave|external} Browser + * @param {Browser} browser + * @param {import('ipfs-http-client').default} instance + * @param {Object} opts + * @returns {void} + */ +function reloadIpfsClientOfflinePages (browser, instance, opts) { + _reloadIpfsClientDependents(browser, instance, opts, [LocalGatewayReloader]) +} + +module.exports = { + initIpfsClient, + destroyIpfsClient, + reloadIpfsClientOfflinePages +} diff --git a/add-on/src/lib/ipfs-client/reloaders/index.js b/add-on/src/lib/ipfs-client/reloaders/index.js new file mode 100644 index 000000000..74a453941 --- /dev/null +++ b/add-on/src/lib/ipfs-client/reloaders/index.js @@ -0,0 +1,33 @@ +const { InternalTabReloader } = require('./internalTabReloader') +const { LocalGatewayReloader } = require('./localGatewayReloader') +const { WebUiReloader } = require('./webUiReloader') + +/** + * Prepares extension by creating an instance and awaiting for init. + * + * @param {Array.[InternalTabReloader|LocalGatewayReloader|WebUiReloader]} extensions + * @param {Browser} browserInstance + * @param {Logger} loggerInstance + * @returns {Promise} + */ +function prepareReloadExtensions (extensions, browserInstance, loggerInstance) { + const reloadExtensions = Array.isArray(extensions) ? extensions : [extensions] + return Promise.all(reloadExtensions + .map(async Ext => { + try { + const ext = new Ext(browserInstance, loggerInstance) + await ext.init() + return ext + } catch (e) { + loggerInstance(`Extension Instance Failed to Initialize with error: ${e}. Extension: ${Ext}`) + } + }) + ) +} + +module.exports = { + InternalTabReloader, + LocalGatewayReloader, + WebUiReloader, + prepareReloadExtensions +} diff --git a/add-on/src/lib/ipfs-client/reloaders/internalTabReloader.js b/add-on/src/lib/ipfs-client/reloaders/internalTabReloader.js new file mode 100644 index 000000000..cc5fc7a23 --- /dev/null +++ b/add-on/src/lib/ipfs-client/reloaders/internalTabReloader.js @@ -0,0 +1,37 @@ +const { ReloaderBase } = require('./reloaderBase') + +class InternalTabReloader extends ReloaderBase { + /** + * Setting up the extension origin. + */ + init () { + this.extensionOrigin = this._browserInstance.runtime.getURL('/') + this._log('InternalTabReloader Ready for use.') + } + + /** + * Performs url validation for the tab. If tab is a WebUI tab. + * + * @param {Object} tab + * @param {string} tab.url + * @returns {boolean} + */ + validation ({ url }) { + return url.startsWith(this.extensionOrigin) + } + + /** + * Returns message when reloading the tab. + * + * @param {Object} tab + * @param {string} tab.url + * @returns {string} message. + */ + message ({ url }) { + return `reloading internal extension page at ${url}` + } +} + +module.exports = { + InternalTabReloader +} diff --git a/add-on/src/lib/ipfs-client/reloaders/localGatewayReloader.js b/add-on/src/lib/ipfs-client/reloaders/localGatewayReloader.js new file mode 100644 index 000000000..e697dec4c --- /dev/null +++ b/add-on/src/lib/ipfs-client/reloaders/localGatewayReloader.js @@ -0,0 +1,42 @@ +const { ReloaderBase } = require('./reloaderBase') +const isIPFS = require('is-ipfs') + +class LocalGatewayReloader extends ReloaderBase { + /** + * Performs url validation for the tab. If tab is loaded via local gateway. + * + * @param {Object} tab + * @param {string} tab.url + * @param {string} tab.url + * @returns {boolean} + */ + validation ({ url, title }) { + /** + * Check if the url is the local gateway url and if the title is contained within the url then it was not loaded. + * - This assumes that the title of most pages on the web will be set and hence when not reachable, the browser + * will set title to the url/host (both chrome and brave) and 'problem loading page' for firefox. + * - There is probability that this might be true in case the tag is omitted, but worst case it only reloads + * those pages. + * - The benefit we get from this approach is the static nature of just observing the tabs in their current state + * which reduces the overhead of injecting content scripts to track urls that were loaded after the connection + * was offline, it may also need extra permissions to inject code on error pages. + */ + return (isIPFS.url(url) || isIPFS.subdomain(url)) && + (url.includes(title) || title.toLowerCase() === 'problem loading page') + } + + /** + * Returns message when reloading the tab. + * + * @param {Object} tab + * @param {string} tab.url + * @returns {string} message. + */ + message ({ url }) { + return `reloading local gateway at ${url}` + } +} + +module.exports = { + LocalGatewayReloader +} diff --git a/add-on/src/lib/ipfs-client/reloaders/reloaderBase.js b/add-on/src/lib/ipfs-client/reloaders/reloaderBase.js new file mode 100644 index 000000000..d4746e6ed --- /dev/null +++ b/add-on/src/lib/ipfs-client/reloaders/reloaderBase.js @@ -0,0 +1,53 @@ +class ReloaderBase { + /** + * Constructor for reloader base class. + * + * @param {Browser} browser + * @param {Logger} log + */ + constructor (browser, log) { + if (!browser || !log) { + throw new Error('Instances of browser and logger are needed!') + } + this._browserInstance = browser + this._log = log + }; + + /** + * Initializes the instance. + */ + init () { + this._log('Initialized without additional config.') + } + + /** + * To be implemented in child class. + */ + validation () { + throw new Error('Validation: Method Not Implemented') + } + + /** + * To be implemented in child class. + */ + message () { + throw new Error('Message: Method Not Implemented') + } + + /** + * Handles reload for all tabs. + * params {Array<tabs>} + */ + reload (tabs) { + tabs + .filter(tab => this.validation(tab)) + .forEach(tab => { + this._log(this.message(tab)) + this._browserInstance.tabs.reload(tab.id) + }) + } +} + +module.exports = { + ReloaderBase +} diff --git a/add-on/src/lib/ipfs-client/reloaders/webUiReloader.js b/add-on/src/lib/ipfs-client/reloaders/webUiReloader.js new file mode 100644 index 000000000..05623f490 --- /dev/null +++ b/add-on/src/lib/ipfs-client/reloaders/webUiReloader.js @@ -0,0 +1,30 @@ +const { ReloaderBase } = require('./reloaderBase') + +class WebUiReloader extends ReloaderBase { + /** + * Performs url validation for the tab. If tab is a WebUI tab. + * + * @param {Object} tab + * @returns {boolean} + */ + validation ({ url }) { + const bundled = !url.startsWith('http') && url.includes('/webui/index.html#/') + const ipns = url.includes('/webui.ipfs.io/#/') + return bundled || ipns + } + + /** + * Returns message when reloading the tab. + * + * @param {Object} tab + * @param {string} tab.url + * @returns {string} message. + */ + message ({ url }) { + return `reloading webui at ${url}` + } +} + +module.exports = { + WebUiReloader +} diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index cf7a9db9e..03399e797 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -15,7 +15,7 @@ const { initState, offlinePeerCount } = require('./state') const { createIpfsPathValidator, sameGateway, safeHostname } = require('./ipfs-path') const createDnslinkResolver = require('./dnslink') const { createRequestModifier } = require('./ipfs-request') -const { initIpfsClient, destroyIpfsClient } = require('./ipfs-client') +const { initIpfsClient, destroyIpfsClient, reloadIpfsClientOfflinePages } = require('./ipfs-client') const { braveNodeType, useBraveEndpoint, releaseBraveEndpoint } = require('./ipfs-client/brave') const { createIpfsImportHandler, formatImportDirectory, browserActionFilesCpImportCurrentTab } = require('./ipfs-import') const createNotifier = require('./notifier') @@ -595,6 +595,7 @@ module.exports = async function init () { if (oldPeerCount === offlinePeerCount && newPeerCount > offlinePeerCount && !state.redirect) { await browser.storage.local.set({ useCustomGateway: true }) await notify('notify_apiOnlineTitle', 'notify_apiOnlineAutomaticModeMsg') + reloadIpfsClientOfflinePages(browser, ipfs, state) } else if (newPeerCount === offlinePeerCount && state.redirect) { await browser.storage.local.set({ useCustomGateway: false }) await notify('notify_apiOfflineTitle', 'notify_apiOfflineAutomaticModeMsg') diff --git a/package.json b/package.json index de3f106f1..b31c8d165 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "webextension-polyfill": "0.7.0" }, "engines": { - "node": ">=14.15.0", + "node": ">=14.15.0 <17", "npm": ">=6.14.0" } } diff --git a/test/functional/lib/ipfs-client/reloaders/reloaders.test.js b/test/functional/lib/ipfs-client/reloaders/reloaders.test.js new file mode 100644 index 000000000..462b14ba9 --- /dev/null +++ b/test/functional/lib/ipfs-client/reloaders/reloaders.test.js @@ -0,0 +1,121 @@ +'use strict' +const { describe, it, before, beforeEach } = require('mocha') +const browser = require('sinon-chrome') +const sinon = require('sinon') + +const CUSTOM_GATEWAY_URL = { + customGatewayUrl: 'http://127.0.0.1:8080' +} + +const logger = sinon.spy() + +// Units to be tested. +const { + prepareReloadExtensions, + InternalTabReloader, + LocalGatewayReloader, + WebUiReloader +} = require('../../../../../add-on/src/lib/ipfs-client/reloaders') + +describe('Reloader Helper Method: prepareReloadExtensions', function () { + it('Prepares the reloader extensions', async function () { + browser.storage.local.get.resolves(CUSTOM_GATEWAY_URL) + browser.runtime.getURL.returns('chrome-extension://<some_id>') + await prepareReloadExtensions([WebUiReloader, InternalTabReloader, LocalGatewayReloader], browser, logger) + sinon.assert.callCount(logger, 3) + }) +}) + +describe('Reloaders', function () { + before(() => { + global.browser = browser + global.chrome = {} + }) + + beforeEach(function () { + browser.flush() + logger.resetHistory() + }) + + describe('WebUiReloader', function () { + let webUiReloader + beforeEach(async function () { + webUiReloader = new WebUiReloader(browser, logger) + await webUiReloader.init() + }) + + it('should initialize', function () { + sinon.assert.calledWith(logger, 'Initialized without additional config.') + }) + + it('should handle webUi reloading', function () { + const tabs = [{ + url: 'http://some-url.com', + id: 1 + }, { + url: 'ipfs://some-ipfs-server.tld/webui/index.html#/', + id: 2 + }] + + webUiReloader.reload(tabs) + sinon.assert.calledWith(browser.tabs.reload, 2) + sinon.assert.calledWith(logger, 'reloading webui at ipfs://some-ipfs-server.tld/webui/index.html#/') + }) + }) + + describe('InternalTabReloader', function () { + let internalTabReloader + beforeEach(async function () { + browser.runtime.getURL.returns('chrome-extension://<some_id>') + internalTabReloader = new InternalTabReloader(browser, logger) + await internalTabReloader.init() + }) + + it('should initialize', function () { + sinon.assert.calledWith(logger, 'InternalTabReloader Ready for use.') + }) + + it('should handle internal tab reloading', function () { + const tabs = [{ + url: 'http://127.0.0.1:8080/ipfs/cid/wiki1', + id: 1 + }, { + url: 'chrome-extension://<some_id>/index.html', + id: 2 + }] + + internalTabReloader.reload(tabs) + sinon.assert.calledWith(browser.tabs.reload, 2) + sinon.assert.calledWith(logger, 'reloading internal extension page at chrome-extension://<some_id>/index.html') + }) + }) + + describe('LocalGatewayReloader', function () { + let localGatewayReloader + beforeEach(async function () { + browser.storage.local.get.resolves(CUSTOM_GATEWAY_URL) + localGatewayReloader = new LocalGatewayReloader(browser, logger) + await localGatewayReloader.init() + }) + + it('should initialize', function () { + sinon.assert.calledWith(logger, 'Initialized without additional config.') + }) + + it('should handle local gateway tab reloading', function () { + const tabs = [{ + title: 'Main Page', + url: 'http://127.0.0.1:8080/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki1', + id: 1 + }, { + title: '127.0.0.1:8080', + url: 'http://127.0.0.1:8080/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki2', + id: 2 + }] + + localGatewayReloader.reload(tabs) + sinon.assert.calledWith(browser.tabs.reload, 2) + sinon.assert.calledWith(logger, 'reloading local gateway at http://127.0.0.1:8080/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki2') + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index e2e395059..2df374747 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2638,14 +2638,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== -binary@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -2880,11 +2872,6 @@ buffer@^5.2.1, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.0.2" ieee754 "^1.1.4" -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= - bufio@~1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.0.7.tgz#b7f63a1369a0829ed64cc14edf0573b3e382a33e" @@ -3001,11 +2988,6 @@ camelcase@^2.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -3017,9 +2999,9 @@ camelcase@^6.0.0: integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ== caniuse-lite@^1.0.30001135, caniuse-lite@^1.0.30001181: - version "1.0.30001238" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001238.tgz" - integrity sha512-bZGam2MxEt7YNsa2VwshqWQMwrYs5tR5WZQRYSuFxsBQunWjBuXhN4cS9nV5FFb1Z9y+DoQcQ0COyQbv6A+CKw== + version "1.0.30001393" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz" + integrity sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA== capital-case@^1.0.4: version "1.0.4" @@ -3082,13 +3064,6 @@ chai@^4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= - dependencies: - traverse ">=0.3.0 <0.4" - chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -3333,15 +3308,6 @@ cli-truncate@^0.2.1: slice-ansi "0.0.4" string-width "^1.0.1" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -3640,15 +3606,6 @@ cross-env@7.0.3: dependencies: cross-spawn "^7.0.1" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -3841,7 +3798,7 @@ decamelize@5.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.0.tgz#88358157b010ef133febfd27c18994bd80c6215b" integrity sha512-U75DcT5hrio3KNtvdULAWnLiAPbFUC4191ldxMmj4FA/mRuBnmDwU0boNfPyFRhnan+Jm+haLeSn3P0afcBn4w== -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -4862,19 +4819,6 @@ evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.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" - execa@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" @@ -5149,15 +5093,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -firefox-addons-add-update-version@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/firefox-addons-add-update-version/-/firefox-addons-add-update-version-1.0.1.tgz#52c87c97f1e0e356670601f5e656813fdf65e36b" - integrity sha512-LL0zMpF4TqEuPVQCM2VkfhBLzfMAyPEugQwulf5VHwxKgcxfxM3etGBCPSQ08DgkejoWjBaEvZnj8P89J3OGQQ== - dependencies: - mozilla-version-comparator "^1.0.2" - unzip-stream "^0.3.0" - yargs "^10.0.3" - firefox-profile@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/firefox-profile/-/firefox-profile-4.2.0.tgz#5442273fa134d20572d2b75b94b001f02cb89dd4" @@ -5434,11 +5369,6 @@ get-browser-rtc@^1.0.0: resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk= -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -6167,11 +6097,6 @@ into-stream@^6.0.0: from2 "^2.3.0" p-is-promise "^3.0.0" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - invert-kv@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" @@ -7952,13 +7877,6 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - lcid@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0" @@ -8859,14 +8777,6 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - ltgt@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" @@ -8950,13 +8860,6 @@ mem-storage-area@1.0.3: resolved "https://registry.yarnpkg.com/mem-storage-area/-/mem-storage-area-1.0.3.tgz#a8a9bc75eaf5ae69efe185fedd475fb731d14880" integrity sha512-UwDsIj6Fmml8NPiPp7QGJavwZVbXMFLvVDfshv3486KQHDcxOExr0od4YkUdJVWFlujZxtct59mEtsPvAQTOMQ== -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - mem@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3" @@ -9243,11 +9146,6 @@ moz-download-url@^2.1.0: resolved "https://registry.yarnpkg.com/moz-download-url/-/moz-download-url-2.1.0.tgz#5798304b334d2840bca8a623135ac23e3db62a07" integrity sha512-TjnvBpx1eHi5Dv5HorCYBTgCvc25jOTviCQ8os0GTkM/yWgaMYXJgJGIykpzZcmN0o37cDBCEiwaKVd9PE1B4A== -mozilla-version-comparator@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/mozilla-version-comparator/-/mozilla-version-comparator-1.0.2.tgz#f86731e70c15d1ff5eb288d13b4db8d1e605f7fc" - integrity sha1-+Gcx5wwV0f9esojRO0240eYF9/w= - mri@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" @@ -10014,13 +9912,6 @@ npm-run-all@4.1.5: shell-quote "^1.6.1" string.prototype.padend "^3.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -10264,15 +10155,6 @@ os-locale@5.0.0: lcid "^3.0.0" mem "^5.0.0" -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - os-shim@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" @@ -10701,7 +10583,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: +path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -11251,11 +11133,6 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -11695,11 +11572,6 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -12541,7 +12413,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -12684,11 +12556,6 @@ strip-dirs@^2.0.0: dependencies: is-natural-number "^4.0.1" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -13069,11 +12936,6 @@ transform-loader@0.2.4: dependencies: loader-utils "^1.0.2" -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -13276,14 +13138,6 @@ unordered-array-remove@^1.0.2: resolved "https://registry.yarnpkg.com/unordered-array-remove/-/unordered-array-remove-1.0.2.tgz#c546e8f88e317a0cf2644c97ecb57dba66d250ef" integrity sha1-xUbo+I4xegzyZEyX7LV9umbSUO8= -unzip-stream@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/unzip-stream/-/unzip-stream-0.3.0.tgz#c30c054cd6b0d64b13a23cd3ece911eb0b2b52d8" - integrity sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A== - dependencies: - binary "^0.3.0" - mkdirp "^0.5.1" - upath@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -13729,14 +13583,6 @@ workerpool@6.1.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" @@ -13854,11 +13700,6 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -13869,11 +13710,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.0, yallist@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -13910,13 +13746,6 @@ yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20" integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA== -yargs-parser@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" - integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ== - dependencies: - camelcase "^4.1.0" - yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -13940,24 +13769,6 @@ yargs@16.2.0, yargs@^16.0.3, yargs@~16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^10.0.3: - version "10.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" - integrity sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^8.1.0" - yargs@^13.2.1: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"