From 7e17317ce1642e258da69dc3beacec91a39caf4b Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Wed, 8 Dec 2021 11:51:16 +0100 Subject: [PATCH] Fix MetaMask mobile on iOS (#4229) * Try Firefox hack for MetaMask mobile on iOS * Try manually injecting mobile provider * Dont import * Add ReactNativePostMessageStream * Add full MM mobile injection * Simplify * Remove web3 shim * Update deps * Update FF hack * Add comment * Show MM mobile links for web3 mobile install --- package.json | 4 +- .../WalletUnlock/Web3ProviderInstall.tsx | 2 +- src/config/wallets.ts | 4 +- src/vendor/MetaMask/MobilePortStream.js | 109 ++++++++++++++ .../MetaMask/ReactNativePostMessageStream.js | 80 +++++++++++ src/vendor/inpage-metamask-mobile.js | 115 +++++++++++++++ src/vendor/inpage-metamask.js | 26 ++-- yarn.lock | 136 ++++++++++-------- 8 files changed, 402 insertions(+), 74 deletions(-) create mode 100644 src/vendor/MetaMask/MobilePortStream.js create mode 100644 src/vendor/MetaMask/ReactNativePostMessageStream.js create mode 100644 src/vendor/inpage-metamask-mobile.js diff --git a/package.json b/package.json index 0f64ed834df..10e84679862 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "@ethersproject/transactions": "5.4.0", "@ethersproject/units": "5.4.0", "@ethersproject/wallet": "5.4.0", - "@metamask/inpage-provider": "6.0.1", + "@metamask/inpage-provider": "8.0.3", + "@metamask/object-multiplex": "1.2.0", + "@metamask/post-message-stream": "4.0.0", "@mycrypto/eth-scan": "3.4.4", "@mycrypto/ui": "0.24.1", "@mycrypto/unlock-scan": "1.2.0", diff --git a/src/components/WalletUnlock/Web3ProviderInstall.tsx b/src/components/WalletUnlock/Web3ProviderInstall.tsx index 22e79a03d3b..b1f674ea111 100644 --- a/src/components/WalletUnlock/Web3ProviderInstall.tsx +++ b/src/components/WalletUnlock/Web3ProviderInstall.tsx @@ -31,7 +31,7 @@ const AppLinkContainer = styled.div` `; function InstallTrunk() { - const providers = [WALLETS_CONFIG.TRUST, WALLETS_CONFIG.COINBASE]; + const providers = [WALLETS_CONFIG.METAMASK, WALLETS_CONFIG.COINBASE]; return ( {providers.map((provider) => ( diff --git a/src/config/wallets.ts b/src/config/wallets.ts index 51b9bb5b802..70b8dc213ea 100644 --- a/src/config/wallets.ts +++ b/src/config/wallets.ts @@ -67,7 +67,9 @@ export const WALLETS_CONFIG: Record = { description: 'ADD_WEB3DESC', helpLink: getKBHelpArticle(MIGRATE_TO_METAMASK), install: { - getItLink: 'https://metamask.io' + getItLink: 'https://metamask.io', + appStore: 'https://apps.apple.com/us/app/metamask/id1438144202', + googlePlay: 'https://play.google.com/store/apps/details?id=io.metamask' }, flags: { supportsNonce: false diff --git a/src/vendor/MetaMask/MobilePortStream.js b/src/vendor/MetaMask/MobilePortStream.js new file mode 100644 index 00000000000..92c0cc60c3d --- /dev/null +++ b/src/vendor/MetaMask/MobilePortStream.js @@ -0,0 +1,109 @@ +const { Duplex } = require('readable-stream'); +const { inherits } = require('util'); + +const noop = () => undefined; + +module.exports = MobilePortStream; + +inherits(MobilePortStream, Duplex); + +/** + * Creates a stream that's both readable and writable. + * The stream supports arbitrary objects. + * + * @class + * @param {Object} port Remote Port object + */ +function MobilePortStream(port) { + Duplex.call(this, { + objectMode: true + }); + this._name = port.name; + this._targetWindow = window; + this._port = port; + this._origin = location.origin; + window.addEventListener('message', this._onMessage.bind(this), false); +} + +/** + * Callback triggered when a message is received from + * the remote Port associated with this Stream. + * + * @private + * @param {Object} msg - Payload from the onMessage listener of Port + */ +MobilePortStream.prototype._onMessage = function (event) { + const msg = event.data; + + // validate message + if (this._origin !== '*' && event.origin !== this._origin) { + return; + } + if (!msg || typeof msg !== 'object') { + return; + } + if (!msg.data || typeof msg.data !== 'object') { + return; + } + if (msg.target && msg.target !== this._name) { + return; + } + // Filter outgoing messages + if (msg.data.data && msg.data.data.toNative) { + return; + } + + if (Buffer.isBuffer(msg)) { + delete msg._isBuffer; + const data = Buffer.from(msg); + this.push(data); + } else { + this.push(msg); + } +}; + +/** + * Callback triggered when the remote Port + * associated with this Stream disconnects. + * + * @private + */ +MobilePortStream.prototype._onDisconnect = function () { + this.destroy(); +}; + +/** + * Explicitly sets read operations to a no-op + */ +MobilePortStream.prototype._read = noop; + +/** + * Called internally when data should be written to + * this writable stream. + * + * @private + * @param {*} msg Arbitrary object to write + * @param {string} encoding Encoding to use when writing payload + * @param {Function} cb Called when writing is complete or an error occurs + */ +MobilePortStream.prototype._write = function (msg, _encoding, cb) { + try { + if (Buffer.isBuffer(msg)) { + const data = msg.toJSON(); + data._isBuffer = true; + window.ReactNativeWebView.postMessage( + JSON.stringify({ ...data, origin: window.location.href }) + ); + } else { + if (msg.data) { + msg.data.toNative = true; + } + window.ReactNativeWebView.postMessage( + JSON.stringify({ ...msg, origin: window.location.href }) + ); + } + } catch (err) { + return cb(new Error('MobilePortStream - disconnected')); + } + return cb(); +}; diff --git a/src/vendor/MetaMask/ReactNativePostMessageStream.js b/src/vendor/MetaMask/ReactNativePostMessageStream.js new file mode 100644 index 00000000000..ab1f53ff929 --- /dev/null +++ b/src/vendor/MetaMask/ReactNativePostMessageStream.js @@ -0,0 +1,80 @@ +const { Duplex } = require('readable-stream'); +const { inherits } = require('util'); + +const noop = () => undefined; + +module.exports = PostMessageStream; + +inherits(PostMessageStream, Duplex); + +function PostMessageStream(opts) { + Duplex.call(this, { + objectMode: true + }); + + this._name = opts.name; + this._target = opts.target; + this._targetWindow = opts.targetWindow || window; + this._origin = opts.targetWindow ? '*' : location.origin; + + // initialization flags + this._init = false; + this._haveSyn = false; + + window.addEventListener('message', this._onMessage.bind(this), false); + // send syncorization message + this._write('SYN', null, noop); + this.cork(); +} + +// private +PostMessageStream.prototype._onMessage = function (event) { + const msg = event.data; + + // validate message + if (this._origin !== '*' && event.origin !== this._origin) { + return; + } + if (event.source !== this._targetWindow && window === top) { + return; + } + if (!msg || typeof msg !== 'object') { + return; + } + if (msg.target !== this._name) { + return; + } + if (!msg.data) { + return; + } + + if (this._init) { + // forward message + try { + this.push(msg.data); + } catch (err) { + this.emit('error', err); + } + } else if (msg.data === 'SYN') { + this._haveSyn = true; + this._write('ACK', null, noop); + } else if (msg.data === 'ACK') { + this._init = true; + if (!this._haveSyn) { + this._write('ACK', null, noop); + } + this.uncork(); + } +}; + +// stream plumbing +PostMessageStream.prototype._read = noop; + +PostMessageStream.prototype._write = function (data, _encoding, cb) { + const message = { + target: this._target, + data + }; + this._targetWindow.postMessage(message, this._origin); + cb(); +}; diff --git a/src/vendor/inpage-metamask-mobile.js b/src/vendor/inpage-metamask-mobile.js new file mode 100644 index 00000000000..a44b28101ab --- /dev/null +++ b/src/vendor/inpage-metamask-mobile.js @@ -0,0 +1,115 @@ +// Based of: https://github.com/MetaMask/mobile-provider/blob/main/src/inpage/index.js + +import { initializeProvider } from '@metamask/inpage-provider'; +import ObjectMultiplex from '@metamask/object-multiplex'; +import pump from 'pump'; + +import MobilePortStream from './MetaMask/MobilePortStream'; +import ReactNativePostMessageStream from './MetaMask/ReactNativePostMessageStream'; + +const INPAGE = 'metamask-inpage'; +const CONTENT_SCRIPT = 'metamask-contentscript'; +const PROVIDER = 'metamask-provider'; + +export const injectMobile = () => { + // Setup stream for content script communication + const metamaskStream = new ReactNativePostMessageStream({ + name: INPAGE, + target: CONTENT_SCRIPT + }); + + // Initialize provider object (window.ethereum) + initializeProvider({ + connectionStream: metamaskStream, + shouldSendMetadata: false + }); + + setupProviderStreams(); +}; + +// Functions + +/** + * Setup function called from content script after the DOM is ready. + */ +function setupProviderStreams() { + // the transport-specific streams for communication between inpage and background + const pageStream = new ReactNativePostMessageStream({ + name: CONTENT_SCRIPT, + target: INPAGE + }); + + const appStream = new MobilePortStream({ + name: CONTENT_SCRIPT + }); + + // create and connect channel muxes + // so we can handle the channels individually + const pageMux = new ObjectMultiplex(); + pageMux.setMaxListeners(25); + const appMux = new ObjectMultiplex(); + appMux.setMaxListeners(25); + + pump(pageMux, pageStream, pageMux, (err) => + logStreamDisconnectWarning('MetaMask Inpage Multiplex', err) + ); + pump(appMux, appStream, appMux, (err) => { + logStreamDisconnectWarning('MetaMask Background Multiplex', err); + notifyProviderOfStreamFailure(); + }); + + // forward communication across inpage-background for these channels only + forwardTrafficBetweenMuxes(PROVIDER, pageMux, appMux); +} + +/** + * Set up two-way communication between muxes for a single, named channel. + * + * @param {string} channelName - The name of the channel. + * @param {ObjectMultiplex} muxA - The first mux. + * @param {ObjectMultiplex} muxB - The second mux. + */ +function forwardTrafficBetweenMuxes(channelName, muxA, muxB) { + const channelA = muxA.createStream(channelName); + const channelB = muxB.createStream(channelName); + pump(channelA, channelB, channelA, (err) => + logStreamDisconnectWarning(`MetaMask muxed traffic for channel "${channelName}" failed.`, err) + ); +} + +/** + * Error handler for page to extension stream disconnections + * + * @param {string} remoteLabel - Remote stream name + * @param {Error} err - Stream connection error + */ +function logStreamDisconnectWarning(remoteLabel, err) { + let warningMsg = `MetamaskContentscript - lost connection to ${remoteLabel}`; + if (err) { + warningMsg += `\n${err.stack}`; + } + console.warn(warningMsg); + console.error(err); +} + +/** + * This function must ONLY be called in pump destruction/close callbacks. + * Notifies the inpage context that streams have failed, via window.postMessage. + * Relies on @metamask/object-multiplex and post-message-stream implementation details. + */ +function notifyProviderOfStreamFailure() { + window.postMessage( + { + target: INPAGE, // the post-message-stream "target" + data: { + // this object gets passed to object-multiplex + name: PROVIDER, // the object-multiplex channel name + data: { + jsonrpc: '2.0', + method: 'METAMASK_STREAM_FAILURE' + } + } + }, + window.location.origin + ); +} diff --git a/src/vendor/inpage-metamask.js b/src/vendor/inpage-metamask.js index a8a16a17b63..27bc9d4a195 100644 --- a/src/vendor/inpage-metamask.js +++ b/src/vendor/inpage-metamask.js @@ -1,20 +1,28 @@ -import { initProvider } from '@metamask/inpage-provider'; -import LocalMessageDuplexStream from 'post-message-stream'; +import { initializeProvider } from '@metamask/inpage-provider'; +import { WindowPostMessageStream } from '@metamask/post-message-stream'; -// Firefox Metamask Hack +import { injectMobile } from './inpage-metamask-mobile'; + +// Metamask injection hack // Due to https://github.com/MetaMask/metamask-extension/issues/3133 (() => { - if (!window.ethereum && !window.web3 && navigator.userAgent.includes('Firefox')) { + if (window.ethereum || window.web3) { + return; + } + if (navigator.userAgent.includes('Firefox')) { // setup background connection - const metamaskStream = new LocalMessageDuplexStream({ - name: 'inpage', - target: 'contentscript' + const metamaskStream = new WindowPostMessageStream({ + name: 'metamask-inpage', + target: 'metamask-contentscript' }); // this will initialize the provider and set it as window.ethereum - initProvider({ - connectionStream: metamaskStream + initializeProvider({ + connectionStream: metamaskStream, + shouldShimWeb3: true }); + } else if (navigator.userAgent.includes('iPhone')) { + injectMobile(); } })(); diff --git a/yarn.lock b/yarn.lock index 99b083f4860..c0f8ede51df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3379,20 +3379,40 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== -"@metamask/inpage-provider@6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@metamask/inpage-provider/-/inpage-provider-6.0.1.tgz#09bf3a3157d604fb511133c5a6e8eb1178d776bc" - integrity sha512-kcc2Tmq6bgjMyYxCgDJczdCAUBmAR4FPo+zmN0np02y/KNgDlzuKLdsrv3Y9B/S5XTxjuW95xYtMaxXZ5drEkQ== +"@metamask/inpage-provider@8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@metamask/inpage-provider/-/inpage-provider-8.0.3.tgz#65f636233a13a00e1f199a421bdfa8099ed28ea4" + integrity sha512-pj9tGNoS1edohuRJzxOuILRqRrQTdgu5mJwMwa9wuOZIMQLFZtr3g2T6vayPBwoNkE1FzLhs/osUqaVQDRfDvQ== dependencies: - eth-json-rpc-errors "^2.0.2" + "@metamask/object-multiplex" "^1.1.0" + "@metamask/safe-event-emitter" "^2.0.0" + eth-rpc-errors "^4.0.2" fast-deep-equal "^2.0.1" - json-rpc-engine "^5.1.5" - json-rpc-middleware-stream "^2.1.1" - loglevel "^1.6.1" - obj-multiplex "^1.0.0" - obs-store "^4.0.3" + is-stream "^2.0.0" + json-rpc-engine "^6.1.0" + json-rpc-middleware-stream "^3.0.0" pump "^3.0.0" - safe-event-emitter "^1.0.1" + +"@metamask/object-multiplex@1.2.0", "@metamask/object-multiplex@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@metamask/object-multiplex/-/object-multiplex-1.2.0.tgz#38fc15c142f61939391e1b9a8eed679696c7e4f4" + integrity sha512-hksV602d3NWE2Q30Mf2Np1WfVKaGqfJRy9vpHAmelbaD0OkDt06/0KQkRR6UVYdMbTbkuEu8xN5JDUU80inGwQ== + dependencies: + end-of-stream "^1.4.4" + once "^1.4.0" + readable-stream "^2.3.3" + +"@metamask/post-message-stream@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/post-message-stream/-/post-message-stream-4.0.0.tgz#72f120e562346ca86ccc9b3684023ad44265f0df" + integrity sha512-r0JcoWXNuHycProx8ClxiIElJY/GVb/0/WWXTMsZu7qDejLo52VNXlwfydCdVjbMXeoT2nK1Yt3d5gjmHy5BWw== + dependencies: + readable-stream "2.3.3" + +"@metamask/safe-event-emitter@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" + integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" @@ -10513,7 +10533,7 @@ encoding@^0.1.11: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -10941,17 +10961,10 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eth-json-rpc-errors@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" - integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== - dependencies: - fast-safe-stringify "^2.0.6" - -eth-rpc-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" - integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== +eth-rpc-errors@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz#6ddb6190a4bf360afda82790bb7d9d5e724f423a" + integrity sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg== dependencies: fast-safe-stringify "^2.0.6" @@ -14914,21 +14927,21 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-rpc-engine@^5.1.5: - version "5.4.0" - resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" - integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== +json-rpc-engine@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz#bf5ff7d029e1c1bf20cb6c0e9f348dcd8be5a393" + integrity sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ== dependencies: - eth-rpc-errors "^3.0.0" - safe-event-emitter "^1.0.1" + "@metamask/safe-event-emitter" "^2.0.0" + eth-rpc-errors "^4.0.2" -json-rpc-middleware-stream@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-2.1.1.tgz#06e5409e201e7ddeae47bef29f7059eafd4d5325" - integrity sha512-WZheufPN+/RKkjXQP3lK5tFYblqG0n+oYv5qpammwwY2vsJRB7mM4Txhr4ajzvYEZi1UkENnplrmaYiqaqafaA== +json-rpc-middleware-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-3.0.0.tgz#8540331d884f36b9e0ad31054cc68ac6b5a89b52" + integrity sha512-JmZmlehE0xF3swwORpLHny/GvW3MZxCsb2uFNBrn8TOqMqivzCfz232NSDLLOtIQlrPlgyEjiYpyzyOPFOzClw== dependencies: + "@metamask/safe-event-emitter" "^2.0.0" readable-stream "^2.3.3" - safe-event-emitter "^1.0.1" json-schema-traverse@^0.4.1: version "0.4.1" @@ -15538,7 +15551,7 @@ logalot@^2.0.0, logalot@^2.1.0: figures "^1.3.5" squeak "^1.0.0" -loglevel@^1.6.1, loglevel@^1.6.6: +loglevel@^1.6.6: version "1.7.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== @@ -16781,15 +16794,6 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -obj-multiplex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/obj-multiplex/-/obj-multiplex-1.0.0.tgz#2f2ae6bfd4ae11befe742ea9ea5b36636eabffc1" - integrity sha1-Lyrmv9SuEb7+dC6p6ls2Y26r/8E= - dependencies: - end-of-stream "^1.4.0" - once "^1.4.0" - readable-stream "^2.3.3" - object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -16915,16 +16919,6 @@ obliterator@^1.6.1: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== -obs-store@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/obs-store/-/obs-store-4.0.3.tgz#b632ec7814baa604fae084a4c97e87c0b7a6d14c" - integrity sha512-+mm13kCRDv6IcvUDKTw0LIy5+dQhIktYaR/RwwZUFzOTi/fjMaNBnk42Adb94qZqJ00qWkjhQSZH7MXlKnTi8A== - dependencies: - readable-stream "^2.2.2" - safe-event-emitter "^1.0.1" - through2 "^2.0.3" - xtend "^4.0.1" - obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -18094,6 +18088,11 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -19082,6 +19081,19 @@ read-pkg@^5.2.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + integrity sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -19858,13 +19870,6 @@ safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-event-emitter@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" - integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== - dependencies: - events "^3.0.0" - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -20829,6 +20834,13 @@ string_decoder@^1.0.0, string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + integrity sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ== + dependencies: + safe-buffer "~5.1.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -21595,7 +21607,7 @@ throttle-debounce@^3.0.1: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== -through2@^2.0.0, through2@^2.0.3: +through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==