From 2a3e634ab90ff3e8631007a4115728e60fcbef6d Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 9 Apr 2018 13:40:15 -0700 Subject: [PATCH 01/79] package.json - update engines field to require node v8.x.x --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 310e357ca..581d610ae 100644 --- a/package.json +++ b/package.json @@ -272,6 +272,6 @@ "watchify": "^3.9.0" }, "engines": { - "node": ">=0.8.0" + "node": ">=8.0.0" } } From 4a2db1d9e14aad7bd8e63c3073180c39305862d8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 9 Apr 2018 13:55:19 -0700 Subject: [PATCH 02/79] ci - complete all tests before running extras --- .circleci/config.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ee2054130..6ed731225 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,15 +19,6 @@ workflows: requires: - prep-deps-npm - prep-build - - job-screens: - requires: - - prep-deps-npm - - prep-build - - job-publish: - requires: - - prep-deps-npm - - prep-build - - job-screens - test-unit: requires: - prep-deps-npm @@ -54,11 +45,21 @@ workflows: - test-lint - test-unit - test-e2e - - job-screens - test-integration-mascara-chrome - test-integration-mascara-firefox - test-integration-flat-chrome - test-integration-flat-firefox + - job-screens: + requires: + - prep-deps-npm + - prep-build + - all-tests-pass + - job-publish: + requires: + - prep-deps-npm + - prep-build + - job-screens + - all-tests-pass jobs: prep-deps-npm: From 2ccd9ca29d9474e376fa6fcfed0d2655e0589462 Mon Sep 17 00:00:00 2001 From: nyatla Date: Tue, 10 Apr 2018 18:46:09 +0900 Subject: [PATCH 03/79] Changed mouse cursor to "pointer" on the cancel text at the top left of the page. --- ui/app/css/itcss/components/add-token.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/css/itcss/components/add-token.scss b/ui/app/css/itcss/components/add-token.scss index 2fdda6f43..01579c27c 100644 --- a/ui/app/css/itcss/components/add-token.scss +++ b/ui/app/css/itcss/components/add-token.scss @@ -36,6 +36,7 @@ font-weight: 400; line-height: 21px; margin-left: 8px; + cursor:pointer; } } From 5166e1a9fda8fce749110c28ace11a5d2cedec9e Mon Sep 17 00:00:00 2001 From: nyatla Date: Tue, 10 Apr 2018 18:47:59 +0900 Subject: [PATCH 04/79] Expanded dropdown menu width of title. Title overflowed in Japanese. --- ui/app/css/itcss/components/network.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/css/itcss/components/network.scss b/ui/app/css/itcss/components/network.scss index c34b5cd06..6c8be0b6d 100644 --- a/ui/app/css/itcss/components/network.scss +++ b/ui/app/css/itcss/components/network.scss @@ -139,7 +139,7 @@ .network-dropdown-title { height: 25px; - width: 75px; + width: 120px; color: $white; font-family: Roboto; font-size: 18px; From c9a4077207923ac5fa5c0f97e65b130c61532051 Mon Sep 17 00:00:00 2001 From: nyatla Date: Tue, 10 Apr 2018 18:51:44 +0900 Subject: [PATCH 05/79] Reduced button padding. Title overflowed in Japanese. --- ui/app/css/itcss/components/send.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss index 89d2be891..362feeec8 100644 --- a/ui/app/css/itcss/components/send.scss +++ b/ui/app/css/itcss/components/send.scss @@ -789,6 +789,7 @@ display: flex; justify-content: center; align-items: center; + padding: 0 3px; cursor: pointer; } From 2d7c3c2b00a698b19ac015624154c3c1cd2619b2 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 6 Apr 2018 11:07:20 -0700 Subject: [PATCH 06/79] meta - transactions - create a transactions dir in controller and move relevant files into it --- .../index.js} | 68 +------- .../lib/tx-state-history-helper.js | 0 .../controllers/transactions/lib/util.js | 66 ++++++++ .../transactions}/nonce-tracker.js | 0 .../transactions}/pending-tx-tracker.js | 0 .../transactions}/tx-gas-utils.js | 2 +- .../transactions}/tx-state-manager.js | 24 ++- app/scripts/migrations/018.js | 2 +- test/unit/nonce-tracker-test.js | 2 +- test/unit/pending-tx-test.js | 2 +- test/unit/tx-controller-test.js | 95 +---------- test/unit/tx-gas-util-test.js | 81 ++++++++-- test/unit/tx-state-history-helper-test.js | 2 +- test/unit/tx-state-history-helper.js | 2 +- test/unit/tx-state-manager-test.js | 4 +- test/unit/tx-utils-test.js | 147 ++++++++++-------- 16 files changed, 256 insertions(+), 241 deletions(-) rename app/scripts/controllers/{transactions.js => transactions/index.js} (84%) rename app/scripts/{ => controllers/transactions}/lib/tx-state-history-helper.js (100%) create mode 100644 app/scripts/controllers/transactions/lib/util.js rename app/scripts/{lib => controllers/transactions}/nonce-tracker.js (100%) rename app/scripts/{lib => controllers/transactions}/pending-tx-tracker.js (100%) rename app/scripts/{lib => controllers/transactions}/tx-gas-utils.js (99%) rename app/scripts/{lib => controllers/transactions}/tx-state-manager.js (93%) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions/index.js similarity index 84% rename from app/scripts/controllers/transactions.js rename to app/scripts/controllers/transactions/index.js index 336b0d8f7..6f66e3a1e 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions/index.js @@ -3,11 +3,11 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const Transaction = require('ethereumjs-tx') const EthQuery = require('ethjs-query') -const TransactionStateManager = require('../lib/tx-state-manager') -const TxGasUtil = require('../lib/tx-gas-utils') -const PendingTransactionTracker = require('../lib/pending-tx-tracker') -const NonceTracker = require('../lib/nonce-tracker') - +const TransactionStateManager = require('./tx-state-manager') +const TxGasUtil = require('./tx-gas-utils') +const PendingTransactionTracker = require('./pending-tx-tracker') +const NonceTracker = require('./nonce-tracker') +const txUtils = require('./lib/util') /* Transaction Controller is an aggregate of sub-controllers and trackers composing them in a way to be exposed to the metamask controller @@ -185,8 +185,8 @@ module.exports = class TransactionController extends EventEmitter { async addUnapprovedTransaction (txParams) { // validate - const normalizedTxParams = this._normalizeTxParams(txParams) - this._validateTxParams(normalizedTxParams) + const normalizedTxParams = txUtils.normalizeTxParams(txParams) + txUtils.validateTxParams(normalizedTxParams) // construct txMeta let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) this.addTx(txMeta) @@ -314,60 +314,6 @@ module.exports = class TransactionController extends EventEmitter { // PRIVATE METHODS // - _normalizeTxParams (txParams) { - // functions that handle normalizing of that key in txParams - const whiteList = { - from: from => ethUtil.addHexPrefix(from).toLowerCase(), - to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(), - nonce: nonce => ethUtil.addHexPrefix(nonce), - value: value => ethUtil.addHexPrefix(value), - data: data => ethUtil.addHexPrefix(data), - gas: gas => ethUtil.addHexPrefix(gas), - gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice), - } - - // apply only keys in the whiteList - const normalizedTxParams = {} - Object.keys(whiteList).forEach((key) => { - if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) - }) - - return normalizedTxParams - } - - _validateTxParams (txParams) { - this._validateFrom(txParams) - this._validateRecipient(txParams) - if ('value' in txParams) { - const value = txParams.value.toString() - if (value.includes('-')) { - throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) - } - - if (value.includes('.')) { - throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) - } - } - } - - _validateFrom (txParams) { - if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`) - if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address') - } - - _validateRecipient (txParams) { - if (txParams.to === '0x' || txParams.to === null ) { - if (txParams.data) { - delete txParams.to - } else { - throw new Error('Invalid recipient address') - } - } else if ( txParams.to !== undefined && !ethUtil.isValidAddress(txParams.to) ) { - throw new Error('Invalid recipient address') - } - return txParams - } - _markNonceDuplicatesDropped (txId) { this.txStateManager.setTxStatusConfirmed(txId) // get the confirmed transactions nonce and from address diff --git a/app/scripts/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js similarity index 100% rename from app/scripts/lib/tx-state-history-helper.js rename to app/scripts/controllers/transactions/lib/tx-state-history-helper.js diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js new file mode 100644 index 000000000..f403b0758 --- /dev/null +++ b/app/scripts/controllers/transactions/lib/util.js @@ -0,0 +1,66 @@ +const { + addHexPrefix, + isValidAddress, +} = require('ethereumjs-util') + +module.exports = { + normalizeTxParams, + validateTxParams, + validateFrom, + validateRecipient +} + + +function normalizeTxParams (txParams) { + // functions that handle normalizing of that key in txParams + const whiteList = { + from: from => addHexPrefix(from).toLowerCase(), + to: to => addHexPrefix(txParams.to).toLowerCase(), + nonce: nonce => addHexPrefix(nonce), + value: value => addHexPrefix(value), + data: data => addHexPrefix(data), + gas: gas => addHexPrefix(gas), + gasPrice: gasPrice => addHexPrefix(gasPrice), + } + + // apply only keys in the whiteList + const normalizedTxParams = {} + Object.keys(whiteList).forEach((key) => { + if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) + }) + + return normalizedTxParams +} + +function validateTxParams (txParams) { + validateFrom(txParams) + validateRecipient(txParams) + if ('value' in txParams) { + const value = txParams.value.toString() + if (value.includes('-')) { + throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + } + + if (value.includes('.')) { + throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) + } + } +} + +function validateFrom (txParams) { + if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`) + if (!isValidAddress(txParams.from)) throw new Error('Invalid from address') +} + +function validateRecipient (txParams) { + if (txParams.to === '0x' || txParams.to === null ) { + if (txParams.data) { + delete txParams.to + } else { + throw new Error('Invalid recipient address') + } + } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) { + throw new Error('Invalid recipient address') + } + return txParams +} \ No newline at end of file diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js similarity index 100% rename from app/scripts/lib/nonce-tracker.js rename to app/scripts/controllers/transactions/nonce-tracker.js diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js similarity index 100% rename from app/scripts/lib/pending-tx-tracker.js rename to app/scripts/controllers/transactions/pending-tx-tracker.js diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js similarity index 99% rename from app/scripts/lib/tx-gas-utils.js rename to app/scripts/controllers/transactions/tx-gas-utils.js index c579e462a..f40542603 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/controllers/transactions/tx-gas-utils.js @@ -3,7 +3,7 @@ const { hexToBn, BnMultiplyByFraction, bnToHex, -} = require('./util') +} = require('../../lib/util') const { addHexPrefix } = require('ethereumjs-util') const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js similarity index 93% rename from app/scripts/lib/tx-state-manager.js rename to app/scripts/controllers/transactions/tx-state-manager.js index c6d10ee62..cb24b8c99 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -1,9 +1,9 @@ const extend = require('xtend') const EventEmitter = require('events') const ObservableStore = require('obs-store') -const createId = require('./random-id') +const createId = require('../../lib/random-id') const ethUtil = require('ethereumjs-util') -const txStateHistoryHelper = require('./tx-state-history-helper') +const txStateHistoryHelper = require('./lib/tx-state-history-helper') // STATUS METHODS // statuses: @@ -92,7 +92,9 @@ module.exports = class TransactionStateManager extends EventEmitter { // or rejected tx's. // not tx's that are pending or unapproved if (txCount > txHistoryLimit - 1) { - let index = transactions.findIndex((metaTx) => metaTx.status === 'confirmed' || metaTx.status === 'rejected') + let index = transactions.findIndex((metaTx) => { + return this.getFinalStates().includes(metaTx.status) + }) if (index !== -1) { transactions.splice(index, 1) } @@ -258,6 +260,16 @@ module.exports = class TransactionStateManager extends EventEmitter { this._setTxStatus(txId, 'failed') } + // returns an array of states that can be considered final + getFinalStates () { + return [ + 'rejected', // the user has responded no! + 'confirmed', // the tx has been included in a block. + 'failed', // the tx failed for some reason, included on tx data. + 'dropped', // the tx nonce was already used + ] + } + wipeTransactions (address) { // network only tx const txs = this.getFullTxList() @@ -273,9 +285,8 @@ module.exports = class TransactionStateManager extends EventEmitter { // PRIVATE METHODS // - // Should find the tx in the tx list and - // update it. - // should set the status in txData + // STATUS METHODS + // statuses: // - `'unapproved'` the user has not responded // - `'rejected'` the user has responded no! // - `'approved'` the user has approved the tx @@ -283,6 +294,7 @@ module.exports = class TransactionStateManager extends EventEmitter { // - `'submitted'` the tx is sent to a server // - `'confirmed'` the tx has been included in a block. // - `'failed'` the tx failed for some reason, included on tx data. + // - `'dropped'` the tx nonce was already used _setTxStatus (txId, status) { const txMeta = this.getTx(txId) txMeta.status = status diff --git a/app/scripts/migrations/018.js b/app/scripts/migrations/018.js index bea1fe3da..ffbf24a4b 100644 --- a/app/scripts/migrations/018.js +++ b/app/scripts/migrations/018.js @@ -7,7 +7,7 @@ This migration updates "transaction state history" to diffs style */ const clone = require('clone') -const txStateHistoryHelper = require('../lib/tx-state-history-helper') +const txStateHistoryHelper = require('../controllers/transactions/lib/tx-state-history-helper') module.exports = { diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 5a27882ef..cf26945d3 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -1,5 +1,5 @@ const assert = require('assert') -const NonceTracker = require('../../app/scripts/lib/nonce-tracker') +const NonceTracker = require('../../app/scripts/controllers/transactions/nonce-tracker') const MockTxGen = require('../lib/mock-tx-gen') let providerResultStub = {} diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js index f0b4e3bfc..001b86dd1 100644 --- a/test/unit/pending-tx-test.js +++ b/test/unit/pending-tx-test.js @@ -4,7 +4,7 @@ const EthTx = require('ethereumjs-tx') const ObservableStore = require('obs-store') const clone = require('clone') const { createTestProviderTools } = require('../stub/provider') -const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker') +const PendingTransactionTracker = require('../../app/scripts/controllers/transactions/pending-tx-tracker') const MockTxGen = require('../lib/mock-tx-gen') const sinon = require('sinon') const noop = () => true diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 824574ff2..08f16d83b 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -5,7 +5,7 @@ const EthjsQuery = require('ethjs-query') const ObservableStore = require('obs-store') const sinon = require('sinon') const TransactionController = require('../../app/scripts/controllers/transactions') -const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') +const TxGasUtils = require('../../app/scripts/controllers/transactions/tx-gas-utils') const { createTestProviderTools } = require('../stub/provider') const noop = () => true @@ -210,99 +210,6 @@ describe('Transaction Controller', function () { }) }) - describe('#_validateTxParams', function () { - it('does not throw for positive values', function () { - var sample = { - from: '0x1678a085c290ebd122dc42cba69373b5953b831d', - value: '0x01', - } - txController._validateTxParams(sample) - }) - - it('returns error for negative values', function () { - var sample = { - from: '0x1678a085c290ebd122dc42cba69373b5953b831d', - value: '-0x01', - } - try { - txController._validateTxParams(sample) - } catch (err) { - assert.ok(err, 'error') - } - }) - }) - - describe('#_normalizeTxParams', () => { - it('should normalize txParams', () => { - let txParams = { - chainId: '0x1', - from: 'a7df1beDBF813f57096dF77FCd515f0B3900e402', - to: null, - data: '68656c6c6f20776f726c64', - random: 'hello world', - } - - let normalizedTxParams = txController._normalizeTxParams(txParams) - - assert(!normalizedTxParams.chainId, 'their should be no chainId') - assert(!normalizedTxParams.to, 'their should be no to address if null') - assert.equal(normalizedTxParams.from.slice(0, 2), '0x', 'from should be hexPrefixd') - assert.equal(normalizedTxParams.data.slice(0, 2), '0x', 'data should be hexPrefixd') - assert(!('random' in normalizedTxParams), 'their should be no random key in normalizedTxParams') - - txParams.to = 'a7df1beDBF813f57096dF77FCd515f0B3900e402' - normalizedTxParams = txController._normalizeTxParams(txParams) - assert.equal(normalizedTxParams.to.slice(0, 2), '0x', 'to should be hexPrefixd') - - }) - }) - - describe('#_validateRecipient', () => { - it('removes recipient for txParams with 0x when contract data is provided', function () { - const zeroRecipientandDataTxParams = { - from: '0x1678a085c290ebd122dc42cba69373b5953b831d', - to: '0x', - data: 'bytecode', - } - const sanitizedTxParams = txController._validateRecipient(zeroRecipientandDataTxParams) - assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x') - }) - - it('should error when recipient is 0x', function () { - const zeroRecipientTxParams = { - from: '0x1678a085c290ebd122dc42cba69373b5953b831d', - to: '0x', - } - assert.throws(() => { txController._validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address') - }) - }) - - - describe('#_validateFrom', () => { - it('should error when from is not a hex string', function () { - - // where from is undefined - const txParams = {} - assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) - - // where from is array - txParams.from = [] - assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) - - // where from is a object - txParams.from = {} - assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) - - // where from is a invalid address - txParams.from = 'im going to fail' - assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address`) - - // should run - txParams.from ='0x1678a085c290ebd122dc42cba69373b5953b831d' - txController._validateFrom(txParams) - }) - }) - describe('#addTx', function () { it('should emit updates', function (done) { const txMeta = { diff --git a/test/unit/tx-gas-util-test.js b/test/unit/tx-gas-util-test.js index 40ea8a7d6..c1d5966da 100644 --- a/test/unit/tx-gas-util-test.js +++ b/test/unit/tx-gas-util-test.js @@ -1,14 +1,77 @@ const assert = require('assert') -const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') -const { createTestProviderTools } = require('../stub/provider') +const Transaction = require('ethereumjs-tx') +const BN = require('bn.js') -describe('Tx Gas Util', function () { - let txGasUtil, provider, providerResultStub - beforeEach(function () { - providerResultStub = {} - provider = createTestProviderTools({ scaffold: providerResultStub }).provider - txGasUtil = new TxGasUtils({ - provider, + +const { hexToBn, bnToHex } = require('../../app/scripts/lib/util') +const TxUtils = require('../../app/scripts/controllers/transactions/tx-gas-utils') + + +describe('txUtils', function () { + let txUtils + + before(function () { + txUtils = new TxUtils(new Proxy({}, { + get: (obj, name) => { + return () => {} + }, + })) + }) + + describe('chain Id', function () { + it('prepares a transaction with the provided chainId', function () { + const txParams = { + to: '0x70ad465e0bab6504002ad58c744ed89c7da38524', + from: '0x69ad465e0bab6504002ad58c744ed89c7da38525', + value: '0x0', + gas: '0x7b0c', + gasPrice: '0x199c82cc00', + data: '0x', + nonce: '0x3', + chainId: 42, + } + const ethTx = new Transaction(txParams) + assert.equal(ethTx.getChainId(), 42, 'chainId is set from tx params') + }) + }) + + describe('addGasBuffer', function () { + it('multiplies by 1.5, when within block gas limit', function () { + // naive estimatedGas: 0x16e360 (1.5 mil) + const inputHex = '0x16e360' + // dummy gas limit: 0x3d4c52 (4 mil) + const blockGasLimitHex = '0x3d4c52' + const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) + const inputBn = hexToBn(inputHex) + const outputBn = hexToBn(output) + const expectedBn = inputBn.muln(1.5) + assert(outputBn.eq(expectedBn), 'returns 1.5 the input value') + }) + + it('uses original estimatedGas, when above block gas limit', function () { + // naive estimatedGas: 0x16e360 (1.5 mil) + const inputHex = '0x16e360' + // dummy gas limit: 0x0f4240 (1 mil) + const blockGasLimitHex = '0x0f4240' + const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) + // const inputBn = hexToBn(inputHex) + const outputBn = hexToBn(output) + const expectedBn = hexToBn(inputHex) + assert(outputBn.eq(expectedBn), 'returns the original estimatedGas value') + }) + + it('buffers up to recommend gas limit recommended ceiling', function () { + // naive estimatedGas: 0x16e360 (1.5 mil) + const inputHex = '0x16e360' + // dummy gas limit: 0x1e8480 (2 mil) + const blockGasLimitHex = '0x1e8480' + const blockGasLimitBn = hexToBn(blockGasLimitHex) + const ceilGasLimitBn = blockGasLimitBn.muln(0.9) + const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) + // const inputBn = hexToBn(inputHex) + // const outputBn = hexToBn(output) + const expectedHex = bnToHex(ceilGasLimitBn) + assert.equal(output, expectedHex, 'returns the gas limit recommended ceiling value') }) }) }) diff --git a/test/unit/tx-state-history-helper-test.js b/test/unit/tx-state-history-helper-test.js index 90cb10713..35e9ef188 100644 --- a/test/unit/tx-state-history-helper-test.js +++ b/test/unit/tx-state-history-helper-test.js @@ -1,6 +1,6 @@ const assert = require('assert') const clone = require('clone') -const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper') +const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper') describe('deepCloneFromTxMeta', function () { it('should clone deep', function () { diff --git a/test/unit/tx-state-history-helper.js b/test/unit/tx-state-history-helper.js index 79ee26d6e..35f7dac57 100644 --- a/test/unit/tx-state-history-helper.js +++ b/test/unit/tx-state-history-helper.js @@ -1,5 +1,5 @@ const assert = require('assert') -const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper') +const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper') const testVault = require('../data/v17-long-history.json') diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js index a5ac13664..e5fe68d0b 100644 --- a/test/unit/tx-state-manager-test.js +++ b/test/unit/tx-state-manager-test.js @@ -1,8 +1,8 @@ const assert = require('assert') const clone = require('clone') const ObservableStore = require('obs-store') -const TxStateManager = require('../../app/scripts/lib/tx-state-manager') -const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper') +const TxStateManager = require('../../app/scripts/controllers/transactions/tx-state-manager') +const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper') const noop = () => true describe('TransactionStateManager', function () { diff --git a/test/unit/tx-utils-test.js b/test/unit/tx-utils-test.js index 8ca13412e..ae7afd285 100644 --- a/test/unit/tx-utils-test.js +++ b/test/unit/tx-utils-test.js @@ -1,77 +1,98 @@ const assert = require('assert') -const Transaction = require('ethereumjs-tx') -const BN = require('bn.js') - - -const { hexToBn, bnToHex } = require('../../app/scripts/lib/util') -const TxUtils = require('../../app/scripts/lib/tx-gas-utils') +const txUtils = require('../../app/scripts/controllers/transactions/lib/util') describe('txUtils', function () { - let txUtils - - before(function () { - txUtils = new TxUtils(new Proxy({}, { - get: (obj, name) => { - return () => {} - }, - })) - }) - - describe('chain Id', function () { - it('prepares a transaction with the provided chainId', function () { - const txParams = { - to: '0x70ad465e0bab6504002ad58c744ed89c7da38524', - from: '0x69ad465e0bab6504002ad58c744ed89c7da38525', - value: '0x0', - gas: '0x7b0c', - gasPrice: '0x199c82cc00', - data: '0x', - nonce: '0x3', - chainId: 42, + describe('#validateTxParams', function () { + it('does not throw for positive values', function () { + var sample = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + value: '0x01', + } + txUtils.validateTxParams(sample) + }) + + it('returns error for negative values', function () { + var sample = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + value: '-0x01', + } + try { + txUtils.validateTxParams(sample) + } catch (err) { + assert.ok(err, 'error') } - const ethTx = new Transaction(txParams) - assert.equal(ethTx.getChainId(), 42, 'chainId is set from tx params') }) }) - describe('addGasBuffer', function () { - it('multiplies by 1.5, when within block gas limit', function () { - // naive estimatedGas: 0x16e360 (1.5 mil) - const inputHex = '0x16e360' - // dummy gas limit: 0x3d4c52 (4 mil) - const blockGasLimitHex = '0x3d4c52' - const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) - const inputBn = hexToBn(inputHex) - const outputBn = hexToBn(output) - const expectedBn = inputBn.muln(1.5) - assert(outputBn.eq(expectedBn), 'returns 1.5 the input value') + describe('#normalizeTxParams', () => { + it('should normalize txParams', () => { + let txParams = { + chainId: '0x1', + from: 'a7df1beDBF813f57096dF77FCd515f0B3900e402', + to: null, + data: '68656c6c6f20776f726c64', + random: 'hello world', + } + + let normalizedTxParams = txUtils.normalizeTxParams(txParams) + + assert(!normalizedTxParams.chainId, 'their should be no chainId') + assert(!normalizedTxParams.to, 'their should be no to address if null') + assert.equal(normalizedTxParams.from.slice(0, 2), '0x', 'from should be hexPrefixd') + assert.equal(normalizedTxParams.data.slice(0, 2), '0x', 'data should be hexPrefixd') + assert(!('random' in normalizedTxParams), 'their should be no random key in normalizedTxParams') + + txParams.to = 'a7df1beDBF813f57096dF77FCd515f0B3900e402' + normalizedTxParams = txUtils.normalizeTxParams(txParams) + assert.equal(normalizedTxParams.to.slice(0, 2), '0x', 'to should be hexPrefixd') + + }) + }) + + describe('#validateRecipient', () => { + it('removes recipient for txParams with 0x when contract data is provided', function () { + const zeroRecipientandDataTxParams = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + to: '0x', + data: 'bytecode', + } + const sanitizedTxParams = txUtils.validateRecipient(zeroRecipientandDataTxParams) + assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x') }) - it('uses original estimatedGas, when above block gas limit', function () { - // naive estimatedGas: 0x16e360 (1.5 mil) - const inputHex = '0x16e360' - // dummy gas limit: 0x0f4240 (1 mil) - const blockGasLimitHex = '0x0f4240' - const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) - // const inputBn = hexToBn(inputHex) - const outputBn = hexToBn(output) - const expectedBn = hexToBn(inputHex) - assert(outputBn.eq(expectedBn), 'returns the original estimatedGas value') + it('should error when recipient is 0x', function () { + const zeroRecipientTxParams = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + to: '0x', + } + assert.throws(() => { txUtils.validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address') }) + }) - it('buffers up to recommend gas limit recommended ceiling', function () { - // naive estimatedGas: 0x16e360 (1.5 mil) - const inputHex = '0x16e360' - // dummy gas limit: 0x1e8480 (2 mil) - const blockGasLimitHex = '0x1e8480' - const blockGasLimitBn = hexToBn(blockGasLimitHex) - const ceilGasLimitBn = blockGasLimitBn.muln(0.9) - const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex) - // const inputBn = hexToBn(inputHex) - // const outputBn = hexToBn(output) - const expectedHex = bnToHex(ceilGasLimitBn) - assert.equal(output, expectedHex, 'returns the gas limit recommended ceiling value') - }) + + describe('#validateFrom', () => { + it('should error when from is not a hex string', function () { + + // where from is undefined + const txParams = {} + assert.throws(() => { txUtils.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) + + // where from is array + txParams.from = [] + assert.throws(() => { txUtils.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) + + // where from is a object + txParams.from = {} + assert.throws(() => { txUtils.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`) + + // where from is a invalid address + txParams.from = 'im going to fail' + assert.throws(() => { txUtils.validateFrom(txParams) }, Error, `Invalid from address`) + + // should run + txParams.from ='0x1678a085c290ebd122dc42cba69373b5953b831d' + txUtils.validateFrom(txParams) + }) }) }) \ No newline at end of file From 5494aa4f9c34353158dc0c6d07d48abec247ccd8 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Tue, 10 Apr 2018 14:53:40 -0700 Subject: [PATCH 07/79] transactions - lint fixes --- app/scripts/controllers/transactions/lib/util.js | 10 +++++----- app/scripts/controllers/transactions/tx-gas-utils.js | 2 +- .../controllers/transactions/tx-state-manager.js | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js index f403b0758..5d5e63c59 100644 --- a/app/scripts/controllers/transactions/lib/util.js +++ b/app/scripts/controllers/transactions/lib/util.js @@ -7,7 +7,7 @@ module.exports = { normalizeTxParams, validateTxParams, validateFrom, - validateRecipient + validateRecipient, } @@ -48,19 +48,19 @@ function validateTxParams (txParams) { } function validateFrom (txParams) { - if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`) + if (!(typeof txParams.from === 'string')) throw new Error(`Invalid from address ${txParams.from} not a string`) if (!isValidAddress(txParams.from)) throw new Error('Invalid from address') } function validateRecipient (txParams) { - if (txParams.to === '0x' || txParams.to === null ) { + if (txParams.to === '0x' || txParams.to === null) { if (txParams.data) { delete txParams.to } else { throw new Error('Invalid recipient address') } - } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) { + } else if (txParams.to !== undefined && !isValidAddress(txParams.to)) { throw new Error('Invalid recipient address') } return txParams -} \ No newline at end of file +} diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js index f40542603..1a7ff5b54 100644 --- a/app/scripts/controllers/transactions/tx-gas-utils.js +++ b/app/scripts/controllers/transactions/tx-gas-utils.js @@ -100,4 +100,4 @@ module.exports = class TxGasUtil { // otherwise use blockGasLimit return bnToHex(upperGasLimitBn) } -} \ No newline at end of file +} diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index cb24b8c99..f898cc44a 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -92,7 +92,7 @@ module.exports = class TransactionStateManager extends EventEmitter { // or rejected tx's. // not tx's that are pending or unapproved if (txCount > txHistoryLimit - 1) { - let index = transactions.findIndex((metaTx) => { + const index = transactions.findIndex((metaTx) => { return this.getFinalStates().includes(metaTx.status) }) if (index !== -1) { @@ -145,7 +145,7 @@ module.exports = class TransactionStateManager extends EventEmitter { } // validates txParams members by type - validateTxParams(txParams) { + validateTxParams (txParams) { Object.keys(txParams).forEach((key) => { const value = txParams[key] // validate types @@ -263,10 +263,10 @@ module.exports = class TransactionStateManager extends EventEmitter { // returns an array of states that can be considered final getFinalStates () { return [ - 'rejected', // the user has responded no! - 'confirmed', // the tx has been included in a block. - 'failed', // the tx failed for some reason, included on tx data. - 'dropped', // the tx nonce was already used + 'rejected', // the user has responded no! + 'confirmed', // the tx has been included in a block. + 'failed', // the tx failed for some reason, included on tx data. + 'dropped', // the tx nonce was already used ] } From 3aaa28531e1b05a93a68d8016d1648191cdc9696 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 12 Apr 2018 12:24:16 -0700 Subject: [PATCH 08/79] transactions - code cleanup --- app/scripts/controllers/transactions/index.js | 113 +++++++++--------- .../transactions/pending-tx-tracker.js | 4 +- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 6f66e3a1e..ca6c3923e 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -43,39 +43,13 @@ module.exports = class TransactionController extends EventEmitter { getNetwork: this.getNetwork.bind(this), }) - this.txStateManager.getFilteredTxList({ - status: 'unapproved', - loadingDefaults: true, - }).forEach((tx) => { - this.addTxDefaults(tx) - .then((txMeta) => { - txMeta.loadingDefaults = false - this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot') - }).catch((error) => { - this.txStateManager.setTxStatusFailed(tx.id, error) - }) - }) - - this.txStateManager.getFilteredTxList({ - status: 'approved', - }).forEach((txMeta) => { - const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') - this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) - }) - + this._onBootCleanUp() this.store = this.txStateManager.store - this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.nonceTracker = new NonceTracker({ provider: this.provider, getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), - getConfirmedTransactions: (address) => { - return this.txStateManager.getFilteredTxList({ - from: address, - status: 'confirmed', - err: undefined, - }) - }, + getConfirmedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager), }) this.pendingTxTracker = new PendingTransactionTracker({ @@ -87,29 +61,7 @@ module.exports = class TransactionController extends EventEmitter { }) this.txStateManager.store.subscribe(() => this.emit('update:badge')) - - this.pendingTxTracker.on('tx:warning', (txMeta) => { - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning') - }) - this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId)) - this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) - this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { - if (!txMeta.firstRetryBlockNumber) { - txMeta.firstRetryBlockNumber = latestBlockNumber - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update') - } - }) - this.pendingTxTracker.on('tx:retry', (txMeta) => { - if (!('retryCount' in txMeta)) txMeta.retryCount = 0 - txMeta.retryCount++ - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry') - }) - - this.blockTracker.on('block', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker)) - // this is a little messy but until ethstore has been either - // removed or redone this is to guard against the race condition - this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker)) - this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker)) + this._setupListners() // memstore is computed from a few different stores this._updateMemstore() this.txStateManager.store.subscribe(() => this._updateMemstore()) @@ -151,16 +103,16 @@ module.exports = class TransactionController extends EventEmitter { } } - wipeTransactions (address) { - this.txStateManager.wipeTransactions(address) - } - - // Adds a tx to the txlist +// Adds a tx to the txlist addTx (txMeta) { this.txStateManager.addTx(txMeta) this.emit(`${txMeta.id}:unapproved`, txMeta) } + wipeTransactions (address) { + this.txStateManager.wipeTransactions(address) + } + async newUnapprovedTransaction (txParams, opts = {}) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) const initialTxMeta = await this.addUnapprovedTransaction(txParams) @@ -314,6 +266,55 @@ module.exports = class TransactionController extends EventEmitter { // PRIVATE METHODS // + _onBootCleanUp () { + this.txStateManager.getFilteredTxList({ + status: 'unapproved', + loadingDefaults: true, + }).forEach((tx) => { + this.addTxDefaults(tx) + .then((txMeta) => { + txMeta.loadingDefaults = false + this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot') + }).catch((error) => { + this.txStateManager.setTxStatusFailed(tx.id, error) + }) + }) + + this.txStateManager.getFilteredTxList({ + status: 'approved', + }).forEach((txMeta) => { + const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') + this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) + }) + } + + _setupListners () { + this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) + this.pendingTxTracker.on('tx:warning', (txMeta) => { + this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning') + }) + this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId)) + this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) + this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { + if (!txMeta.firstRetryBlockNumber) { + txMeta.firstRetryBlockNumber = latestBlockNumber + this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update') + } + }) + this.pendingTxTracker.on('tx:retry', (txMeta) => { + if (!('retryCount' in txMeta)) txMeta.retryCount = 0 + txMeta.retryCount++ + this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry') + }) + + this.blockTracker.on('block', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker)) + // this is a little messy but until ethstore has been either + // removed or redone this is to guard against the race condition + this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker)) + this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker)) + + } + _markNonceDuplicatesDropped (txId) { this.txStateManager.setTxStatusConfirmed(txId) // get the confirmed transactions nonce and from address diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index e8869e6b8..f2259fb96 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -171,8 +171,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { try { await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta))) } catch (err) { - console.error('PendingTransactionWatcher - Error updating pending transactions') - console.error(err) + log.error('PendingTransactionWatcher - Error updating pending transactions') + log.error(err) } nonceGlobalLock.releaseLock() } From 88f4212363601b2bb3778f4090235a0a0740b4c9 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 13 Apr 2018 12:38:07 -0700 Subject: [PATCH 09/79] meta - transactions - code clean up and jsDoc --- app/scripts/controllers/transactions/index.js | 150 +++++++++++++----- test/unit/tx-controller-test.js | 4 +- 2 files changed, 111 insertions(+), 43 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index ca6c3923e..ca83941fc 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -8,22 +8,37 @@ const TxGasUtil = require('./tx-gas-utils') const PendingTransactionTracker = require('./pending-tx-tracker') const NonceTracker = require('./nonce-tracker') const txUtils = require('./lib/util') -/* + +module.exports = TransactionController + +/** Transaction Controller is an aggregate of sub-controllers and trackers composing them in a way to be exposed to the metamask controller - - txStateManager +
- txStateManager responsible for the state of a transaction and storing the transaction - - pendingTxTracker +
- pendingTxTracker watching blocks for transactions to be include and emitting confirmed events - - txGasUtil +
- txGasUtil gas calculations and safety buffering - - nonceTracker +
- nonceTracker calculating nonces + + +@param {object} opts - + - initState, initial transaction list default is an empty array
+ - networkStore, an observable store for network number
+ - blockTracker,
+ - provider,
+ - signTransaction, function the signs an ethereumjs-tx
+ - getGasPrice, optional gas price calculator
+ - txHistoryLimit, number *optional* for limiting how many transactions are in state
+ - preferencesStore, +@class */ -module.exports = class TransactionController extends EventEmitter { +class TransactionController extends EventEmitter { constructor (opts) { super() this.networkStore = opts.networkStore || new ObservableStore({}) @@ -42,7 +57,7 @@ module.exports = class TransactionController extends EventEmitter { txHistoryLimit: opts.txHistoryLimit, getNetwork: this.getNetwork.bind(this), }) - + this._mapMethods() this._onBootCleanUp() this.store = this.txStateManager.store @@ -68,31 +83,7 @@ module.exports = class TransactionController extends EventEmitter { this.networkStore.subscribe(() => this._updateMemstore()) this.preferencesStore.subscribe(() => this._updateMemstore()) } - - getState () { - return this.memStore.getState() - } - - getNetwork () { - return this.networkStore.getState() - } - - getSelectedAddress () { - return this.preferencesStore.getState().selectedAddress - } - - getUnapprovedTxCount () { - return Object.keys(this.txStateManager.getUnapprovedTxList()).length - } - - getPendingTxCount (account) { - return this.txStateManager.getPendingTransactions(account).length - } - - getFilteredTxList (opts) { - return this.txStateManager.getFilteredTxList(opts) - } - + /** @returns {number} the chainId*/ getChainId () { const networkState = this.networkStore.getState() const getChainId = parseInt(networkState) @@ -103,16 +94,27 @@ module.exports = class TransactionController extends EventEmitter { } } -// Adds a tx to the txlist +/** Adds a tx to the txlist */ addTx (txMeta) { this.txStateManager.addTx(txMeta) this.emit(`${txMeta.id}:unapproved`, txMeta) } +/** + wipes the transactions for a given account + @param address {string} - hex string of the from address for txs being removed +*/ wipeTransactions (address) { this.txStateManager.wipeTransactions(address) } +/** +add a new unapproved transaction to the pipeline +@returns {promise} +@param txParams {object} - txParams for the transaction +@param opts {object} - with the key origin to put the origin on the txMeta + +*/ async newUnapprovedTransaction (txParams, opts = {}) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) const initialTxMeta = await this.addUnapprovedTransaction(txParams) @@ -135,6 +137,13 @@ module.exports = class TransactionController extends EventEmitter { }) } + /** + validates and generates a txMeta with defaults and puts it in txStateManager + store + + @returns {txMeta} + */ + async addUnapprovedTransaction (txParams) { // validate const normalizedTxParams = txUtils.normalizeTxParams(txParams) @@ -145,7 +154,7 @@ module.exports = class TransactionController extends EventEmitter { this.emit('newUnapprovedTx', txMeta) // add default tx params try { - txMeta = await this.addTxDefaults(txMeta) + txMeta = await this.addTxGasDefaults(txMeta) } catch (error) { console.log(error) this.txStateManager.setTxStatusFailed(txMeta.id, error) @@ -157,8 +166,12 @@ module.exports = class TransactionController extends EventEmitter { return txMeta } - - async addTxDefaults (txMeta) { +/** + adds the tx gas defaults: gas && gasPrice + @param txMeta {object} - the txMeta object + @returns {promise} resolves with txMeta +*/ + async addTxGasDefaults (txMeta) { const txParams = txMeta.txParams // ensure value txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) @@ -167,11 +180,18 @@ module.exports = class TransactionController extends EventEmitter { gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice() } txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) - txParams.value = txParams.value || '0x0' // set gasLimit return await this.txGasUtil.analyzeGasUsage(txMeta) } + /** + creates a new txMeta with the same txParams as the original + to allow the user to resign the transaction with a higher gas values + @param originalTxId {number} - the id of the txMeta that + you want to attempt to retry + @return {txMeta} + */ + async retryTransaction (originalTxId) { const originalTxMeta = this.txStateManager.getTx(originalTxId) const lastGasPrice = originalTxMeta.txParams.gasPrice @@ -185,15 +205,31 @@ module.exports = class TransactionController extends EventEmitter { return txMeta } + /** + updates the txMeta in the txStateManager + @param txMeta {object} - the updated txMeta + */ async updateTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction') } + /** + updates and approves the transaction + @param txMeta {object} + */ async updateAndApproveTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') await this.approveTransaction(txMeta.id) } + /** + sets the tx status to approved + auto fills the nonce + signs the transaction + publishes the transaction + if any of these steps fails the tx status will be set to failed + @param txId {number} - the tx's Id + */ async approveTransaction (txId) { let nonceLock try { @@ -225,7 +261,11 @@ module.exports = class TransactionController extends EventEmitter { throw err } } - + /** + adds the chain id and signs the transaction and set the status to signed + @param txId {number} - the tx's Id + @returns - rawTx {string} + */ async signTransaction (txId) { const txMeta = this.txStateManager.getTx(txId) // add network/chain id @@ -241,6 +281,11 @@ module.exports = class TransactionController extends EventEmitter { return rawTx } + /** + publishes the raw tx and sets the txMeta to submitted + @param txId {number} - the tx's Id + @param rawTx {string} - the hex string of the serialized signed transaction + */ async publishTransaction (txId, rawTx) { const txMeta = this.txStateManager.getTx(txId) txMeta.rawTx = rawTx @@ -250,11 +295,19 @@ module.exports = class TransactionController extends EventEmitter { this.txStateManager.setTxStatusSubmitted(txId) } + /** + convenience method for the ui thats sets the transaction to rejected + @param txId {number} - the tx's Id + */ async cancelTransaction (txId) { this.txStateManager.setTxStatusRejected(txId) } - // receives a txHash records the tx as signed + /** + sets the txHas on the txMeta + @param txId {number} - the tx's Id + @param txHash {string} - the hash for the txMeta + */ setTxHash (txId, txHash) { // Add the tx hash to the persisted meta-tx object const txMeta = this.txStateManager.getTx(txId) @@ -265,13 +318,28 @@ module.exports = class TransactionController extends EventEmitter { // // PRIVATE METHODS // + /** maps methods for convenience*/ + _mapMethods () { + /** Returns the state in transaction controller */ + this.getState = () => this.memStore.getState() + /** Returns the network number stored in networkStore */ + this.getNetwork = () => this.networkStore.getState() + /** Returns the user selected address */ + this.getSelectedAddress = () => this.preferencesStore.getState().selectedAddress + /** Returns an array of transactions whos status is unapproved */ + this.getUnapprovedTxCount = () => Object.keys(this.txStateManager.getUnapprovedTxList()).length + /** Returns a number that represents how many transactions have the status submitted*/ + this.getPendingTxCount = (account) => this.txStateManager.getPendingTransactions(account).length + /** see txStateManager */ + this.getFilteredTxList = (opts) => this.txStateManager.getFilteredTxList(opts) + } _onBootCleanUp () { this.txStateManager.getFilteredTxList({ status: 'unapproved', loadingDefaults: true, }).forEach((tx) => { - this.addTxDefaults(tx) + this.addTxGasDefaults(tx) .then((txMeta) => { txMeta.loadingDefaults = false this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot') @@ -339,4 +407,4 @@ module.exports = class TransactionController extends EventEmitter { }) this.memStore.updateState({ unapprovedTxs, selectedAddressTxList }) } -} +} \ No newline at end of file diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 08f16d83b..20d6f8573 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -188,7 +188,7 @@ describe('Transaction Controller', function () { }) - describe('#addTxDefaults', function () { + describe('#addTxGasDefaults', function () { it('should add the tx defaults if their are none', function (done) { const txMeta = { 'txParams': { @@ -199,7 +199,7 @@ describe('Transaction Controller', function () { providerResultStub.eth_gasPrice = '4a817c800' providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' } providerResultStub.eth_estimateGas = '5209' - txController.addTxDefaults(txMeta) + txController.addTxGasDefaults(txMeta) .then((txMetaWithDefaults) => { assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value') assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price') From 943eea043cc40ea42ffe757a7115ccbc5585b37b Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 13 Apr 2018 13:18:45 -0700 Subject: [PATCH 10/79] fix up - more docs --- app/scripts/controllers/transactions/index.js | 11 ++- .../controllers/transactions/lib/util.js | 32 ++++----- .../transactions/pending-tx-tracker.js | 72 ++++++++++++++----- 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index ca83941fc..c81251cd2 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -9,8 +9,6 @@ const PendingTransactionTracker = require('./pending-tx-tracker') const NonceTracker = require('./nonce-tracker') const txUtils = require('./lib/util') -module.exports = TransactionController - /** Transaction Controller is an aggregate of sub-controllers and trackers composing them in a way to be exposed to the metamask controller @@ -356,6 +354,11 @@ add a new unapproved transaction to the pipeline }) } + /** + is called in constructor applies the listeners for pendingTxTracker txStateManager + and blockTracker +
+ */ _setupListners () { this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.pendingTxTracker.on('tx:warning', (txMeta) => { @@ -407,4 +410,6 @@ add a new unapproved transaction to the pipeline }) this.memStore.updateState({ unapprovedTxs, selectedAddressTxList }) } -} \ No newline at end of file +} + +module.exports = TransactionController \ No newline at end of file diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js index 5d5e63c59..b18283997 100644 --- a/app/scripts/controllers/transactions/lib/util.js +++ b/app/scripts/controllers/transactions/lib/util.js @@ -11,24 +11,24 @@ module.exports = { } +// functions that handle normalizing of that key in txParams +const normalizers = { + from: from => addHexPrefix(from).toLowerCase(), + to: to => addHexPrefix(to).toLowerCase(), + nonce: nonce => addHexPrefix(nonce), + value: value => value ? addHexPrefix(value) : '0x0', + data: data => addHexPrefix(data), + gas: gas => addHexPrefix(gas), + gasPrice: gasPrice => addHexPrefix(gasPrice), +} + /** + */ function normalizeTxParams (txParams) { - // functions that handle normalizing of that key in txParams - const whiteList = { - from: from => addHexPrefix(from).toLowerCase(), - to: to => addHexPrefix(txParams.to).toLowerCase(), - nonce: nonce => addHexPrefix(nonce), - value: value => addHexPrefix(value), - data: data => addHexPrefix(data), - gas: gas => addHexPrefix(gas), - gasPrice: gasPrice => addHexPrefix(gasPrice), - } - - // apply only keys in the whiteList + // apply only keys in the normalizers const normalizedTxParams = {} - Object.keys(whiteList).forEach((key) => { - if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) - }) - + for (let key in normalizers) { + if (txParams[key]) normalizedTxParams[key] = normalizers[key](txParams[key]) + } return normalizedTxParams } diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index f2259fb96..503343e22 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -1,23 +1,23 @@ const EventEmitter = require('events') const EthQuery = require('ethjs-query') -/* - - Utility class for tracking the transactions as they - go from a pending state to a confirmed (mined in a block) state +/** + Event emitter utility class for tracking the transactions as they
+ go from a pending state to a confirmed (mined in a block) state
+
As well as continues broadcast while in the pending state +
+@param config {object} - non optional configuration object consists of: +
provider +
nonceTracker: see nonce tracker +
getPendingTransactions: a function for getting an array of transactions, +
publishTransaction: a async function for publishing raw transactions, - ~config is not optional~ - requires a: { - provider: //, - nonceTracker: //see nonce tracker, - getPendingTransactions: //() a function for getting an array of transactions, - publishTransaction: //(rawTx) a async function for publishing raw transactions, - } +@class */ -module.exports = class PendingTransactionTracker extends EventEmitter { +class PendingTransactionTracker extends EventEmitter { constructor (config) { super() this.query = new EthQuery(config.provider) @@ -29,8 +29,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this._checkPendingTxs() } - // checks if a signed tx is in a block and - // if included sets the tx status as 'confirmed' + /** + checks if a signed tx is in a block and + if it is included emits tx status as 'confirmed' + @param block {object}, a full block + @emits tx:confirmed + @emits tx:failed + */ checkForTxInBlock (block) { const signedTxList = this.getPendingTransactions() if (!signedTxList.length) return @@ -52,6 +57,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter { }) } + /** + asks the network for the transaction to see if a block number is included on it + if we have skipped/missed blocks + @param object - oldBlock newBlock + */ queryPendingTxs ({ oldBlock, newBlock }) { // check pending transactions on start if (!oldBlock) { @@ -63,7 +73,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter { if (diff > 1) this._checkPendingTxs() } - + /** + Will resubmit any transactions who have not been confirmed in a block + @param block {object} - a block object + @emits tx:warning + */ resubmitPendingTxs (block) { const pending = this.getPendingTransactions() // only try resubmitting if their are transactions to resubmit @@ -100,6 +114,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter { })) } + /** + resubmits the individual txMeta used in resubmitPendingTxs + @param txMeta {object} - txMeta object + @param latestBlockNumber {string} - hex string for the latest block number + @emits tx:retry + @returns txHash {string} + */ async _resubmitTx (txMeta, latestBlockNumber) { if (!txMeta.firstRetryBlockNumber) { this.emit('tx:block-update', txMeta, latestBlockNumber) @@ -123,7 +144,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this.emit('tx:retry', txMeta) return txHash } - + /** + Ask the network for the transaction to see if it has been include in a block + @param txMeta {object} - the txMeta object + @emits tx:failed + @emits tx:confirmed + @emits tx:warning + */ async _checkPendingTx (txMeta) { const txHash = txMeta.hash const txId = txMeta.id @@ -162,8 +189,9 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } } - // checks the network for signed txs and - // if confirmed sets the tx status as 'confirmed' + /** + checks the network for signed txs and releases the nonce global lock if it is + */ async _checkPendingTxs () { const signedTxList = this.getPendingTransactions() // in order to keep the nonceTracker accurate we block it while updating pending transactions @@ -177,6 +205,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter { nonceGlobalLock.releaseLock() } + /** + checks to see if a confirmed txMeta has the same nonce + @param txMeta {object} - txMeta object + @returns {boolean} + */ async _checkIfNonceIsTaken (txMeta) { const address = txMeta.txParams.from const completed = this.getCompletedTransactions(address) @@ -185,5 +218,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter { }) return sameNonce.length > 0 } - } + +module.exports = PendingTransactionTracker \ No newline at end of file From 49e02baea0b62ef13a585b678480709b34b808f7 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 18 Apr 2018 22:09:40 +0000 Subject: [PATCH 11/79] fix(package): update ethjs-contract to version 0.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c5b05547..7004f67cd 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "ethereumjs-wallet": "^0.6.0", "etherscan-link": "^1.0.2", "ethjs": "^0.3.4", - "ethjs-contract": "^0.1.9", + "ethjs-contract": "^0.2.0", "ethjs-ens": "^2.0.0", "ethjs-query": "^0.3.4", "express": "^4.15.5", From ce74cd7774dbf2ecc7f9c9aa98f61fcd08fb6d39 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 18 Apr 2018 17:34:28 -0700 Subject: [PATCH 12/79] Fix custom RPC not redirecting to Settings page --- ui/app/components/dropdowns/network-dropdown.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index 9e47f38ef..dcd6b4370 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -3,12 +3,14 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect +const { withRouter } = require('react-router-dom') +const { compose } = require('recompose') const actions = require('../../actions') const Dropdown = require('./components/dropdown').Dropdown const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem const NetworkDropdownIcon = require('./components/network-dropdown-icon') const R = require('ramda') - +const { SETTINGS_ROUTE } = require('../../routes') // classes from nodes of the toggle element. const notToggleElementClassnames = [ @@ -41,9 +43,6 @@ function mapDispatchToProps (dispatch) { setRpcTarget: (target) => { dispatch(actions.setRpcTarget(target)) }, - showConfigPage: () => { - dispatch(actions.showConfigPage()) - }, showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()), hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()), } @@ -59,7 +58,10 @@ NetworkDropdown.contextTypes = { t: PropTypes.func, } -module.exports = connect(mapStateToProps, mapDispatchToProps)(NetworkDropdown) +module.exports = compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(NetworkDropdown) // TODO: specify default props and proptypes @@ -227,7 +229,7 @@ NetworkDropdown.prototype.render = function () { DropdownMenuItem, { closeMenu: () => this.props.hideNetworkDropdown(), - onClick: () => this.props.showConfigPage(), + onClick: () => this.props.history.push(SETTINGS_ROUTE), style: dropdownMenuItemStyle, }, [ From f78d2f5e0b633ac33da8c0a9e98ca36d8814e155 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Apr 2018 17:54:50 -0700 Subject: [PATCH 13/79] Added docs to MM-controller --- app/scripts/metamask-controller.js | 533 ++++++++++++++++++++--------- 1 file changed, 362 insertions(+), 171 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 782bc50ac..7847b6048 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -263,6 +263,7 @@ module.exports = class MetamaskController extends EventEmitter { /** * Constructor helper: initialize a public config store. + * This store is used to make some config info available to Dapps synchronously. */ initPublicConfigStore () { // get init state @@ -314,7 +315,8 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * Returns an api-object which is consumed by the UI + * Returns an api-object which is consumed by the UI. + * The API object is composed of callback functions. * * @returns {Object} */ @@ -407,14 +409,16 @@ module.exports = class MetamaskController extends EventEmitter { //============================================================================= /** - * Creates a new Vault(?) and create a new keychain(?) + * Creates a new Vault and create a new keychain. * - * A vault is ... + * A vault, or KeyringController, is a controller that contains + * many different account strategies, currently called Keyrings. + * Creating it new means wiping all previous keyrings. * - * A keychain is ... + * A keychain, or keyring, controls many accounts with a single backup and signing strategy. + * For example, a mnemonic phrase can generate many accounts, and is a keyring. * - * - * @param {} password + * @param {string} password * * @returns {} vault */ @@ -442,7 +446,7 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * Create a new Vault and restore an existent keychain + * Create a new Vault and restore an existent keyring. * @param {} password * @param {} seed */ @@ -459,11 +463,17 @@ module.exports = class MetamaskController extends EventEmitter { } } + /** + * @type Identity + * @property {string} name - The account nickname. + * @property {string} address - The account's ethereum address, in lower case. + * @property {boolean} mayBeFauceting - Whether this account is currently + * receiving funds from our automatic Ropsten faucet. + */ + /** * Retrieves the first Identiy from the passed Vault and selects the related address * - * An Identity is ... - * * @param {} vault */ selectFirstIdentity (vault) { @@ -472,12 +482,12 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(address) } - // ? - // Opinionated Keyring Management + // + // Account Management // /** - * Adds a new account to ... + * Adds a new account to the default (first) HD seed phrase Keyring. * * @returns {} keyState */ @@ -507,6 +517,8 @@ module.exports = class MetamaskController extends EventEmitter { * * Used when creating a first vault, to allow confirmation. * Also used when revealing the seed words in the confirmation view. + * + * @param {Function} cb - A callback called on completion. */ placeSeedWords (cb) { @@ -526,6 +538,8 @@ module.exports = class MetamaskController extends EventEmitter { * Validity: seed phrase restores the accounts belonging to the current vault. * * Called when the first account is created and on unlocking the vault. + * + * @returns {Promise} Seed phrase to be confirmed by the user. */ async verifySeedPhrase () { @@ -556,6 +570,7 @@ module.exports = class MetamaskController extends EventEmitter { * * The seed phrase remains available in the background process. * + * @param {function} cb Callback function called with the current address. */ clearSeedWordCache (cb) { this.configManager.setSeedWords(null) @@ -563,9 +578,13 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * ? + * Clears the transaction history, to allow users to force-reset their nonces. + * Mostly used in development environments, when networks are restarted with + * the same network ID. + * + * @returns Promise The current selected address. */ - async resetAccount (cb) { + async resetAccount () { const selectedAddress = this.preferencesController.getSelectedAddress() this.txController.wipeTransactions(selectedAddress) @@ -577,11 +596,13 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * Imports an account ... ? + * Imports an account with the specified import strategy. + * These are defined in app/scripts/account-import-strategies + * Each strategy represents a different way of serializing an Ethereum key pair. * - * @param {} strategy - * @param {} args - * @param {} cb + * @param {string} strategy - A unique identifier for a + * @param {any} args - The data required by that strategy to import an account. + * @param {Function} cb - A callback function called with a state update on success. */ importAccountWithStrategy (strategy, args, cb) { accountImporter.importAccount(strategy, args) @@ -595,13 +616,42 @@ module.exports = class MetamaskController extends EventEmitter { } // --------------------------------------------------------------------------- - // Identity Management (sign) + // Identity Management (signature operations) + + // eth_sign methods: /** - * @param {} msgParams - * @param {} cb + * Called when a Dapp uses the eth_sign method, to request user approval. + * eth_sign is a pure signature of arbitrary data. It is on a deprecation + * path, since this data can be a transaction, or can leak private key + * information. + * + * @param {Object} msgParams - The params passed to eth_sign. + * @param {Function} cb = The callback function called with the signature. */ - signMessage (msgParams, cb) { + newUnsignedMessage (msgParams, cb) { + const msgId = this.messageManager.addUnapprovedMessage(msgParams) + this.sendUpdate() + this.opts.showUnconfirmedMessage() + this.messageManager.once(`${msgId}:finished`, (data) => { + switch (data.status) { + case 'signed': + return cb(null, data.rawSig) + case 'rejected': + return cb(new Error('MetaMask Message Signature: User denied message signature.')) + default: + return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`)) + } + }) + } + + /** + * Signifies user intent to complete an eth_sign method. + * + * @param {Object} msgParams The params passed to eth_call. + * @returns Promise Full state update. + */ + signMessage (msgParams) { log.info('MetaMaskController - signMessage') const msgId = msgParams.metamaskId @@ -620,148 +670,32 @@ module.exports = class MetamaskController extends EventEmitter { }) } - // Prefixed Style Message Signing Methods: - - /** + /* + * Used to cancel a message submitted via eth_sign. * - * @param {} msgParams - * @param {} cb + * @param {String} msgId - The id of the message to cancel. */ - approvePersonalMessage (msgParams, cb) { - const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams) - this.sendUpdate() - this.opts.showUnconfirmedMessage() - this.personalMessageManager.once(`${msgId}:finished`, (data) => { - switch (data.status) { - case 'signed': - return cb(null, data.rawSig) - case 'rejected': - return cb(new Error('MetaMask Message Signature: User denied transaction signature.')) - default: - return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`)) - } - }) - } - - /** - * @param {} msgParams - */ - signPersonalMessage (msgParams) { - log.info('MetaMaskController - signPersonalMessage') - const msgId = msgParams.metamaskId - // sets the status op the message to 'approved' - // and removes the metamaskId for signing - return this.personalMessageManager.approveMessage(msgParams) - .then((cleanMsgParams) => { - // signs the message - return this.keyringController.signPersonalMessage(cleanMsgParams) - }) - .then((rawSig) => { - // tells the listener that the message has been signed - // and can be returned to the dapp - this.personalMessageManager.setMsgStatusSigned(msgId, rawSig) - return this.getState() - }) - } - - /** - * @param {} msgParams - */ - signTypedMessage (msgParams) { - log.info('MetaMaskController - signTypedMessage') - const msgId = msgParams.metamaskId - // sets the status op the message to 'approved' - // and removes the metamaskId for signing - return this.typedMessageManager.approveMessage(msgParams) - .then((cleanMsgParams) => { - // signs the message - return this.keyringController.signTypedMessage(cleanMsgParams) - }) - .then((rawSig) => { - // tells the listener that the message has been signed - // and can be returned to the dapp - this.typedMessageManager.setMsgStatusSigned(msgId, rawSig) - return this.getState() - }) - } - - // --------------------------------------------------------------------------- - // Account Restauration - - /** - * ? - * - * @param {} migratorOutput - */ - restoreOldVaultAccounts (migratorOutput) { - const { serialized } = migratorOutput - return this.keyringController.restoreKeyring(serialized) - .then(() => migratorOutput) - } - - /** - * ? - * - * @param {} migratorOutput - */ - restoreOldLostAccounts (migratorOutput) { - const { lostAccounts } = migratorOutput - if (lostAccounts) { - this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) - return this.importLostAccounts(migratorOutput) + cancelMessage (msgId, cb) { + const messageManager = this.messageManager + messageManager.rejectMsg(msgId) + if (cb && typeof cb === 'function') { + cb(null, this.getState()) } - return Promise.resolve(migratorOutput) } - /** - * Import (lost) Accounts + // personal_sign methods: + + /* + * Called when a dapp uses the personal_sign method. + * This is identical to the Geth eth_sign method, and may eventually replace + * eth_sign. * - * @param {Object} {lostAccounts} @Array accounts <{ address, privateKey }> + * We currently define our eth_sign and personal_sign mostly for legacy Dapps. * - * Uses the array's private keys to create a new Simple Key Pair keychain - * and add it to the keyring controller. + * @param {Object} msgParams - The params of the message to sign & return to the Dapp. + * @param {Function} cb - The callback function called with the signature. + * Passed back to the requesting Dapp. */ - importLostAccounts ({ lostAccounts }) { - const privKeys = lostAccounts.map(acct => acct.privateKey) - return this.keyringController.restoreKeyring({ - type: 'Simple Key Pair', - data: privKeys, - }) - } - -//============================================================================= -// END (VAULT / KEYRING RELATED METHODS) -//============================================================================= - -// - -//============================================================================= -// MESSAGES -//============================================================================= - - async retryTransaction (txId, cb) { - await this.txController.retryTransaction(txId) - const state = await this.getState() - return state - } - - - newUnsignedMessage (msgParams, cb) { - const msgId = this.messageManager.addUnapprovedMessage(msgParams) - this.sendUpdate() - this.opts.showUnconfirmedMessage() - this.messageManager.once(`${msgId}:finished`, (data) => { - switch (data.status) { - case 'signed': - return cb(null, data.rawSig) - case 'rejected': - return cb(new Error('MetaMask Message Signature: User denied message signature.')) - default: - return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`)) - } - }) - } - newUnsignedPersonalMessage (msgParams, cb) { if (!msgParams.from) { return cb(new Error('MetaMask Message Signature: from field is required.')) @@ -782,6 +716,52 @@ module.exports = class MetamaskController extends EventEmitter { }) } + /** + * Signifies a user's approval to sign a personal_sign message in queue. + * Triggers signing, and the callback function from newUnsignedPersonalMessage. + * + * @param {Object} msgParams - The params of the message to sign & return to the Dapp. + * @returns {Promise { + // signs the message + return this.keyringController.signPersonalMessage(cleanMsgParams) + }) + .then((rawSig) => { + // tells the listener that the message has been signed + // and can be returned to the dapp + this.personalMessageManager.setMsgStatusSigned(msgId, rawSig) + return this.getState() + }) + } + + /* + * Used to cancel a personal_sign type message. + * @param {String} msgId - The ID of the message to cancel. + * @param {Function} cb - The callback function called with a full state update. + */ + cancelPersonalMessage (msgId, cb) { + const messageManager = this.personalMessageManager + messageManager.rejectMsg(msgId) + if (cb && typeof cb === 'function') { + cb(null, this.getState()) + } + } + + // eth_signTypedData methods + + /* + * Called when a dapp uses the eth_signTypedData method, per EIP 712. + * + * @param {Object} msgParams - The params passed to eth_signTypedData. + * @param {Function} cb - The callback function, called with the signature. + */ newUnsignedTypedMessage (msgParams, cb) { let msgId try { @@ -804,22 +784,36 @@ module.exports = class MetamaskController extends EventEmitter { }) } - cancelMessage (msgId, cb) { - const messageManager = this.messageManager - messageManager.rejectMsg(msgId) - if (cb && typeof cb === 'function') { - cb(null, this.getState()) - } - } - - cancelPersonalMessage (msgId, cb) { - const messageManager = this.personalMessageManager - messageManager.rejectMsg(msgId) - if (cb && typeof cb === 'function') { - cb(null, this.getState()) - } + /** + * The method for a user approving a call to eth_signTypedData, per EIP 712. + * Triggers the callback in newUnsignedTypedMessage. + * + * @param {Object} msgParams - The params passed to eth_signTypedData. + * @returns {Object} Full state update. + */ + signTypedMessage (msgParams) { + log.info('MetaMaskController - signTypedMessage') + const msgId = msgParams.metamaskId + // sets the status op the message to 'approved' + // and removes the metamaskId for signing + return this.typedMessageManager.approveMessage(msgParams) + .then((cleanMsgParams) => { + // signs the message + return this.keyringController.signTypedMessage(cleanMsgParams) + }) + .then((rawSig) => { + // tells the listener that the message has been signed + // and can be returned to the dapp + this.typedMessageManager.setMsgStatusSigned(msgId, rawSig) + return this.getState() + }) } + /* + * Used to cancel a eth_signTypedData type message. + * @param {String} msgId - The ID of the message to cancel. + * @param {Function} cb - The callback function called with a full state update. + */ cancelTypedMessage (msgId, cb) { const messageManager = this.typedMessageManager messageManager.rejectMsg(msgId) @@ -828,18 +822,112 @@ module.exports = class MetamaskController extends EventEmitter { } } + // --------------------------------------------------------------------------- + // MetaMask Version 3 Migration Account Restauration Methods + + /** + * A legacy method (probably dead code) that was used when we swapped out our + * key management library that we depended on. + * + * Described in: + * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd + * + * @deprecated + * @param {} migratorOutput + */ + restoreOldVaultAccounts (migratorOutput) { + const { serialized } = migratorOutput + return this.keyringController.restoreKeyring(serialized) + .then(() => migratorOutput) + } + + /* + * A legacy method used to record user confirmation that they understand + * that some of their accounts have been recovered but should be backed up. + * + * @deprecated + * @param {Function} cb - A callback function called with a full state update. + */ markAccountsFound (cb) { this.configManager.setLostAccounts([]) this.sendUpdate() cb(null, this.getState()) } + /** + * A legacy method (probably dead code) that was used when we swapped out our + * key management library that we depended on. + * + * Described in: + * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd + * + * @deprecated + * @param {} migratorOutput + */ + restoreOldLostAccounts (migratorOutput) { + const { lostAccounts } = migratorOutput + if (lostAccounts) { + this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) + return this.importLostAccounts(migratorOutput) + } + return Promise.resolve(migratorOutput) + } + + /** + * Probably no longer needed, related to the Version 3 migration. + * Imports a hash of accounts to private keys into the vault. + * + * Described in: + * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd + * + * Uses the array's private keys to create a new Simple Key Pair keychain + * and add it to the keyring controller. + * @deprecated + * @param {Object} {lostAccounts} @Array accounts <{ address, privateKey }> + */ + importLostAccounts ({ lostAccounts }) { + const privKeys = lostAccounts.map(acct => acct.privateKey) + return this.keyringController.restoreKeyring({ + type: 'Simple Key Pair', + data: privKeys, + }) + } + +//============================================================================= +// END (VAULT / KEYRING RELATED METHODS) +//============================================================================= + + /* + * Allows a user to try to speed up a transaction by retrying it + * with higher gas. + * + * @param {String} txId - The ID of the transaction to speed up. + * @param {Function} cb - The callback function called with a full state update. + */ + async retryTransaction (txId, cb) { + await this.txController.retryTransaction(txId) + const state = await this.getState() + return state + } + +//============================================================================= +// PASSWORD MANAGEMENT +//============================================================================= + + /* + * Allows a user to begin the seed phrase recovery process. + * @param {Function} cb - A callback function called when complete. + */ markPasswordForgotten(cb) { this.configManager.setPasswordForgotten(true) this.sendUpdate() cb() } + /* + * Allows a user to end the seed phrase recovery process. + * @param {Function} cb - A callback function called when complete. + */ unMarkPasswordForgotten(cb) { this.configManager.setPasswordForgotten(false) this.sendUpdate() @@ -850,6 +938,13 @@ module.exports = class MetamaskController extends EventEmitter { // SETUP //============================================================================= + /* + * Used to create a multiplexed stream for connecting to an untrusted context + * like a Dapp or other extension. + * @param {*} connectionStream - The Duplex stream to connect to. + * @param {String} originDomain - The domain requesting the stream, which + * may trigger a blacklist reload. + */ setupUntrustedCommunication (connectionStream, originDomain) { // Check if new connection is blacklisted if (this.blacklistController.checkForPhishing(originDomain)) { @@ -865,6 +960,16 @@ module.exports = class MetamaskController extends EventEmitter { this.setupPublicConfig(mux.createStream('publicConfig')) } + /* + * Used to create a multiplexed stream for connecting to a trusted context, + * like our own user interfaces, which have the provider APIs, but also + * receive the exported API from this controller, which includes trusted + * functions, like the ability to approve transactions or sign messages. + * + * @param {*} connectionStream - The duplex stream to connect to. + * @param {String} originDomain - The domain requesting the connection, + * used in logging and error reporting. + */ setupTrustedCommunication (connectionStream, originDomain) { // setup multiplexing const mux = setupMultiplex(connectionStream) @@ -873,12 +978,25 @@ module.exports = class MetamaskController extends EventEmitter { this.setupProviderConnection(mux.createStream('provider'), originDomain) } + /* + * Called when we detect a suspicious domain. Requests the browser redirects + * to our anti-phishing page. + * + * @private + * @param {*} connectionStream - The duplex stream to the per-page script, + * for sending the reload attempt to. + * @param {String} hostname - The URL that triggered the suspicion. + */ sendPhishingWarning (connectionStream, hostname) { const mux = setupMultiplex(connectionStream) const phishingStream = mux.createStream('phishing') phishingStream.write({ hostname }) } + /* + * A method for providing our API over a stream using Dnode. + * @param {*} outStream - The stream to provide our API over. + */ setupControllerConnection (outStream) { const api = this.getApi() const dnode = Dnode(api) @@ -897,6 +1015,11 @@ module.exports = class MetamaskController extends EventEmitter { }) } + /* + * A method for serving our ethereum provider over a given stream. + * @param {*} outStream - The stream to provide over. + * @param {String} origin - The URI of the requesting resource. + */ setupProviderConnection (outStream, origin) { // setup json rpc engine stack const engine = new RpcEngine() @@ -926,6 +1049,16 @@ module.exports = class MetamaskController extends EventEmitter { ) } + /* + * A method for providing our public config info over a stream. + * This includes info we like to be synchronous if possible, like + * the current selected account, and network ID. + * + * Since synchronous methods have been deprecated in web3, + * this is a good candidate for deprecation. + * + * @param {*} outStream - The stream to provide public config over. + */ setupPublicConfig (outStream) { pump( asStream(this.publicConfigStore), @@ -936,10 +1069,21 @@ module.exports = class MetamaskController extends EventEmitter { ) } + /* + * A method for emitting the full MetaMask state to all registered listeners. + * @private + */ privateSendUpdate () { this.emit('update', this.getState()) } + /* + * A method for estimating a good gas price at recent prices. + * Returns the lowest price that would have been included in + * 50% of recent blocks. + * + * @returns {String} A hex representation of the suggested wei gas price. + */ getGasPrice () { const { recentBlocksController } = this const { recentBlocks } = recentBlocksController.store.getState() @@ -973,6 +1117,11 @@ module.exports = class MetamaskController extends EventEmitter { // Log blocks + /* + * A method for setting the user's preferred display currency. + * @param {String} currencyCode - The code of the preferred currency. + * @param {Function} cb - A callback function returning currency info. + */ setCurrentCurrency (currencyCode, cb) { try { this.currencyController.setCurrentCurrency(currencyCode) @@ -988,6 +1137,13 @@ module.exports = class MetamaskController extends EventEmitter { } } + /* + * A method for forwarding the user to the easiest way to obtain ether, + * or the network "gas" currency, for the current selected network. + * + * @param {String} address - The address to fund. + * @param {String} amount - The amount of ether desired, as a base 10 string. + */ buyEth (address, amount) { if (!amount) amount = '5' const network = this.networkController.getNetworkState() @@ -995,18 +1151,33 @@ module.exports = class MetamaskController extends EventEmitter { if (url) this.platform.openWindow({ url }) } + /* + * A method for triggering a shapeshift currency transfer. + * @param {String} depositAddress - The address to deposit to. + * @property {string} depositType - An abbreviation of the type of crypto currency to be deposited. + */ createShapeShiftTx (depositAddress, depositType) { this.shapeshiftController.createShapeShiftTx(depositAddress, depositType) } // network - async setCustomRpc (rpcTarget, rpcList) { + /* + * A method for selecting a custom URL for an ethereum RPC provider. + * @param {String} rpcTarget - A URL for a valid Ethereum RPC API. + * @returns {Promise} - The RPC Target URL confirmed. + */ + async setCustomRpc (rpcTarget) { this.networkController.setRpcTarget(rpcTarget) await this.preferencesController.updateFrequentRpcList(rpcTarget) return rpcTarget } + /* + * Sets whether or not to use the blockie identicon format. + * @param {Boolean} val - True for bockie, false for jazzicon. + * @param {Function} cb - A callback function called when complete. + */ setUseBlockie (val, cb) { try { this.preferencesController.setUseBlockie(val) @@ -1016,6 +1187,11 @@ module.exports = class MetamaskController extends EventEmitter { } } + /* + * A method for setting a user's current locale, affecting the language rendered. + * @param {String} key - Locale identifier. + * @param {Function} cb - A callback function called when complete. + */ setCurrentLocale (key, cb) { try { this.preferencesController.setCurrentLocale(key) @@ -1025,6 +1201,11 @@ module.exports = class MetamaskController extends EventEmitter { } } + /* + * A method for initializing storage the first time. + * @param {Object} initState - The default state to initialize with. + * @private + */ recordFirstTimeInfo (initState) { if (!('firstTimeInfo' in initState)) { initState.firstTimeInfo = { @@ -1034,11 +1215,21 @@ module.exports = class MetamaskController extends EventEmitter { } } + /* + * A method for recording whether the MetaMask user interface is open or not. + * @private + * @param {Boolean} open + */ set isClientOpen (open) { this._isClientOpen = open this.isClientOpenAndUnlocked = this.getState().isUnlocked && open } + /* + * A method for activating the retrieval of price data, which should only be fetched when the UI is visible. + * @private + * @param {Boolean} active - True if price data should be getting fetched. + */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active } From 9f12c26d44a0d78f28af25056857b993f80bbd95 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 19 Apr 2018 13:08:56 -0230 Subject: [PATCH 14/79] Even more documentation for various controllers and libs. --- app/scripts/controllers/balance.js | 56 +++++++ app/scripts/controllers/blacklist.js | 54 ++++++- app/scripts/controllers/preferences.js | 2 +- app/scripts/controllers/recent-blocks.js | 68 ++++++++ app/scripts/lib/account-tracker.js | 81 +++++++++- app/scripts/lib/extractEthjsErrorMessage.js | 23 +-- app/scripts/lib/getObjStructure.js | 17 ++ app/scripts/lib/message-manager.js | 145 +++++++++++++++++- app/scripts/lib/notification-manager.js | 9 +- app/scripts/lib/pending-balance-calculator.js | 38 ++++- app/scripts/lib/personal-message-manager.js | 142 ++++++++++++++++- app/scripts/lib/seed-phrase-verifier.js | 18 ++- app/scripts/lib/typed-message-manager.js | 138 +++++++++++++++++ 13 files changed, 742 insertions(+), 49 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index f83f294cc..55811185a 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -4,6 +4,24 @@ const BN = require('ethereumjs-util').BN class BalanceController { + /** + * Controller responsible for storing and updating an account's balance. + * + * @typedef {Object} BalanceController + * @param {Object} opts Initialize various properties of the class. + * @property {string} address A base 16 hex string. The account address which has the balance managed by this + * BalanceController. + * @property {AccountTracker} accountTracker Contains and updates the users accounts; is the source of the account + * for which this BalanceController manages balance. + * @property {TransactionController} txController Stores, tracks and manages transactions. Here used to create a listener for + * transaction updates. + * @property {BlockTracker} blockTracker Tracks updates to blocks. On new blocks, this BalanceController updates its balance + * @property {Object} store The store for the ethBalance + * @property {string} store.ethBalance A base 16 hex string. The balance for the current account. + * @property {PendingBalanceCalculator} balanceCalc Used to calculate the accounts balance with possible pending + * transaction costs taken into account. + * + */ constructor (opts = {}) { this._validateParams(opts) const { address, accountTracker, txController, blockTracker } = opts @@ -26,6 +44,11 @@ class BalanceController { this._registerUpdates() } + /** + * Updates the ethBalance property to the current pending balance + * + * @returns {Promise} Promises undefined + */ async updateBalance () { const balance = await this.balanceCalc.getBalance() this.store.updateState({ @@ -33,6 +56,15 @@ class BalanceController { }) } + /** + * Sets up listeners and subscriptions which should trigger an update of ethBalance. These updates include: + * - when a transaction changes to a submitted, confirmed or failed state + * - when the current account changes (i.e. a new account is selected) + * - when there is a block update + * + * @private + * + */ _registerUpdates () { const update = this.updateBalance.bind(this) @@ -51,6 +83,14 @@ class BalanceController { this.blockTracker.on('block', update) } + /** + * Gets the balance, as a base 16 hex string, of the account at this BalanceController's current address. + * If the current account has no balance, returns undefined. + * + * @returns {Promise} Promises a BN with a value equal to the balance of the current account, or undefined + * if the current account has no balance + * + */ async _getBalance () { const { accounts } = this.accountTracker.store.getState() const entry = accounts[this.address] @@ -58,6 +98,14 @@ class BalanceController { return balance ? new BN(balance.substring(2), 16) : undefined } + /** + * Gets the pending transactions (i.e. those with a 'submitted' status). These are accessed from the + * TransactionController passed to this BalanceController during construction. + * + * @private + * @returns {Promise} Promises an array of transaction objects. + * + */ async _getPendingTransactions () { const pending = this.txController.getFilteredTxList({ from: this.address, @@ -67,6 +115,14 @@ class BalanceController { return pending } + /** + * Validates that the passed options have all required properties. + * + * @param {Object} opts The options object to validate + * @throws {string} Throw a custom error indicating that address, accountTracker, txController and blockTracker are + * missing and at least one is required + * + */ _validateParams (opts) { const { address, accountTracker, txController, blockTracker } = opts if (!address || !accountTracker || !txController || !blockTracker) { diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js index df41c90c0..f100c4525 100644 --- a/app/scripts/controllers/blacklist.js +++ b/app/scripts/controllers/blacklist.js @@ -1,6 +1,7 @@ const ObservableStore = require('obs-store') const extend = require('xtend') const PhishingDetector = require('eth-phishing-detect/src/detector') +const log = require('loglevel') // compute phishing lists const PHISHING_DETECTION_CONFIG = require('eth-phishing-detect/src/config.json') @@ -9,6 +10,22 @@ const POLLING_INTERVAL = 4 * 60 * 1000 class BlacklistController { + /** + * Responsible for polling for and storing an up to date 'eth-phishing-detect' config.json file, while + * exposing a method that can check whether a given url is a phishing attempt. The 'eth-phishing-detect' + * config.json file contains a fuzzylist, whitelist and blacklist. + * + * + * @typedef {Object} BlacklistController + * @param {object} opts Overrides the defaults for the initial state of this.store + * @property {object} store The the store of the current phishing config + * @property {object} store.phishing Contains fuzzylist, whitelist and blacklist arrays. @see + * {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json} + * @property {object} _phishingDetector The PhishingDetector instantiated by passing store.phishing to + * PhishingDetector. + * @property {object} _phishingUpdateIntervalRef Id of the interval created to periodically update the blacklist + * + */ constructor (opts = {}) { const initState = extend({ phishing: PHISHING_DETECTION_CONFIG, @@ -21,16 +38,28 @@ class BlacklistController { this._phishingUpdateIntervalRef = null } - // - // PUBLIC METHODS - // - + /** + * Given a url, returns the result of checking if that url is in the store.phishing blacklist + * + * @param {string} hostname The hostname portion of a url; the one that will be checked against the white and + * blacklists of store.phishing + * @returns {boolean} Whether or not the passed hostname is on our phishing blacklist + * + */ checkForPhishing (hostname) { if (!hostname) return false const { result } = this._phishingDetector.check(hostname) return result } + /** + * Queries `https://api.infura.io/v2/blacklist` for an updated blacklist config. This is passed to this._phishingDetector + * to update our phishing detector instance, and is updated in the store. The new phishing config is returned + * + * + * @returns {Promise} Promises the updated blacklist config for the phishingDetector + * + */ async updatePhishingList () { const response = await fetch('https://api.infura.io/v2/blacklist') const phishing = await response.json() @@ -39,6 +68,11 @@ class BlacklistController { return phishing } + /** + * Initiates the updating of the local blacklist at a set interval. The update is done via this.updatePhishingList(). + * Also, this method store a reference to that interval at this._phishingUpdateIntervalRef + * + */ scheduleUpdates () { if (this._phishingUpdateIntervalRef) return this.updatePhishingList().catch(log.warn) @@ -47,10 +81,14 @@ class BlacklistController { }, POLLING_INTERVAL) } - // - // PRIVATE METHODS - // - + /** + * Sets this._phishingDetector to a new PhishingDetector instance. + * @see {@link https://github.com/MetaMask/eth-phishing-detect} + * + * @private + * @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json} + * + */ _setupPhishingDetector (config) { this._phishingDetector = new PhishingDetector(config) } diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 653e6d762..d54efb889 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -8,7 +8,7 @@ class PreferencesController { * * @typedef {Object} PreferencesController * @param {object} opts Overrides the defaults for the initial state of this.store - * @property {object} store The an object containing a users preferences, stored in local storage + * @property {object} store The stored object containing a users preferences, stored in local storage * @property {array} store.frequentRpcList A list of custom rpcs to provide the user * @property {string} store.currentAccountTab Indicates the selected tab in the ui * @property {array} store.tokens The tokens the user wants display in their token lists diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js index 4ae3810eb..ddcaa7220 100644 --- a/app/scripts/controllers/recent-blocks.js +++ b/app/scripts/controllers/recent-blocks.js @@ -5,6 +5,23 @@ const EthQuery = require('eth-query') class RecentBlocksController { + /** + * Controller responsible for storing, updating and managing the recent history of blocks. Blocks are back filled + * upon the controller's construction and then the list is updated when the given block tracker gets a 'block' event + * (indicating that there is a new block to process). + * + * @typedef {Object} RecentBlocksController + * @param {object} opts Contains objects necessary for tracking blocks and querying the blockchain + * @param {BlockTracker} opts.blockTracker Contains objects necessary for tracking blocks and querying the blockchain + * @param {BlockTracker} opts.provider The provider used to create a new EthQuery instance. + * @property {BlockTracker} blockTracker Points to the passed BlockTracker. On RecentBlocksController construction, + * listens for 'block' events so that new blocks can be processed and added to storage. + * @property {EthQuery} ethQuery Points to the EthQuery instance created with the passed provider + * @property {number} historyLength The maximum length of blocks to track + * @property {object} store Stores the recentBlocks + * @property {array} store.recentBlocks Contains all recent blocks, up to a total that is equal to this.historyLength + * + */ constructor (opts = {}) { const { blockTracker, provider } = opts this.blockTracker = blockTracker @@ -20,12 +37,23 @@ class RecentBlocksController { this.backfill() } + /** + * Sets store.recentBlocks to an empty array + * + */ resetState () { this.store.updateState({ recentBlocks: [], }) } + /** + * Receives a new block and modifies it with this.mapTransactionsToPrices. Then adds that block to the recentBlocks + * array in storage. If the recentBlocks array contains the maximum number of blocks, the oldest block is removed. + * + * @param {object} newBlock The new block to modify and add to the recentBlocks array + * + */ processBlock (newBlock) { const block = this.mapTransactionsToPrices(newBlock) @@ -39,6 +67,15 @@ class RecentBlocksController { this.store.updateState(state) } + /** + * Receives a new block and modifies it with this.mapTransactionsToPrices. Adds that block to the recentBlocks + * array in storage, but only if the recentBlocks array contains fewer than the maximum permitted. + * + * Unlike this.processBlock, backfillBlock adds the modified new block to the beginning of the recent block array. + * + * @param {object} newBlock The new block to modify and add to the beginning of the recentBlocks array + * + */ backfillBlock (newBlock) { const block = this.mapTransactionsToPrices(newBlock) @@ -51,6 +88,14 @@ class RecentBlocksController { this.store.updateState(state) } + /** + * Receives a block and gets the gasPrice of each of its transactions. These gas prices are added to the block at a + * new property, and the block's transactions are removed. + * + * @param {object} newBlock The block to modify. It's transaction array will be replaced by a gasPrices array. + * @returns {object} The modified block. + * + */ mapTransactionsToPrices (newBlock) { const block = extend(newBlock, { gasPrices: newBlock.transactions.map((tx) => { @@ -61,6 +106,16 @@ class RecentBlocksController { return block } + /** + * On this.blockTracker's first 'block' event after this RecentBlocksController's instantiation, the store.recentBlocks + * array is populated with this.historyLength number of blocks. The block number of the this.blockTracker's first + * 'block' event is used to iteratively generate all the numbers of the previous blocks, which are obtained by querying + * the blockchain. These blocks are backfilled so that the recentBlocks array is ordered from oldest to newest. + * + * Each iteration over the block numbers is delayed by 100 milliseconds. + * + * @returns {Promise} Promises undefined + */ async backfill() { this.blockTracker.once('block', async (block) => { let blockNum = block.number @@ -89,12 +144,25 @@ class RecentBlocksController { }) } + /** + * A helper for this.backfill. Provides an easy way to ensure a 100 millisecond delay using await + * + * @returns {Promise} Promises undefined + * + */ async wait () { return new Promise((resolve) => { setTimeout(resolve, 100) }) } + /** + * Uses EthQuery to get a block that has a given block number. + * + * @param {number} number The number of the block to get + * @returns {Promise} Promises A block with the passed number + * + */ async getBlockByNumber (number) { const bn = new BN(number) return new Promise((resolve, reject) => { diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 8c3dd8c71..1b707b2bb 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -16,6 +16,24 @@ function noop () {} class AccountTracker extends EventEmitter { + /** + * This module is responsible for tracking any number of accounts and caching their current balances & transaction + * counts. + * + * It also tracks transaction hashes, and checks their inclusion status on each new block. + * + * @typedef {Object} AccountTracker + * @param {Object} opts Initialize various properties of the class. + * @property {Object} store The stored object containing all accounts to track, as well as the current block's gas limit. + * @property {Object} store.accounts The accounts currently stored in this AccountTracker + * @property {string} store.currentBlockGasLimit A hex string indicating the gas limit of the current block + * @property {Object} _provider A provider needed to create the EthQuery instance used within this AccountTracker. + * @property {EthQuery} _query An EthQuery instance used to access account information from the blockchain + * @property {BlockTracker} _blockTracker A BlockTracker instance. Needed to ensure that accounts and their info updates + * when a new block is created. + * @property {Object} _currentBlockNumber Reference to a property on the _blockTracker: the number (i.e. an id) of the the current block + * + */ constructor (opts = {}) { super() @@ -34,10 +52,17 @@ class AccountTracker extends EventEmitter { this._currentBlockNumber = this._blockTracker.currentBlock } - // - // public - // - + /** + * Ensures that the locally stored accounts are in sync with a set of accounts stored externally to this + * AccountTracker. + * + * Once this AccountTracker's accounts are up to date with those referenced by the passed addresses, each + * of these accounts are given an updated balance via Ethquery. + * + * @param {array} address The array of hex addresses for accounts with which this AccountTracker's accounts should be + * in sync + * + */ syncWithAddresses (addresses) { const accounts = this.store.getState().accounts const locals = Object.keys(accounts) @@ -61,6 +86,13 @@ class AccountTracker extends EventEmitter { this._updateAccounts() } + /** + * Adds a new address to this AccountTracker's accounts object, which points to an empty object. This object will be + * given a balance as long this._currentBlockNumber is defined. + * + * @param {string} address A hex address of a new account to store in this AccountTracker's accounts object + * + */ addAccount (address) { const accounts = this.store.getState().accounts accounts[address] = {} @@ -69,16 +101,27 @@ class AccountTracker extends EventEmitter { this._updateAccount(address) } + /** + * Removes an account from this AccountTracker's accounts object + * + * @param {string} address A hex address of a the account to remove + * + */ removeAccount (address) { const accounts = this.store.getState().accounts delete accounts[address] this.store.updateState({ accounts }) } - // - // private - // - + /** + * Given a block, updates this AccountTracker's currentBlockGasLimit, and then updates each local account's balance + * via EthQuery + * + * @private + * @param {object} block Data about the block that contains the data to update to. + * @fires 'block' The updated state, if all account updates are successfull + * + */ _updateForBlock (block) { this._currentBlockNumber = block.number const currentBlockGasLimit = block.gasLimit @@ -93,12 +136,26 @@ class AccountTracker extends EventEmitter { }) } + /** + * Calls this._updateAccount for each account in this.store + * + * @param {Function} cb A callback to pass to this._updateAccount, called after each account is succesfully updated + * + */ _updateAccounts (cb = noop) { const accounts = this.store.getState().accounts const addresses = Object.keys(accounts) async.each(addresses, this._updateAccount.bind(this), cb) } + /** + * Updates the current balance of an account. Gets an updated balance via this._getAccount. + * + * @private + * @param {string} address A hex address of a the account to be updated + * @param {Function} cb A callback to call once the account at address is successfully update + * + */ _updateAccount (address, cb = noop) { this._getAccount(address, (err, result) => { if (err) return cb(err) @@ -113,6 +170,14 @@ class AccountTracker extends EventEmitter { }) } + /** + * Gets the current balance of an account via EthQuery. + * + * @private + * @param {string} address A hex address of a the account to query + * @param {Function} cb A callback to call once the account at address is successfully update + * + */ _getAccount (address, cb = noop) { const query = this._query async.parallel({ diff --git a/app/scripts/lib/extractEthjsErrorMessage.js b/app/scripts/lib/extractEthjsErrorMessage.js index bac541735..3f0fb0232 100644 --- a/app/scripts/lib/extractEthjsErrorMessage.js +++ b/app/scripts/lib/extractEthjsErrorMessage.js @@ -4,17 +4,18 @@ const errorLabelPrefix = 'Error: ' module.exports = extractEthjsErrorMessage -// -// ethjs-rpc provides overly verbose error messages -// if we detect this type of message, we extract the important part -// Below is an example input and output -// -// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced -// -// Transaction Failed: replacement transaction underpriced -// - - +/** + * Extracts the important part of an ethjs-rpc error message. If the passed error is not anis isEthjsRpcError, the error + * is returned unchanged. + * + * @param {string} errorMessage The error message to parse + * @returns {string} Returns an error message, either the same as was passed, or the ending message portion of an isEthjsRpcError + * + * @example + * // returns 'Transaction Failed: replacement transaction underpriced' + * extractEthjsErrorMessage(`Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced`) + * +*/ function extractEthjsErrorMessage(errorMessage) { const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug) if (isEthjsRpcError) { diff --git a/app/scripts/lib/getObjStructure.js b/app/scripts/lib/getObjStructure.js index 3db389507..52250d3fb 100644 --- a/app/scripts/lib/getObjStructure.js +++ b/app/scripts/lib/getObjStructure.js @@ -14,6 +14,15 @@ module.exports = getObjStructure // } // } +/** + * Creates an object that represents the structure of the given object. It replaces all values with the result of their + * type. + * + * @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class. + * @returns {object} The "mapped" version of a deep clone of the passed object, with each non-object property value + * replaced with the javascript type of that value. + * + */ function getObjStructure(obj) { const structure = clone(obj) return deepMap(structure, (value) => { @@ -21,6 +30,14 @@ function getObjStructure(obj) { }) } +/** + * Modifies all the properties and deeply nested of a passed object. Iterates recursively over all nested objects and + * their properties, and covers the entire depth of the object. At each property value which is not an object is modified. + * + * @param {object} target The object to modify + * @param {Function} visit The modifier to apply to each non-object property value + * @returns {object} The modified object + */ function deepMap(target = {}, visit) { Object.entries(target).forEach(([key, value]) => { if (typeof value === 'object' && value !== null) { diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index f52e048e0..52b897720 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -3,8 +3,45 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const createId = require('./random-id') +var msgData = { + id: msgId, + msgParams: msgParams, + time: time, + status: 'unapproved', + type: 'eth_sign', +} + +/** + * Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for + * an eth_sign call is requested. + * + * @see {@link https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign} + * + * @typedef {Object} Message + * @property {number} id An id to track and identify the message object + * @property {object} msgParams The parameters to pass to the eth_sign method once the signature request is approved. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request + * @property {number} time The epoch time at which the this message was created + * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' + * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' with + * always have a 'eth_sign' type. + * + */ module.exports = class MessageManager extends EventEmitter { + + /** + * Controller in charge of managing - storing, adding, removing, updating - Messages. + * + * @typedef {Object} MessageManager + * @param {object} opts @deprecated + * @property {object} memStore The observable store where Messages are saved with persistance. + * @property {object} memStore.unapprovedMsgs A collection of all Messages in the 'unapproved' state + * @property {number} memStore.unapprovedMsgCount The count of all Messages in this.memStore.unapprobedMsgs + * @property {array} messages Holds all messages that have been created by this MessageManager + * + */ constructor (opts) { super() this.memStore = new ObservableStore({ @@ -14,15 +51,35 @@ module.exports = class MessageManager extends EventEmitter { this.messages = [] } + /** + * A getter for the number of 'unapproved' Messages in this.messages + * + * @returns {number} The number of 'unapproved' Messages in this.messages + * + */ get unapprovedMsgCount () { return Object.keys(this.getUnapprovedMsgs()).length } + /** + * A getter for the 'unapproved' Messages in this.messages + * + * @returns {object} An index of Message ids to Messages, for all 'unapproved' Messages in this.messages + * + */ getUnapprovedMsgs () { return this.messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } + /** + * Creates a new Message with an 'unapproved' status using the passed msgParams. this.addMsg is called to add the + * new Message to this.messages, and to save the unapproved Messages from that list to this.memStore. + * + * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @returns {number} The id of the newly created message. + * + */ addUnapprovedMessage (msgParams) { msgParams.data = normalizeMsgData(msgParams.data) // create txData obj with parameters and meta data @@ -42,24 +99,61 @@ module.exports = class MessageManager extends EventEmitter { return msgId } + /** + * Adds a passed Message to this.messages, and calls this._saveMsgList() to save the unapproved Messages from that + * list to this.memStore. + * + * @param {Message} msg The Message to add to this.messages + * + */ addMsg (msg) { this.messages.push(msg) this._saveMsgList() } + /** + * Returns a specified Message. + * + * @param {number} msgId The id of the Message to get + * @returns {Message|undefined} The Message with the id that matches the passed msgId, or undefined if no Message has that id. + * + */ getMsg (msgId) { return this.messages.find(msg => msg.id === msgId) } + /** + * Approves a Message. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise with + * any the message params modified for proper signing. + * + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @returns {Promise} Promises the msgParams object with metamaskId removed. + * + */ approveMessage (msgParams) { this.setMsgStatusApproved(msgParams.metamaskId) return this.prepMsgForSigning(msgParams) } + /** + * Sets a Message status to 'approved' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the Message to approve. + * + */ setMsgStatusApproved (msgId) { this._setMsgStatus(msgId, 'approved') } + /** + * Sets a Message status to 'signed' via a call to this._setMsgStatus and updates that Message in this.messages by + * adding the raw signature data of the signature request to the Message + * + * @param {number} msgId The id of the Message to sign. + * @param {buffer} rawSig The raw data of the signature request + * + */ setMsgStatusSigned (msgId, rawSig) { const msg = this.getMsg(msgId) msg.rawSig = rawSig @@ -67,19 +161,40 @@ module.exports = class MessageManager extends EventEmitter { this._setMsgStatus(msgId, 'signed') } + /** + * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams + * + * @param {object} msgParams The msgParams to modify + * @returns {Promise} Promises the msgParams with the metamaskId property removed + * + */ prepMsgForSigning (msgParams) { delete msgParams.metamaskId return Promise.resolve(msgParams) } + /** + * Sets a Message status to 'rejected' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the Message to reject. + * + */ rejectMsg (msgId) { this._setMsgStatus(msgId, 'rejected') } - // - // PRIVATE METHODS - // - + /** + * Updates the status of a Message in this.messages via a call to this._updateMsg + * + * @private + * @param {number} msgId The id of the Message to update. + * @param {string} status The new status of the Message. + * @throws A 'MessageManager - Message not found for id: "${msgId}".' if there is no Message in this.messages with an + * id equal to the passed msgId + * @fires An event with a name equal to `${msgId}:${status}`. The Message is also fired. + * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along with the message + * + */ _setMsgStatus (msgId, status) { const msg = this.getMsg(msgId) if (!msg) throw new Error('MessageManager - Message not found for id: "${msgId}".') @@ -91,6 +206,14 @@ module.exports = class MessageManager extends EventEmitter { } } + /** + * Sets a Message in this.messages to the passed Message if the ids are equal. Then saves the unapprovedMsg list to + * storage via this._saveMsgList + * + * @private + * @param {msg} Message A Message that will replace an existing Message (with the same id) in this.messages + * + */ _updateMsg (msg) { const index = this.messages.findIndex((message) => message.id === msg.id) if (index !== -1) { @@ -99,6 +222,13 @@ module.exports = class MessageManager extends EventEmitter { this._saveMsgList() } + /** + * Saves the unapproved messages, and their count, to this.memStore + * + * @private + * @fires 'updateBadge' + * + */ _saveMsgList () { const unapprovedMsgs = this.getUnapprovedMsgs() const unapprovedMsgCount = Object.keys(unapprovedMsgs).length @@ -108,6 +238,13 @@ module.exports = class MessageManager extends EventEmitter { } +/** + * A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex. + * + * @param {any} data The buffer data to convert to a hex + * @returns {string} A hex string conversion of the buffer data + * + */ function normalizeMsgData (data) { if (data.slice(0, 2) === '0x') { // data is already hex diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js index 1fcb7cf69..ce4825d5b 100644 --- a/app/scripts/lib/notification-manager.js +++ b/app/scripts/lib/notification-manager.js @@ -5,9 +5,12 @@ const width = 360 class NotificationManager { - // - // Public - // + /** + * A collection of methods for controlling the showing and hiding of the notification popup. + * + * @typedef {Object} NotificationManager + * + */ showPopup () { this._getPopup((err, popup) => { diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index 6ae526463..0f1dc19a9 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -3,16 +3,28 @@ const normalize = require('eth-sig-util').normalize class PendingBalanceCalculator { - // Must be initialized with two functions: - // getBalance => Returns a promise of a BN of the current balance in Wei - // getPendingTransactions => Returns an array of TxMeta Objects, - // which have txParams properties, which include value, gasPrice, and gas, - // all in a base=16 hex format. + /** + * Used for calculating a users "pending balance": their current balance minus the total possible cost of all their + * pending transactions. + * + * @typedef {Object} PendingBalanceCalculator + * @param {Function} getBalance Returns a promise of a BN of the current balance in Wei + * @param {Function} getPendingTransactions Returns an array of TxMeta Objects, which have txParams properties, + * which include value, gasPrice, and gas, all in a base=16 hex format. + * + */ constructor ({ getBalance, getPendingTransactions }) { this.getPendingTransactions = getPendingTransactions this.getNetworkBalance = getBalance } + /** + * Returns the users "pending balance": their current balance minus the total possible cost of all their + * pending transactions. + * + * @returns {Promise} Promises a base 16 hex string that contains the user's "pending balance" + * + */ async getBalance () { const results = await Promise.all([ this.getNetworkBalance(), @@ -29,6 +41,15 @@ class PendingBalanceCalculator { return `0x${balance.sub(pendingValue).toString(16)}` } + /** + * Calculates the maximum possible cost of a single transaction, based on the value, gas price and gas limit. + * + * @param {object} tx Contains all that data about a transaction. + * @property {object} tx.txParams Contains data needed to calculate the maximum cost of the transaction: gas, + * gasLimit and value. + * + * @returns {string} Returns a base 16 hex string that contains the maximum possible cost of the transaction. + */ calculateMaxCost (tx) { const txValue = tx.txParams.value const value = this.hexToBn(txValue) @@ -42,6 +63,13 @@ class PendingBalanceCalculator { return value.add(gasCost) } + /** + * Converts a hex string to a BN object + * + * @param {string} hex A number represented as a hex string + * @returns {Object} A BN object + * + */ hexToBn (hex) { return new BN(normalize(hex).substring(2), 16) } diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 6602f5aa8..3c502329c 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -4,8 +4,37 @@ const ethUtil = require('ethereumjs-util') const createId = require('./random-id') const hexRe = /^[0-9A-Fa-f]+$/g +/** + * Represents, and contains data about, an 'personal_sign' type signature request. These are created when a + * signature for an personal_sign call is requested. + * + * @see {@link https://web3js.readthedocs.io/en/1.0/web3-eth-personal.html#sign} + * + * @typedef {Object} PersonalMessage + * @property {number} id An id to track and identify the message object + * @property {object} msgParams The parameters to pass to the personal_sign method once the signature request is + * approved. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request + * @property {number} time The epoch time at which the this message was created + * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' + * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will + * always have a 'personal_sign' type. + * + */ module.exports = class PersonalMessageManager extends EventEmitter { + /** + * Controller in charge of managing - storing, adding, removing, updating - PersonalMessage. + * + * @typedef {Object} PersonalMessageManager + * @param {object} opts @deprecated + * @property {object} memStore The observable store where PersonalMessage are saved with persistance. + * @property {object} memStore.unapprovedPersonalMsgs A collection of all PersonalMessages in the 'unapproved' state + * @property {number} memStore.unapprovedPersonalMsgCount The count of all PersonalMessages in this.memStore.unapprobedMsgs + * @property {array} messages Holds all messages that have been created by this PersonalMessageManager + * + */ constructor (opts) { super() this.memStore = new ObservableStore({ @@ -15,15 +44,37 @@ module.exports = class PersonalMessageManager extends EventEmitter { this.messages = [] } + /** + * A getter for the number of 'unapproved' PersonalMessages in this.messages + * + * @returns {number} The number of 'unapproved' PersonalMessages in this.messages + * + */ get unapprovedPersonalMsgCount () { return Object.keys(this.getUnapprovedMsgs()).length } + /** + * A getter for the 'unapproved' PersonalMessages in this.messages + * + * @returns {object} An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in + * this.messages + * + */ getUnapprovedMsgs () { return this.messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } + /** + * Creates a new PersonalMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add + * the new PersonalMessage to this.messages, and to save the unapproved PersonalMessages from that list to + * this.memStore. + * + * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @returns {number} The id of the newly created PersonalMessage. + * + */ addUnapprovedMessage (msgParams) { log.debug(`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`) msgParams.data = this.normalizeMsgData(msgParams.data) @@ -44,24 +95,62 @@ module.exports = class PersonalMessageManager extends EventEmitter { return msgId } + /** + * Adds a passed PersonalMessage to this.messages, and calls this._saveMsgList() to save the unapproved PersonalMessages from that + * list to this.memStore. + * + * @param {Message} msg The PersonalMessage to add to this.messages + * + */ addMsg (msg) { this.messages.push(msg) this._saveMsgList() } + /** + * Returns a specified PersonalMessage. + * + * @param {number} msgId The id of the PersonalMessage to get + * @returns {PersonalMessage|undefined} The PersonalMessage with the id that matches the passed msgId, or undefined + * if no PersonalMessage has that id. + * + */ getMsg (msgId) { return this.messages.find(msg => msg.id === msgId) } + /** + * Approves a PersonalMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise + * with any the message params modified for proper signing. + * + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @returns {Promise} Promises the msgParams object with metamaskId removed. + * + */ approveMessage (msgParams) { this.setMsgStatusApproved(msgParams.metamaskId) return this.prepMsgForSigning(msgParams) } + /** + * Sets a PersonalMessage status to 'approved' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the PersonalMessage to approve. + * + */ setMsgStatusApproved (msgId) { this._setMsgStatus(msgId, 'approved') } + /** + * Sets a PersonalMessage status to 'signed' via a call to this._setMsgStatus and updates that PersonalMessage in + * this.messages by adding the raw signature data of the signature request to the PersonalMessage + * + * @param {number} msgId The id of the PersonalMessage to sign. + * @param {buffer} rawSig The raw data of the signature request + * + */ setMsgStatusSigned (msgId, rawSig) { const msg = this.getMsg(msgId) msg.rawSig = rawSig @@ -69,19 +158,41 @@ module.exports = class PersonalMessageManager extends EventEmitter { this._setMsgStatus(msgId, 'signed') } + /** + * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams + * + * @param {object} msgParams The msgParams to modify + * @returns {Promise} Promises the msgParams with the metamaskId property removed + * + */ prepMsgForSigning (msgParams) { delete msgParams.metamaskId return Promise.resolve(msgParams) } + /** + * Sets a PersonalMessage status to 'rejected' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the PersonalMessage to reject. + * + */ rejectMsg (msgId) { this._setMsgStatus(msgId, 'rejected') } - // - // PRIVATE METHODS - // - + /** + * Updates the status of a PersonalMessage in this.messages via a call to this._updateMsg + * + * @private + * @param {number} msgId The id of the PersonalMessage to update. + * @param {string} status The new status of the PersonalMessage. + * @throws A 'PersonalMessageManager - PersonalMessage not found for id: "${msgId}".' if there is no PersonalMessage + * in this.messages with an id equal to the passed msgId + * @fires An event with a name equal to `${msgId}:${status}`. The PersonalMessage is also fired. + * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along + * with the PersonalMessage + * + */ _setMsgStatus (msgId, status) { const msg = this.getMsg(msgId) if (!msg) throw new Error('PersonalMessageManager - Message not found for id: "${msgId}".') @@ -93,6 +204,15 @@ module.exports = class PersonalMessageManager extends EventEmitter { } } + /** + * Sets a PersonalMessage in this.messages to the passed PersonalMessage if the ids are equal. Then saves the + * unapprovedPersonalMsgs index to storage via this._saveMsgList + * + * @private + * @param {msg} PersonalMessage A PersonalMessage that will replace an existing PersonalMessage (with the same + * id) in this.messages + * + */ _updateMsg (msg) { const index = this.messages.findIndex((message) => message.id === msg.id) if (index !== -1) { @@ -101,6 +221,13 @@ module.exports = class PersonalMessageManager extends EventEmitter { this._saveMsgList() } + /** + * Saves the unapproved PersonalMessages, and their count, to this.memStore + * + * @private + * @fires 'updateBadge' + * + */ _saveMsgList () { const unapprovedPersonalMsgs = this.getUnapprovedMsgs() const unapprovedPersonalMsgCount = Object.keys(unapprovedPersonalMsgs).length @@ -108,6 +235,13 @@ module.exports = class PersonalMessageManager extends EventEmitter { this.emit('updateBadge') } + /** + * A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex. + * + * @param {any} data The buffer data to convert to a hex + * @returns {string} A hex string conversion of the buffer data + * + */ normalizeMsgData (data) { try { const stripped = ethUtil.stripHexPrefix(data) diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js index 9cea22029..826e73306 100644 --- a/app/scripts/lib/seed-phrase-verifier.js +++ b/app/scripts/lib/seed-phrase-verifier.js @@ -2,11 +2,19 @@ const KeyringController = require('eth-keyring-controller') const seedPhraseVerifier = { - // Verifies if the seed words can restore the accounts. - // - // The seed words can recreate the primary keyring and the accounts belonging to it. - // The created accounts in the primary keyring are always the same. - // The keyring always creates the accounts in the same sequence. + /** + * Verifies if the seed words can restore the accounts. + * + * Key notes: + * - The seed words can recreate the primary keyring and the accounts belonging to it. + * - The created accounts in the primary keyring are always the same. + * - The keyring always creates the accounts in the same sequence. + * + * @param {array} createdAccounts The accounts to restore + * @param {string} seedWords The seed words to verify + * @returns {Promise} Promises undefined + * + */ verifyAccounts (createdAccounts, seedWords) { return new Promise((resolve, reject) => { diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 8b760790e..8716ebf9a 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -4,8 +4,38 @@ const createId = require('./random-id') const assert = require('assert') const sigUtil = require('eth-sig-util') +/** + * Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a + * signature for an eth_signTypedData call is requested. + * + * @see {@link } + * + * @typedef {Object} TypedMessage + * @property {number} id An id to track and identify the message object + * @property {object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is + * approved. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {object} msgParams.from The address that is making the signature request. + * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request + * @property {number} time The epoch time at which the this message was created + * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' + * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will + * always have a 'eth_signTypedData' type. + * + */ module.exports = class TypedMessageManager extends EventEmitter { + /** + * Controller in charge of managing - storing, adding, removing, updating - TypedMessage. + * + * @typedef {Object} TypedMessage + * @param {object} opts @deprecated + * @property {object} memStore The observable store where TypedMessage are saved with persistance. + * @property {object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state + * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs + * @property {array} messages Holds all messages that have been created by this TypedMessage + * + */ constructor (opts) { super() this.memStore = new ObservableStore({ @@ -15,15 +45,37 @@ module.exports = class TypedMessageManager extends EventEmitter { this.messages = [] } + /** + * A getter for the number of 'unapproved' TypedMessages in this.messages + * + * @returns {number} The number of 'unapproved' TypedMessages in this.messages + * + */ get unapprovedTypedMessagesCount () { return Object.keys(this.getUnapprovedMsgs()).length } + /** + * A getter for the 'unapproved' TypedMessages in this.messages + * + * @returns {object} An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in + * this.messages + * + */ getUnapprovedMsgs () { return this.messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } + /** + * Creates a new TypedMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add + * the new TypedMessage to this.messages, and to save the unapproved TypedMessages from that list to + * this.memStore. Before any of this is done, msgParams are validated + * + * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @returns {number} The id of the newly created TypedMessage. + * + */ addUnapprovedMessage (msgParams) { this.validateParams(msgParams) @@ -45,6 +97,12 @@ module.exports = class TypedMessageManager extends EventEmitter { return msgId } + /** + * Helper method for this.addUnapprovedMessage. Validates that the passed params have the required properties. + * + * @param {object} params The params to validate + * + */ validateParams (params) { assert.equal(typeof params, 'object', 'Params should ben an object.') assert.ok('data' in params, 'Params must include a data field.') @@ -56,24 +114,62 @@ module.exports = class TypedMessageManager extends EventEmitter { }, 'Expected EIP712 typed data') } + /** + * Adds a passed TypedMessage to this.messages, and calls this._saveMsgList() to save the unapproved TypedMessages from that + * list to this.memStore. + * + * @param {Message} msg The TypedMessage to add to this.messages + * + */ addMsg (msg) { this.messages.push(msg) this._saveMsgList() } + /** + * Returns a specified TypedMessage. + * + * @param {number} msgId The id of the TypedMessage to get + * @returns {TypedMessage|undefined} The TypedMessage with the id that matches the passed msgId, or undefined + * if no TypedMessage has that id. + * + */ getMsg (msgId) { return this.messages.find(msg => msg.id === msgId) } + /** + * Approves a TypedMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise + * with any the message params modified for proper signing. + * + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @returns {Promise} Promises the msgParams object with metamaskId removed. + * + */ approveMessage (msgParams) { this.setMsgStatusApproved(msgParams.metamaskId) return this.prepMsgForSigning(msgParams) } + /** + * Sets a TypedMessage status to 'approved' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the TypedMessage to approve. + * + */ setMsgStatusApproved (msgId) { this._setMsgStatus(msgId, 'approved') } + /** + * Sets a TypedMessage status to 'signed' via a call to this._setMsgStatus and updates that TypedMessage in + * this.messages by adding the raw signature data of the signature request to the TypedMessage + * + * @param {number} msgId The id of the TypedMessage to sign. + * @param {buffer} rawSig The raw data of the signature request + * + */ setMsgStatusSigned (msgId, rawSig) { const msg = this.getMsg(msgId) msg.rawSig = rawSig @@ -81,11 +177,24 @@ module.exports = class TypedMessageManager extends EventEmitter { this._setMsgStatus(msgId, 'signed') } + /** + * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams + * + * @param {object} msgParams The msgParams to modify + * @returns {Promise} Promises the msgParams with the metamaskId property removed + * + */ prepMsgForSigning (msgParams) { delete msgParams.metamaskId return Promise.resolve(msgParams) } + /** + * Sets a TypedMessage status to 'rejected' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the TypedMessage to reject. + * + */ rejectMsg (msgId) { this._setMsgStatus(msgId, 'rejected') } @@ -94,6 +203,19 @@ module.exports = class TypedMessageManager extends EventEmitter { // PRIVATE METHODS // + /** + * Updates the status of a TypedMessage in this.messages via a call to this._updateMsg + * + * @private + * @param {number} msgId The id of the TypedMessage to update. + * @param {string} status The new status of the TypedMessage. + * @throws A 'TypedMessageManager - TypedMessage not found for id: "${msgId}".' if there is no TypedMessage + * in this.messages with an id equal to the passed msgId + * @fires An event with a name equal to `${msgId}:${status}`. The TypedMessage is also fired. + * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along + * with the TypedMessage + * + */ _setMsgStatus (msgId, status) { const msg = this.getMsg(msgId) if (!msg) throw new Error('TypedMessageManager - Message not found for id: "${msgId}".') @@ -105,6 +227,15 @@ module.exports = class TypedMessageManager extends EventEmitter { } } + /** + * Sets a TypedMessage in this.messages to the passed TypedMessage if the ids are equal. Then saves the + * unapprovedTypedMsgs index to storage via this._saveMsgList + * + * @private + * @param {msg} TypedMessage A TypedMessage that will replace an existing TypedMessage (with the same + * id) in this.messages + * + */ _updateMsg (msg) { const index = this.messages.findIndex((message) => message.id === msg.id) if (index !== -1) { @@ -113,6 +244,13 @@ module.exports = class TypedMessageManager extends EventEmitter { this._saveMsgList() } + /** + * Saves the unapproved TypedMessages, and their count, to this.memStore + * + * @private + * @fires 'updateBadge' + * + */ _saveMsgList () { const unapprovedTypedMessages = this.getUnapprovedMsgs() const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages).length From 16434776a4f474cc4d4bdf004b4715f292f01fe5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 Apr 2018 11:23:15 -0700 Subject: [PATCH 15/79] Document Background Script --- app/scripts/background.js | 60 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 6550e8944..244173dcf 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,3 +1,7 @@ +/* + * @file The entry point for the web extension singleton process. + */ + const urlUtil = require('url') const endOfStream = require('end-of-stream') const pump = require('pump') @@ -61,6 +65,21 @@ initialize().catch(log.error) // setup metamask mesh testing container setupMetamaskMeshMetrics() +/* + * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. + * @typedef MetaMaskState + */ + +/* + * @typedef VersionedData + * @property {MetaMaskState} data - The data emitted from MetaMask controller, or used to initialize it. + * @property {Number} version - The latest migration version that has been run. + */ + +/* + * Initializes the MetaMask controller, and sets up all platform configuration. + * @returns {Promise} Setup complete. + */ async function initialize () { const initState = await loadStateFromPersistence() const initLangCode = await getFirstPreferredLangCode() @@ -72,6 +91,11 @@ async function initialize () { // State and Persistence // +/* + * Loads any stored data, prioritizing the latest storage strategy. + * Migrates that data schema in case it was last loaded on an older version. + * @returns {Promise} Last data emitted from previous instance of MetaMask. + */ async function loadStateFromPersistence () { // migrations const migrator = new Migrator({ migrations }) @@ -134,6 +158,16 @@ async function loadStateFromPersistence () { return versionedData.data } +/* + * Initializes the MetaMask Controller with any initial state and default language. + * Configures platform-specific error reporting strategy. + * Streams emitted state updates to platform-specific storage strategy. + * Creates platform listeners for new Dapps/Contexts, and sets up their data connections to the controller. + * + * @param {Object} initState - The initial state to start the controller with, matches the state that is emitted from the controller. + * @param {String} initLangCode - The region code for the language preferred by the current user. + * @returns {Promise} After setup is complete. + */ function setupController (initState, initLangCode) { // // MetaMask Controller @@ -172,6 +206,11 @@ function setupController (initState, initLangCode) { } ) + /* + * Assigns the given state to the versioned object (with metadata), and returns that. + * @param {Object} state - The state object as emitted by the MetaMaskController. + * @returns {VersionedData} The state object wrapped in an object that includes a metadata key. + */ function versionifyData (state) { versionedData.data = state return versionedData @@ -208,6 +247,18 @@ function setupController (initState, initLangCode) { return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen } + /* + * A runtime.Port object, as provided by the browser: + * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port + * @typedef Port + * @type Object + */ + + /* + * Connects a Port to the MetaMask controller via a multiplexed duplex stream. + * This method identifies trusted (MetaMask) interfaces, and connects them differently from untrusted (web pages). + * @param {Port} remotePort - The port provided by a new context. + */ function connectRemote (remotePort) { const processName = remotePort.name const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName] @@ -261,7 +312,10 @@ function setupController (initState, initLangCode) { controller.messageManager.on('updateBadge', updateBadge) controller.personalMessageManager.on('updateBadge', updateBadge) - // plugin badge text + /* + * Updates the Web Extension's "badge" number, on the little fox in the toolbar. + * The number reflects the current number of pending transactions or message signatures needing user approval. + */ function updateBadge () { var label = '' var unapprovedTxCount = controller.txController.getUnapprovedTxCount() @@ -283,7 +337,9 @@ function setupController (initState, initLangCode) { // Etc... // -// popup trigger +/* + * Opens the browser popup for user confirmation + */ function triggerUi () { extension.tabs.query({ active: true }, tabs => { const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id])) From eeb9390de81ce6fc92247d5c499e991dce8330bd Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 19 Apr 2018 11:29:26 -0700 Subject: [PATCH 16/79] meta - transactions - docs yo! --- .../controllers/transactions/README.md | 92 ++++++++ app/scripts/controllers/transactions/index.js | 27 ++- .../lib/tx-state-history-helper.js | 27 ++- .../controllers/transactions/lib/util.js | 37 +++- .../controllers/transactions/nonce-tracker.js | 28 ++- .../transactions/pending-tx-tracker.js | 10 +- .../controllers/transactions/tx-gas-utils.js | 1 + .../transactions/tx-state-manager.js | 202 +++++++++++++----- docs/transaction-flow.png | Bin 0 -> 141125 bytes 9 files changed, 351 insertions(+), 73 deletions(-) create mode 100644 app/scripts/controllers/transactions/README.md create mode 100644 docs/transaction-flow.png diff --git a/app/scripts/controllers/transactions/README.md b/app/scripts/controllers/transactions/README.md new file mode 100644 index 000000000..ea38b5ae6 --- /dev/null +++ b/app/scripts/controllers/transactions/README.md @@ -0,0 +1,92 @@ +# Transaction Controller + +Transaction Controller is an aggregate of sub-controllers and trackers +composing them in a way to be exposed to the metamask controller + +- txStateManager + responsible for the state of a transaction and + storing the transaction +- pendingTxTracker + watching blocks for transactions to be include + and emitting confirmed events +- txGasUtil + gas calculations and safety buffering +- nonceTracker + calculating nonces + +## flow digram of processing a transaction + +![transaction-flow](../../../../docs/transaction-flow.png) + +## txMeta's && txParams + +A txMeta is the "meta" object it has all the random bits of info we need about a transaction on it. txParams are sacred every thing on txParams gets signed so it must +be a valid key and be hex prefixed except for the network number. Extra stuff must go on the txMeta! + +Here is a txMeta too look at: + +```js +txMeta = { + "id": 2828415030114568, // unique id for this txMeta used for look ups + "time": 1524094064821, // time of creation + "status": "confirmed", + "metamaskNetworkId": "1524091532133", //the network id for the transaction + "loadingDefaults": false, // used to tell the ui when we are done calculatyig gass defaults + "txParams": { // the txParams object + "from": "0x8acce2391c0d510a6c5e5d8f819a678f79b7e675", + "to": "0x8acce2391c0d510a6c5e5d8f819a678f79b7e675", + "value": "0x0", + "gasPrice": "0x3b9aca00", + "gas": "0x7b0c", + "nonce": "0x0" + }, + "history": [{ //debug + "id": 2828415030114568, + "time": 1524094064821, + "status": "unapproved", + "metamaskNetworkId": "1524091532133", + "loadingDefaults": true, + "txParams": { + "from": "0x8acce2391c0d510a6c5e5d8f819a678f79b7e675", + "to": "0x8acce2391c0d510a6c5e5d8f819a678f79b7e675", + "value": "0x0" + } + }, + [ + { + "op": "add", + "path": "/txParams/gasPrice", + "value": "0x3b9aca00" + }, + ...], // I've removed most of history for this + "gasPriceSpecified": false, //weather or not the user/dapp has specified gasPrice + "gasLimitSpecified": false, //weather or not the user/dapp has specified gas + "estimatedGas": "5208", + "origin": "MetaMask", //debug + "nonceDetails": { + "params": { + "highestLocallyConfirmed": 0, + "highestSuggested": 0, + "nextNetworkNonce": 0 + }, + "local": { + "name": "local", + "nonce": 0, + "details": { + "startPoint": 0, + "highest": 0 + } + }, + "network": { + "name": "network", + "nonce": 0, + "details": { + "baseCount": 0 + } + } + }, + "rawTx": "0xf86980843b9aca00827b0c948acce2391c0d510a6c5e5d8f819a678f79b7e67580808602c5b5de66eea05c01a320b96ac730cb210ca56d2cb71fa360e1fc2c21fa5cf333687d18eb323fa02ed05987a6e5fd0f2459fcff80710b76b83b296454ad9a37594a0ccb4643ea90", // used for rebroadcast + "hash": "0xa45ba834b97c15e6ff4ed09badd04ecd5ce884b455eb60192cdc73bcc583972a", + "submittedTime": 1524094077902 // time of the attempt to submit the raw tx to the network, used in the ui to show the retry button +} +``` diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index c81251cd2..d7287450b 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -25,14 +25,15 @@ const txUtils = require('./lib/util') @param {object} opts - - - initState, initial transaction list default is an empty array
- - networkStore, an observable store for network number
- - blockTracker,
- - provider,
- - signTransaction, function the signs an ethereumjs-tx
- - getGasPrice, optional gas price calculator
- - txHistoryLimit, number *optional* for limiting how many transactions are in state
- - preferencesStore, + @property {object} opts.initState initial transaction list default is an empty array + @property {Object} opts.networkStore an observable store for network number + @property {Object} opts.blockTracker + @property {Object} opts.provider + @property {Object} opts.signTransaction function the signs an ethereumjs-tx + @property {function} opts.getGasPrice optional gas price calculator + @property {function} opts.signTransaction ethTx signer that returns a rawTx + @property {number} opts.txHistoryLimit number *optional* for limiting how many transactions are in state + @property {Object} opts.preferencesStore @class */ @@ -50,12 +51,12 @@ class TransactionController extends EventEmitter { this.query = new EthQuery(this.provider) this.txGasUtil = new TxGasUtil(this.provider) + this._mapMethods() this.txStateManager = new TransactionStateManager({ initState: opts.initState, txHistoryLimit: opts.txHistoryLimit, getNetwork: this.getNetwork.bind(this), }) - this._mapMethods() this._onBootCleanUp() this.store = this.txStateManager.store @@ -92,7 +93,10 @@ class TransactionController extends EventEmitter { } } -/** Adds a tx to the txlist */ +/** + Adds a tx to the txlist + @emits ${txMeta.id}:unapproved +*/ addTx (txMeta) { this.txStateManager.addTx(txMeta) this.emit(`${txMeta.id}:unapproved`, txMeta) @@ -172,6 +176,7 @@ add a new unapproved transaction to the pipeline async addTxGasDefaults (txMeta) { const txParams = txMeta.txParams // ensure value + txParams.value = txParams.value ? ethUtil.addHexPrefix(value) : '0x0', txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) let gasPrice = txParams.gasPrice if (!gasPrice) { @@ -412,4 +417,4 @@ add a new unapproved transaction to the pipeline } } -module.exports = TransactionController \ No newline at end of file +module.exports = TransactionController diff --git a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js index 94c7b6792..7a57e3cb5 100644 --- a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js +++ b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js @@ -1,6 +1,6 @@ const jsonDiffer = require('fast-json-patch') const clone = require('clone') - +/** @module*/ module.exports = { generateHistoryEntry, replayHistory, @@ -8,7 +8,11 @@ module.exports = { migrateFromSnapshotsToDiffs, } - +/** + converts non-initial history entries into diffs + @param longHistory {array} + @returns {array} +*/ function migrateFromSnapshotsToDiffs (longHistory) { return ( longHistory @@ -20,6 +24,17 @@ function migrateFromSnapshotsToDiffs (longHistory) { ) } +/** + generates an array of history objects sense the previous state. + The object has the keys opp(the operation preformed), + path(the key and if a nested object then each key will be seperated with a `/`) + value + with the first entry having the note + @param previousState {object} - the previous state of the object + @param newState {object} - the update object + @param note {string} - a optional note for the state change + @reurns {array} +*/ function generateHistoryEntry (previousState, newState, note) { const entry = jsonDiffer.compare(previousState, newState) // Add a note to the first op, since it breaks if we append it to the entry @@ -27,11 +42,19 @@ function generateHistoryEntry (previousState, newState, note) { return entry } +/** + Recovers previous txMeta state obj + @return {object} +*/ function replayHistory (_shortHistory) { const shortHistory = clone(_shortHistory) return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument) } +/** + @param txMeta {object} + @returns {object} a clone object of the txMeta with out history +*/ function snapshotFromTxMeta (txMeta) { // create txMeta snapshot for history const snapshot = clone(txMeta) diff --git a/app/scripts/controllers/transactions/lib/util.js b/app/scripts/controllers/transactions/lib/util.js index b18283997..84f7592a0 100644 --- a/app/scripts/controllers/transactions/lib/util.js +++ b/app/scripts/controllers/transactions/lib/util.js @@ -3,11 +3,15 @@ const { isValidAddress, } = require('ethereumjs-util') +/** +@module +*/ module.exports = { normalizeTxParams, validateTxParams, validateFrom, validateRecipient, + getFinalStates, } @@ -16,22 +20,30 @@ const normalizers = { from: from => addHexPrefix(from).toLowerCase(), to: to => addHexPrefix(to).toLowerCase(), nonce: nonce => addHexPrefix(nonce), - value: value => value ? addHexPrefix(value) : '0x0', + value: value => addHexPrefix(value), data: data => addHexPrefix(data), gas: gas => addHexPrefix(gas), gasPrice: gasPrice => addHexPrefix(gasPrice), } + /** + normalizes txParams + @param txParams {object} + @returns {object} normalized txParams */ function normalizeTxParams (txParams) { // apply only keys in the normalizers const normalizedTxParams = {} - for (let key in normalizers) { + for (const key in normalizers) { if (txParams[key]) normalizedTxParams[key] = normalizers[key](txParams[key]) } return normalizedTxParams } + /** + validates txParams + @param txParams {object} + */ function validateTxParams (txParams) { validateFrom(txParams) validateRecipient(txParams) @@ -47,11 +59,19 @@ function validateTxParams (txParams) { } } + /** + validates the from field in txParams + @param txParams {object} + */ function validateFrom (txParams) { if (!(typeof txParams.from === 'string')) throw new Error(`Invalid from address ${txParams.from} not a string`) if (!isValidAddress(txParams.from)) throw new Error('Invalid from address') } + /** + validates the to field in txParams + @param txParams {object} + */ function validateRecipient (txParams) { if (txParams.to === '0x' || txParams.to === null) { if (txParams.data) { @@ -64,3 +84,16 @@ function validateRecipient (txParams) { } return txParams } + + /** + @returns an {array} of states that can be considered final + */ +function getFinalStates () { + return [ + 'rejected', // the user has responded no! + 'confirmed', // the tx has been included in a block. + 'failed', // the tx failed for some reason, included on tx data. + 'dropped', // the tx nonce was already used + ] +} + diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js index 5b1cd7f43..e0f4d0fe3 100644 --- a/app/scripts/controllers/transactions/nonce-tracker.js +++ b/app/scripts/controllers/transactions/nonce-tracker.js @@ -1,7 +1,15 @@ const EthQuery = require('ethjs-query') const assert = require('assert') const Mutex = require('await-semaphore').Mutex - +/** + @param opts {object} - + @property {Object} opts.provider a ethereum provider + @property {function} opts.getPendingTransactions a function that returns an array of txMeta + whos status is `submitted` + @property {function} opts.getConfirmedTransactions a function that returns an array of txMeta + whos status is `confirmed` + @class +*/ class NonceTracker { constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { @@ -12,6 +20,9 @@ class NonceTracker { this.lockMap = {} } + /** + @returns {object} with the key releaseLock (the gloabl mutex) + */ async getGlobalLock () { const globalMutex = this._lookupMutex('global') // await global mutex free @@ -19,8 +30,19 @@ class NonceTracker { return { releaseLock } } - // releaseLock must be called - // releaseLock must be called after adding signed tx to pending transactions (or discarding) + /** + this will return an object with the `nextNonce` `nonceDetails` which is an + object with: + highestLocallyConfirmed (nonce), + highestSuggested (either the network nonce or the highestLocallyConfirmed nonce), + nextNetworkNonce (the nonce suggested by the network), + and the releaseLock +
note: releaseLock must be called after adding signed tx to pending transactions + (or discarding)
+ + @param address {string} the hex string for the address whos nonce we are calculating + @returns {object} + */ async getNonceLock (address) { // await global mutex free await this._globalMutexFree() diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index 503343e22..98b3d2b08 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -8,10 +8,10 @@ const EthQuery = require('ethjs-query') As well as continues broadcast while in the pending state
@param config {object} - non optional configuration object consists of: -
provider -
nonceTracker: see nonce tracker -
getPendingTransactions: a function for getting an array of transactions, -
publishTransaction: a async function for publishing raw transactions, + @property {Object} config.provider + @property {Object} config.nonceTracker see nonce tracker + @property {function} config.getPendingTransactions a function for getting an array of transactions, + @property {function} config.publishTransaction a async function for publishing raw transactions, @class @@ -220,4 +220,4 @@ class PendingTransactionTracker extends EventEmitter { } } -module.exports = PendingTransactionTracker \ No newline at end of file +module.exports = PendingTransactionTracker diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js index 1a7ff5b54..31a5bfcf4 100644 --- a/app/scripts/controllers/transactions/tx-gas-utils.js +++ b/app/scripts/controllers/transactions/tx-gas-utils.js @@ -11,6 +11,7 @@ const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. tx-utils are utility methods for Transaction manager its passed ethquery and used to do things like calculate gas of a tx. +@param provider {object} */ module.exports = class TxGasUtil { diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index f898cc44a..328024925 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -1,22 +1,33 @@ const extend = require('xtend') const EventEmitter = require('events') const ObservableStore = require('obs-store') -const createId = require('../../lib/random-id') const ethUtil = require('ethereumjs-util') const txStateHistoryHelper = require('./lib/tx-state-history-helper') - -// STATUS METHODS - // statuses: - // - `'unapproved'` the user has not responded - // - `'rejected'` the user has responded no! - // - `'approved'` the user has approved the tx - // - `'signed'` the tx is signed - // - `'submitted'` the tx is sent to a server - // - `'confirmed'` the tx has been included in a block. - // - `'failed'` the tx failed for some reason, included on tx data. - // - `'dropped'` the tx nonce was already used - -module.exports = class TransactionStateManager extends EventEmitter { +const createId = require('../../lib/random-id') +const { getFinalStates } = require('./lib/util') +/** + TransactionStateManager is responsible for the state of a transaction and + storing the transaction + it also has some convenience methods for finding subsets of transactions + * + *STATUS METHODS +
statuses: +
- `'unapproved'` the user has not responded +
- `'rejected'` the user has responded no! +
- `'approved'` the user has approved the tx +
- `'signed'` the tx is signed +
- `'submitted'` the tx is sent to a server +
- `'confirmed'` the tx has been included in a block. +
- `'failed'` the tx failed for some reason, included on tx data. +
- `'dropped'` the tx nonce was already used + @param opts {object} - + @property {object} opts.initState with the key transaction {array} + @property {number} opts.txHistoryLimit limit for how many finished + transactions can hang around in state + @property {function} opts.getNetwork return network number + @class +*/ +class TransactionStateManager extends EventEmitter { constructor ({ initState, txHistoryLimit, getNetwork }) { super() @@ -28,6 +39,10 @@ module.exports = class TransactionStateManager extends EventEmitter { this.getNetwork = getNetwork } + /** + @param opts {object} - the object to use when overwriting defaults + @returns {txMeta} the default txMeta object + */ generateTxMeta (opts) { return extend({ id: createId(), @@ -38,17 +53,25 @@ module.exports = class TransactionStateManager extends EventEmitter { }, opts) } + /** + @returns {array} of txMetas that have been filtered for only the current network + */ getTxList () { const network = this.getNetwork() const fullTxList = this.getFullTxList() return fullTxList.filter((txMeta) => txMeta.metamaskNetworkId === network) } + /** + @returns {array} of all the txMetas in store + */ getFullTxList () { return this.store.getState().transactions } - // Returns the tx list + /** + @returns {array} the tx list whos status is unapproved + */ getUnapprovedTxList () { const txList = this.getTxsByMetaData('status', 'unapproved') return txList.reduce((result, tx) => { @@ -57,18 +80,35 @@ module.exports = class TransactionStateManager extends EventEmitter { }, {}) } + /** + @param address {string} - hex prefixed address to sort the txMetas for [optional] + @returns {array} the tx list whos status is submitted + */ getPendingTransactions (address) { const opts = { status: 'submitted' } if (address) opts.from = address return this.getFilteredTxList(opts) } + /** + @param address {string} - hex prefixed address to sort the txMetas for [optional] + @returns {array} the tx list whos status is confirmed + */ getConfirmedTransactions (address) { const opts = { status: 'confirmed' } if (address) opts.from = address return this.getFilteredTxList(opts) } + /** + Adds the txMeta to the list of transactions in the store. + if the list is over txHistoryLimit it will remove a transaction that + is in its final state + it will allso add the key `history` to the txMeta with the snap shot of the original + object + @param txMeta {object} + @returns {object} the txMeta + */ addTx (txMeta) { this.once(`${txMeta.id}:signed`, function (txId) { this.removeAllListeners(`${txMeta.id}:rejected`) @@ -93,7 +133,7 @@ module.exports = class TransactionStateManager extends EventEmitter { // not tx's that are pending or unapproved if (txCount > txHistoryLimit - 1) { const index = transactions.findIndex((metaTx) => { - return this.getFinalStates().includes(metaTx.status) + return getFinalStates().includes(metaTx.status) }) if (index !== -1) { transactions.splice(index, 1) @@ -103,12 +143,21 @@ module.exports = class TransactionStateManager extends EventEmitter { this._saveTxList(transactions) return txMeta } - // gets tx by Id and returns it + /** + @param txId {number} + @returns {object} the txMeta who matches the given id if none found + for the network returns undefined + */ getTx (txId) { const txMeta = this.getTxsByMetaData('id', txId)[0] return txMeta } + /** + updates the txMeta in the list and adds a history entry + @param txMeta {object} - the txMeta to update + @param note {string} - a not about the update for history + */ updateTx (txMeta, note) { // validate txParams if (txMeta.txParams) { @@ -136,15 +185,22 @@ module.exports = class TransactionStateManager extends EventEmitter { } - // merges txParams obj onto txData.txParams - // use extend to ensure that all fields are filled + /** + merges txParams obj onto txMeta.txParams + use extend to ensure that all fields are filled + @param txId {number} - the id of the txMeta + @param txParams {object} - the updated txParams + */ updateTxParams (txId, txParams) { const txMeta = this.getTx(txId) txMeta.txParams = extend(txMeta.txParams, txParams) this.updateTx(txMeta, `txStateManager#updateTxParams`) } - // validates txParams members by type + /** + validates txParams members by type + @param txParams {object} - txParams to validate + */ validateTxParams (txParams) { Object.keys(txParams).forEach((key) => { const value = txParams[key] @@ -161,17 +217,18 @@ module.exports = class TransactionStateManager extends EventEmitter { }) } -/* - Takes an object of fields to search for eg: - let thingsToLookFor = { - to: '0x0..', - from: '0x0..', - status: 'signed', - err: undefined, - } - and returns a list of tx with all +/** + @param opts {object} - an object of fields to search for eg:
+ let thingsToLookFor = {
+ to: '0x0..',
+ from: '0x0..',
+ status: 'signed',
+ err: undefined,
+ }
+ @returns a {array} of txMeta with all options matching - + */ + /* ****************HINT**************** | `err: undefined` is like looking | | for a tx with no err | @@ -192,7 +249,14 @@ module.exports = class TransactionStateManager extends EventEmitter { }) return filteredTxList } + /** + @param key {string} - the key to check + @param value - the value your looking for + @param txList {array} - [optional] the list to search. default is the txList + from txStateManager#getTxList + @returns {array} a list of txMetas who matches the search params + */ getTxsByMetaData (key, value, txList = this.getTxList()) { return txList.filter((txMeta) => { if (txMeta.txParams[key]) { @@ -205,33 +269,51 @@ module.exports = class TransactionStateManager extends EventEmitter { // get::set status - // should return the status of the tx. + /** + @param txId {number} - the txMeta Id + @return {string} the status of the tx. + */ getTxStatus (txId) { const txMeta = this.getTx(txId) return txMeta.status } - // should update the status of the tx to 'rejected'. + /** + should update the status of the tx to 'rejected'. + @param txId {number} - the txMeta Id + */ setTxStatusRejected (txId) { this._setTxStatus(txId, 'rejected') } - // should update the status of the tx to 'unapproved'. + /** + should update the status of the tx to 'unapproved'. + @param txId {number} - the txMeta Id + */ setTxStatusUnapproved (txId) { this._setTxStatus(txId, 'unapproved') } - // should update the status of the tx to 'approved'. + /** + should update the status of the tx to 'approved'. + @param txId {number} - the txMeta Id + */ setTxStatusApproved (txId) { this._setTxStatus(txId, 'approved') } - // should update the status of the tx to 'signed'. + /** + should update the status of the tx to 'signed'. + @param txId {number} - the txMeta Id + */ setTxStatusSigned (txId) { this._setTxStatus(txId, 'signed') } - // should update the status of the tx to 'submitted'. - // and add a time stamp for when it was called + /** + should update the status of the tx to 'submitted'. + and add a time stamp for when it was called + @param txId {number} - the txMeta Id + */ setTxStatusSubmitted (txId) { const txMeta = this.getTx(txId) txMeta.submittedTime = (new Date()).getTime() @@ -239,17 +321,29 @@ module.exports = class TransactionStateManager extends EventEmitter { this._setTxStatus(txId, 'submitted') } - // should update the status of the tx to 'confirmed'. + /** + should update the status of the tx to 'confirmed'. + @param txId {number} - the txMeta Id + */ setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') } - // should update the status dropped + /** + should update the status of the tx to 'dropped'. + @param txId {number} - the txMeta Id + */ setTxStatusDropped (txId) { this._setTxStatus(txId, 'dropped') } + /** + should update the status of the tx to 'failed'. + and put the error on the txMeta + @param txId {number} - the txMeta Id + @param err {erroObject} - error object + */ setTxStatusFailed (txId, err) { const txMeta = this.getTx(txId) txMeta.err = { @@ -260,16 +354,11 @@ module.exports = class TransactionStateManager extends EventEmitter { this._setTxStatus(txId, 'failed') } - // returns an array of states that can be considered final - getFinalStates () { - return [ - 'rejected', // the user has responded no! - 'confirmed', // the tx has been included in a block. - 'failed', // the tx failed for some reason, included on tx data. - 'dropped', // the tx nonce was already used - ] - } - + /** + Removes transaction from the given address for the current network + from the txList + @param address {string} - hex string of the from address on the txParams to remove + */ wipeTransactions (address) { // network only tx const txs = this.getFullTxList() @@ -295,6 +384,14 @@ module.exports = class TransactionStateManager extends EventEmitter { // - `'confirmed'` the tx has been included in a block. // - `'failed'` the tx failed for some reason, included on tx data. // - `'dropped'` the tx nonce was already used + + /** + @param txId {number} - the txMeta Id + @param status {string} - the status to set on the txMeta + @emits tx:status-update - passes txId and status + @emits ${txMeta.id}:finished - if it is a finished state. Passes the txMeta + @emits update:badge + */ _setTxStatus (txId, status) { const txMeta = this.getTx(txId) txMeta.status = status @@ -307,9 +404,14 @@ module.exports = class TransactionStateManager extends EventEmitter { this.emit('update:badge') } - // Saves the new/updated txList. + /** + Saves the new/updated txList. + @param transactions {array} - the list of transactions to save + */ // Function is intended only for internal use _saveTxList (transactions) { this.store.updateState({ transactions }) } } + +module.exports = TransactionStateManager diff --git a/docs/transaction-flow.png b/docs/transaction-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..1059b60d8bae07e4a9c683ab67a702982635226a GIT binary patch literal 141125 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¥bRY0IIi;RD_>l)<(aVz*_;Kf%Tv6-R z#CJ|urw>~X>C)MCs_Rck!5nQtg&tm)kj7SUtSl2n$Zon;>pi`VQ0C<1bg8JoD~I_< zSN1Cjl7P0xad&u)JvBONaBctK@`Kf7hX?5B8J?sHlgEc=KqJlRx|@DJUM|4f@L5Y^ z$8mUHVB+B^JJ4$#(@m5rD{6VE&rPy^yop-}QuO6Q+O_QHaO<6fcqCav^J&DBpr(xc zrc{L!5EL8k6tM3z>Y}jdwWRF~!}I-Kpp3c^xl7e8x)Tg)l&9^ayT6f6=Lvn_u}@B< z_X>Qt-?-g*WxL`NbTcmt^nIl_E*;%z2S7QfF6|sj+@}W@XA12n2=6v)XTB|)o7ll5nVH$4qzaOHjk@O_ zW0tI6%F9SNrz3qeBO{n>AujXmNpbi;s+J7b2VjH#6q}2AagVHV@7MeKpXjM4P1gQg zISCEXiO8eU=bNkkhGNxMJ-?f?ZVjT5;(Eyh@I?|W;}?MTRB2ZeFESLd<8aq#$sFUa^TuQ>B#I`RavP+)^pQ9fHAYgcj4&H4+rQ~4O@KJ-8oOr52K|q&X1x z$uYa!8HJ}}W_4k2eD#~w*EgRdd?#Ia_$15iMA7d+>u`EVqu1xo^K3)$GI_*l7fc$S z1p3vP6MVzPNsPRdyhU zule9hs^uN+`5udZBtd^Zrlb*#PbBXSA>kED-&`GIaS(o(YrhIlkv}Uj0;#@Fcvs~H zb_RBJox9fi%GBex_>>M?+BV);y6k0A`1^thsk?wR^{f}I^Glr+`F1aIxXIc!P{Q!hH_aecL^~h z@-vmc1VO+ta3G#FK~;S5)+7 z+x3-)f+mukMyJCZsEO)kGbWmsH-p~nJlR6?Yt@%{83M!pGBair67Av)j_JQo;GjlA z5-`@-T-nC>Gqt1u6*q@eG)m!8+i$x!Ca2o^V0EW-UJr+6zz%X_+)!|R-KlMla)BrC z=xh(AHTNJ91|1YsIi>jx-5oEGaK9ibLti^{h>3KeL$uXubaZfkY_)R2kbmZ` z^i)T{6mW2^KUR+&==&031VTDQ>Dvk`o7QMTRSS}hE*%ZK%T`4v#VUTlXWOZHzMiB*NM*^Bei?D=(#%&{ z7>Y#>ja)}Qro_4HYqwGHBD0r8Dkjfv9G0dpC6(kQeMsOIukoq`0b|G;lJh!5XByQk zlgec$o}P^O!sP(dK(*cDtBu(ngH*Argv({1CWrgt$XoI>{Jm2;>e%eF_W0kjYPMuBd`02)P{xTpz!;@o$eS zrYJ<okENa{ z`r6w=&z_X@Kc@bC3Z=3n5N+vXUgB~^IzAj>@Wydw?u>sex$qT|7$X$RcRSdT+Ap)r z52e!d(sIL1e8@sTo6w)y#A|vNSj@+IM09q4S=bGkX8T=tczw3GxOBSpkl{DJ8}}wq zW2J!ZAz~=G5P?~9{_d^-mJ|8wI=)RmmUK?l5RcZN!}525o$F<}LXG^$1R-Q!&9$hk z(wm-;4)GHrmQP42X2X(7n!ip)m{{LZ$X6~Mal?irtk9#T1`z)g5^B)j=10P$xw~ht zi+zQHCNGNl5o!g=E|f7^;MI8R^rJz%_tZOCe7MoKu1CXM1Bs6%T}=_G zwZ&y{p2^St29L7b;gkS!lDFG)p{55x{Ug~Q153GzvKA6i$R5GS1YCk~`Gcy;s@OxR zQk$7cQU-7GMg#Zo8`kq$zw%qP+(VamoRpi0_OFifmTxusT z8lT=Hce+eZ-zq9=RV4mGhgE5u9KRMU5xvS>FG%ZlY4dj4CMR%2oN_|hz~Ww~bp056 zM;&`RnJhL)6MQCyOcA?x?8tMs#gWAmHD$D3WY?%LqnNGUHaABuUn&s)F?Qgsw5SS4 z6xC3Il0QzJEb8E>OH|)xj+=f?t?9&0I9bPsfE#EMgo5U_eXqTjswIKf<3^bt^_QK7 zMYyV>)5CCxa_#fc&ZjuVxZKZa1+wnor@NZ&-1{oLCLL^061u@htbUJ^t)f>6%b_)0 z2)4q6Wy1}Oz-`n+0+ro!Mofih=CPdjUQ)d7jmdv6FvP>)zH*7TyS$+BVDy~j|DcjL zr247g_jDmmBSxII&Da$-A5Xq0g!ozFFa97dRIN43(f(yU3Lj@EA9*Nw&Vs1PuI)t+ zeCAd@z*Ph>?sk3i_GubWDL`p~quQa*fOH$^9m)~p@0w4g1vu9hJswF@c&*fl-S{3p z_|d@WSqVKq&&De|%;Gr;ZD2?nJSOX{u}HU#FbV!ZO|33AlRus)5em?G@))_X5SPXu(<0q(}ndRWwiXdAf7$afp*;ik-;>d0F>gOJ9h$YEq2>#h| zB4fzp>%e+4?%Ob@1Vs^;w->JVI24B(F)J2&HvaC^GT;wtGyuMWhuhNh69IRs1kCL8 z*Fyz@>n9(;ln7-D`+zOc_d-w4@}eJ$<`2fD$)e`6%ttv-e}|sP!|Qcw-o+GmiOX;( z&FJQQ4S#`82o8+0NHneG&5_n4dofoHMZ6=~Y;*54m} zKTeo4O-Ib7<+X@Rb-RXqnh?*d{W91}_|OOuP5Z)OULim57gVi$qCLc&Ht+pSut z^xP@3{qB5ArN6qvHZMtovO-E?*P^lo%gC4qcNcg_GU|0LfT^YgAqZykvI?qU|KkVd zsCdM4cQ_iltq>9Wsl%w#;T>7KTw&1r=Gz(U?V{&k@=M*T!t5@8Jpf5u)vAY zhgT|=t-NlIuKBT|fNg5^{0osR-J$G-Yu$&$iTZYqrx`@+&!9cMT7{ckMCvpO`3w_W zv;D<6OH~?GkPK}fZL(LLNmR2j&HU^wwo>s&#*@$QGxTr?s=cvM@7Bx{R+Vw;VRty`q~wB^uCk3X#*HxXc(=%?*O(8Bp6g5JN=B2 z!zcs&RIRgrXr^+A547aeIqg@4!~c>A<0R}nlWgpioYM>PVelhW+k-j&^-WosX-oVXtAgo%t2BXJKSN8Y;zR!XU|@kYjKusHrUg0q?k{X}F@YF(+9gxg zOVC9SvApx(iv^)q^3{~X=%+{(ldH#rHA25MYc4X;3^i6n;V}M)LCE~m$ls-gySvXi zeXmUm&YO?8@R+vmzg3!8KcsvJ(zAu-Tqy3#Q|QHSU22=yL(M>6aW6zAB6f?3-g`O4 zBQ>Q18bw%PdNP045QuAsvU(iS(!OdG*Ua3kt>ac#q%7l;KhM`{Le??%%b#gg|H<3o zNU{{6x0p2UDK@j?d~K_Hcv`i=%Ow#?{ykQ>n_IWRz1ITISQ4_lGQfQU7dL^%cr%V2 zOeH~V$XfV*++sa^JYt=C0Lk(@e1H>u@~iYP49qnInp7^>C49+kGQW-IVk~hK zOTgPU2!8Hhtvp(%QE@_c1W+IuE_BgR01Y}`@28%xbbFUr65rztxx{~wyk4O(IXrdt znDpzUqM48F*{wRr?ocVNu@V;h=(JuzDGu)}7>qx3oZncL{KBe9n7d1j@we{#&*?rL z1p$U5(EH!2BvxsqNk-4ESr%I0+9E=sIRe^hC-Q89DjWayQRdsR-e?bTg)pX^eKcX( z`=1rMV|mhZvgT}>`?dtbM!5jLro<7B`wM>v@#z9eyfAmsb~`n`FCmLfYh_GYJNCt<+YlG1 z_iU|TU@e4d%`9B}3?S^FZ~ADQtO>Q?Ot5#F_}GdoarYMZ4PT5&G@3AVVe6 zLaQ=!8gW~(UXX=g1$#0vd-%;pEeRrBJZWh%&g{NSvy|AVYHU+juxSu3c`zTDc-R;D zee$RtJ35N+$Y-Q5n2%D(pGrlEGhVv7s34OHaC)|mLK7F;lpm|XJUw5$ulGe38-g$! zbuabD=;hi;LR*db`-FP|xwb=6tfNp%;@15671&nqWXf>Oj#qa3C~-=W+AI7AoWV?U z3TZH!f9?Pvr$kls)%-EogZWYVyS5lH>!yV9M@9dX#JBg~o$23K7id(uD%8PPah#v= z<=!&{t9*)Y{l0r|YId#WBd;u0qlY}i9s$47S)0YEgt~71$KbRC1QIlxlE*&-_o`Jv zx{yRoBu|TpR_1#ZKZgRQ@k={J=DdM(XOHc9N>(e=>|L1FrG{zVBcOn`Ikt^vupB1a zn|D7eM8^l&!~W*#V#Y8D4831+b6E?trSBw!C(vCf^$m^%h%wF4mzXv-6&Vod;vb*a zFHlnp;yxq$;TBS0xfSla#c~nYf4e%2U2D1EntMmzjuZp;ZGk#nJXAIE673V=;YLQ* zpF4oo1-=s+_ODhYV_(fxOfvY|)L4tJ(b0sn73h~p$m)Bd{?Ha+C7#>G8}POd$}@LZrfKAawcxU@a3DL*meQJ zh<=v{wXifvLwN35YMM3Y;`DeOXmryj6>J-uE5in@#{&O}mTdi%65C+A;$Y~)g5&<| z1efF0a&um^2W&`ffKk$INGC2!G~Pi`Xbo}|rfAj?Hb$^*7jwt$@7{?i`#n)5cSK0# zyiqE6$gzw)HMwqqDPrioF6|u?&A@yAz0YBu>e~b)besk47R!25<_#;swb~?d0>gN)%3+>A87{W78_|-7oi2ru4N+`jM z-Y`lSeAXYh5C^sP$Iv@?5w5^mV{9$7rtnj4;Zz|7+4{|wm{Ou65z^&%5=rQh=}WA* zTgPFq?hrb8sEre*8p#!-a{R?lbVxgo`)Ye~4Y)g-oVmZKq5PHwUIKKyzIi6#ScD(? zSkZm$f-pKG&e>4_=(I;daRAmDlM6i{yAKl*TePOolK=$2F2hO-!)7< zsb8UZ!!gmP0JEc^|NTQUD!I>yd)NgdqCB#XS`ESm%S4}0_f5;HV}9w&NG$ja^0RE*uqa?x8s?^2uK^>k17@gB z=ekSR=O;U4kvwg@5}OqsuXX5qn@1e`CHI3syLh|imxY^|S9P??B5$bAL}E@kCDC@46gkCz{P@f8Y6!spxby6Ih}s3lXeN1aYwb9d$~fG9o|KJRCAOhkMq0RJ%k>I% z@5B&0lsTTbub*dK0P1dt{waZCF#L%UwC>8&qMXo%Zufg_?W6!S)%7MuA5+yge*RJv z;wvKjHWtC3PA^FdpkT%*U8BZ0 z`}k|_9bY}z!6>f$#37NB1ChU|$1yNg)J6C`wA#@|^tFvc6MYeOOa%05$#%?Q7|z<9 zj$tyU_l_-V4-UiJ$_Z(f>oxM-A%Y-5Pa)fl7~;vB`))3`>M$82Khs)xr`!r%KttdF zc3K^P*b}`fR52b5J&9*u@GO2ve3DDt6cUw{5@-)Pd{d`(t8A@=B^um(^M0KmfJdHa zALNxo#*ix$lel)pzA~|3a^VRT7oH3cOkMQ60%4dvw6N3-iFqg~VAsKKAdnK`ec1o9 zLkbbOiZ_F0m?`|&akNYgdN|RQ;%H=XUMbwgj#pcU>k>Y$aO3KWf?*7k?R{RdF80wZ zha#7FP=QtQPYFH0+gJ<^)Nj9SE&6S61RZ8wX8>7fNM(KQ9HS(fnS?T5c_l>UqoUh4 zQ+R2COB~{n8Z*_N+L9$lhNHmSBG>NM^O2|_Nn#`VQAVdd%=&BtR7NlW(B1t0dO)`6 z-ZSS}(?;KGj=(=ah7S%nD^z>Skk+$}aXt07JT5NNSP)u|Cf$2+(|xKb|7yJVJay+m zIK*LxJF;UBP02!4t`zGOyw>%7#TOoUcAF=Z(wi8l!bX3!{zEoZY37`&ukP?PRiyI1 zpZyUA^E%#W*agjrk{TL&{AsJqoo7xNct=_|A4?Ft)hew2b%`-iC|tbh8Twf~{%6f! z1W?5|e}GR3h&S)Xho8s3mH>1Q;`_E4fH1>n6mXgqV zD`^Tr+Zs>n6G@YikSLnsLt@|F8>b|9B)ERZqLTeh6CN4rsYfFt0~~ku3J(h%_rHzDS%e@ld1a|j0*i5Z>{MqsVyMN%JTQ)gF;>{kB(c$XOoi^gR=zeV< zM~1oRS1r9tuQA5j;FclA@CES`z@xT}EQfyE;eLha2@M_9VNRbD&Z8GxpUwH`tVMQr zzMQTaJLcU2*jhORA_!+ooFEiBExVPq@Mm|&WGtHTTy57^ z&(@qW)H5h?E|!6br8w07;Mj~4XC5~RHZIp5TAz0II+C%~g?Se3sW6BCBLbDQV1PB& zbDX4&7Cx^@rVO^V=i~O|$AumSz$-rXodW5`#0Ik;&3vuQAejJf`_K3{k_Z(t+)~y= z2?oP@j_97BXhC0lDJ=2WE!1@!oIr_y_SVVm^X=6d3`r`!6xp>2YNP!Nb`8M^CU}KQ z@z3}VUpg|Z@=VHmQWN0bE~0=Yv4tVkCh4|*tin36_K0Ylf>&!8KfJ`>Bsw>zE+LO^ zL7w^O!suD5m4F6y|3?4m{=*l^Av!i7sba!sG*2`K5#p*Ja&%-P(eRkaGXQr<;Ms;{xEYpjUxA$%q5 z(T>m{%qZj~L&kPK^%k&-Mxm6iSSm!oqmwmW+NR zmTMCOj|Nv?Vnn2XUUKQ`p#uF9$f>OznB*77U*>qVGuI1N9KSqR1`G#Qygj?s$5Y)g zr%tF6&4MRYRmgHJL5&C4UKPEuC%!%_7gXpE#y--Idrg*G6--IgCM$+;A4JC%rzFc` z8(>o0g%K4?80WvmI9n@oUG(}Wa93D(LQus5eYEvi{R-P-s@((y2$QaI0PY*2Afuoa zxZfNG3G|mr1P}4dSUjDdF(v-eR$O4cnh@(@D1nw1TFuZ__D%&~Y zDBtoi1?Tm(X0v2hJzbDz+r+_GfqAH%HolMJ{(Dv)+s;vqXy<6L45GY8S+eyL_c66f zm(vZRFg%n)KL{DjiX~V8=`TA&vh?ApMh8}`7<0>;7OR0${8L_x(%Bd#x?m*c@?%%Ol!EE6F#BZq2DWQb;6f zAKziX&BLYe-gz1j2(Htn@NN7xY2Xu!Rvt7Xv0YZy#WN&ilp>P1usXOeqoslBfZ>QP zUYY-ZtP!nqm>@PO_L+FLgE7<>x88GXE9;Okp(<3*Pz?RtA(?*oOBat)1^o;s#0r&4 z5@wyEm-@?+UhOZ+m(S(z-N%x%9^*{^kPPTPWG=DBI(WIY%PFzZ_wUO#RWLN4eHQWb zI`0uHytB@JhwB&h=tx0GQ2dPq{@ZVb*ZKgX#l+O2HKa(4n+f~3%Kf|i z3b{ttE#u@BGJd5h5KQaEud?W>XS=b_B9+tZs1hO=O{#}OaCW&t)#A2^Nj@*Q@#idn zQm68SNkUNO649BK7F4f#FPn7FI*H}-eA5zkBgh_uFq9Umag>P?q%k%J zl=~#+>SRmny&iuE!H$ZNa8Y*#Uvjd~|Da@d=A@;iZB;<(%!L>IzHgc!M5ci`@BCY7 zqr?Z%{aI#ygJqo{wRjdW>lPkL?3VIQzRzZ2BV4UZVq++7TBYOm0<7FW$pl7Cx|}bM zIRLcx6RkZwQ}T`6k*|M@XuIY|#_zq1RhCflWu0@8h`*vCpWtl^PmttY<-_-AnpGCB z4*|~B$4Cr6qZmgcb`Wp-+ecp&?vt)nJT|KbtME^VcIe&^V`ss(IB_Kx8SmyE^uH!I z0KjN{mIAQz>YZb!rp&|OzDjF)a@pmdQP0$J6OzOCRW&zf_-M*LJ#~!J$ zdE@wo>-aFCuL^<158@3G!z|T|nsP#({)?4;>-x=cbBWlX^Z5;e3unLd+s7`k3CE&?(VuK<{8A;dUs;{ZZOIP zlEv|ageXT!dhStc-{aFaq5PH0z^Eu7r5h|odkO*Gz1@npbh*7YrhEUEDoiz!-bp@y(x1 z&KjdKG5Fh>yPHy)M6ZGOyT7JbAnoYOV%)$Og@1ItGe>%P511HRn?_)0Ut%2wnT9!s zY-VhCp*@rh7q0UabRDe&HriXg9Df1onVA+w{XPiJzrKATIvONF*Bd}ayacFsJCtOc zbX*P#6=!odQ%LXDQM1iBk+}xD3KG%BN%n)#=S)8n4o?d7`o}MKY(;;aG>vQ$T ziWmNk4gi9)LrO-$Ku_}qrl=e_22^qUmaOarkb!NKsA_zFB{woAAFx0q6~Hs6$9?B0 zKjWa3(|@u)Fs-7LOv4lY>&{*&d%a%kYkk8TS)UoikOG~y)+I`kB)+jt#$(7JVevVp$(Zg0%OCFRaz2vqxe1 zGr($W(2WIX-HH6cTV!*)9E}OhddW*9x+wL5;2^)h&v8U=flw2YwkW|ilREkWrOW*W zDP$?D=QkT<$TY>QiSX86&4ctRFAXbReip3=9?AZJXo2KmpdZ zrDVcpfc3E@M?E8Q^8d7ap;Svm6#^@3JS>7>?w$&zGh<_}x>RZ=VT95+LERAOs&?nc3+R7`e4=U&4f+5FRn=e%< zL;W$-+kr`-dse6h)Ah;;!W0^qml0`NnfKe}-W?eeu z|34mNgriA;M*Y`r8$PHn6(Zk7uzaAoxW@c9|N84bAgn-87SwAc1_67ljp1@+12Et; z=AparY21mJi2r8g-->R0JVXE_9ti%Fw157Rdi#6}1x7glG7oQSsk);eZm*|212wJA z2Lj1+lN4G;FlN1-DK-vZC9^wfI&8yhKi<%1OrAL2Gu$@wT!F12NxE1|Fry$ z^;bIu<!{N;ZYa{J^CCAjdXWNH%K>pbNf8! zees+>-=BT$A3N5XYu+{PagP}atuV9@BP8nOb;PI*9oH6V_2YfqYq$>FCngl$@gLg0 zXvxb}-ev1nET|ub%ztw@Q2jB3AB`m-WX#QgIo9?AZXj@Su=|}IGZ!{vPwU5Hba&g{ z^BMv=9co0THQBTL$8{rK(mIi8aL>_qy#`@Rf4ma;+&QlzVh@xIhSpX z2P{qE9GUiV>?FYe1TH|f#*p+s5oHBF&W67>&IH(>5gl06HdHTl{N;ZQ?16~JX&e{( zUtT{A`%zmyjsi8+j}aTmv}NxkIu$(YPO$oD?*bmd*Q!sUO~ApG2~mt8Z~=qi?0Nl# zYSJpO?;{)m@!CVxyH5r7UeFnv6H6$RS4TIXgsHzB%-6`*5fH=YKZ9LG{xcQz|G{4V z#p+;vKB_+^69(xN3a9hF^78R`b5rID(@+^L*4`35$!RI(vpsqqLUakgGn*)pDS@7G z*e}C4UK>WnC+>0M?Ei@(Uv2L_+c-_CzLYsj^SVHY9xYqYBVgxHu1A-_a@n zw1h-3xhsZ7Ji=r<^-Pv%@Qhic;HYjV2&Edj*Lcg!$82yPM)Vu`nMwP;2)L`muLkCK zHZ|BNns9PCER*`3lcG^;9PQ!iJ-WJ8bR8c8q`{*n+-r#X(jglslxp>Rh|kF(TZeC# zc8Rdd_zxGVI>qNXla)YkLW~g5GNMflKo)m9u5|iv(}HWiN|-ZvK+O{M^6+078B_NK zXI87RHe@WbF0%*^GHy{mmmE#r>XlvPt&ovz`mXNcKoX7_GCxRBgM~MR+T5rA<&&aF zy?)Ual;;3UwqjcxDoKIK0|bR|x7)$Ue;!cbiHcfQD2dCK0wNXpstf_}^){cs89+k^ zVcG+nB+8jC4qMt_)ZofE#O&MNnw|6O- zPDKp{mfRfm|4 z7E8reT9C3n+iL}b$)oCk<@sXh+2vYAZewW)%NRoKcN{@GoN0R0Oc_TF4@*d+oJ?8e{h*5m`FI@T%MROnLT zZoXo_R!5D0wLu=0U|Z&rDwd6ZyT|5Y+FmjCV)69uKt2CCy=LQhslJ=3<4HU%Y6L-hU%wZUpVGO#o!m%q-sS;k$cp671u4k>FS=AAa}naAFZC|t5&M-o@I4x zv8VoRO$<1;vyL(nY>X&`5gn^HJRWBOMm;*Ob=|I$*#yG4YCNn=M{@5^BGgU}LzTMm zdQGk+Qiq3fQYc&X2UOI)Wg$Z5CFU(=vp-SeAy}yFqE^G|_u6=JYApCP~#xybtT_W*@f4xsv z3~~S(OZ|o={H@RDQ|0;U9J0VN<_YV-M1z2k58oo$7UtyjC58f+uFL^c)$j?rFik(R z;oYeOVp5;N+j+nm`gYd;T`y67$2s3904KRt^D5>E+hI@u^poUspqN%8mHoe?wY$)EudY@j9uDC>;=6V&7?s+l*R{^1n->xTs!Kq$g5;R$m1jLmspNY4D^ zBL|PcBa?4pq}vPegA@JU19k=YLx3BVu3;BTD_=a&LDhcH&XOnJydWfusC)T()fMQ~ zFk5u?y|5=m!H_SBlL;ZNh~pT?UdSj5YUO4!b>|0~Qg}AoK=;b*bEa=-_d>dzu^a_n zz&F2{7U#&(ejndZrP(b&m=mQud3ia$wKvd!Ko=$1QL=Gmbz-B@xm5YdY}>8crB|lS zJFHPQ^_8#txkEMfatm9^-UjYrRHuek1>}YA$>`EZo~CAiZoT8)3)ZAQt15#v`H{7S z1u;1eM`dnpw^768=>s-2V&<|=_jEEp$9n9KAPWa%DM58@YC)m)waxTa;qCWopw|JOw^VW{RkU_lU096Ve1zS z1*7z{9VY=)p0VHQ7)sD)ZQ0ss4}Vjp7Buh}NGv%6FGZK`;K%p8M5#P4mqe#_+H}l7 zP>!XemPH!WKr)ak{Hz)+8a}*;vnEZ?RQi(0=9`GmZ=#G5t((`efO+|4eko}N6yE}78A#hS8T&=fO)>nl20S1qn# z%pfkFAJeHGF==(kvqPzTl>%St&f}V9y2hh}Eot>- zXO`3i3*09IT;Bt+mpOZ4?Q+Tn+DV`xL>a36>f zp2ta``Sh5SRm1sx26Yli`R^G|{Yp%Qy_71VVGxI&p39BVnI13Wt=3Qy82*jf-5C^EJg==eKOei)35X4@4`*7-zt^6;!vFQ3=PYhN&Ap5u+*ojlek(Q!#Vf{P8nEWu(*8qF3Hx%=24pj25Y67*LHHSGRf7} zKDYnEAEW=8>M^Okty4;4Qu9!VvCQMA{(t#Mutq@;m^#j{)Ge(#g5{sl?)eAJ<*Z-| zSAQF;ozjNy5>-XKe2vhLZA#Zb(eRcWksd2YqMB#Du%eG**lH-4D=1pM<=yaEnTS*n z?$l)fUzOwDc}Ud_liCXM60hZ+v72cCCoj97|K^Mx{ZVO+9)`txFj+5nX~uig=;XlY zxE(2%PrR(%MkQb0tz|z~`!i~&oNF{!@yYY|qfJWx($ULxhN4+CwikGH)6b6`_ijRV zcbEl_o4*v!RU{rA%1Fz8b zR)rgS|IJ0Tsn+0<`KBdAmtX0gPTD5$TqhrhCrV=L_!AE877*6d0SmlOzraymh{3ar zCSevOqE+L(@bG+Tvnoj@6rnfO{X*q4=6Io^h;vWlQP)-%-W(I2!|(;F;vz9h3Q2ZX z$fb=WE9A}1&Qw9vxTYc)$ae-3IP_sMc)nZX7|Ju(M~CKJaRiu!YyIxQrelSQ+d3cQ zTL*4UfGXGRZDZWuA`V@F;JKDl1=zex7HmPWeM?{F*MCy0+Hs80PoXAo)kj3}&^Aq#;dxZlX-oMJlhCl2NmpCiuGmvoJ}IgDxIFu7)}a$)R*Dz}L>ZUnXCm zk!7`g8|X;SCHc~I{&3PvcI~#})POPsK^bp(!iPa?0ee93F8cIAX=aD|B&s&&yD3FQ%-$ zN&Ft$IgDD}Tnem|op;r76ejugEXJ$I4<$KwgB)hd-CkWl+QcS;_QQ`wLKIsia&klG z&PlspWdz*@V|m&^#UI9>)i5g?YFn9r$UxDbFE*r}wSNfq!Wx|(DqRckTQS%He$$)11;B4We# zQ`cj*=%9g=-&Xnh>a>qT!+YjJ z0{>9O!+~$nc(Tey?p54S){Egij_9*01}DU17gHq_2UUnTvl8Rjcl}Vq6-wx+ zfP>DfKC7{gWN8xVZXNCI<91>FrH+s&SrR!OyG`}zf{^&0g!Su%sqQh-&t zTeqZ2*PPHd>j;zrzX8>Nws$zy<=D$qJ2t@A;_rR5A6d#r`rL;A-RcJwh0AKK8uG!T zzMOLuHCiD#eCuDP-x@E`l^|V|1wcJf)m7v9`@|Tg6d^QX_B8%$vMk_S#6BmN*WmS% zd0g+PN|VQ=bMCt~4((`gM18vONU!$F_IuiY zi{gKHs+a~a6cItI#s2_Q)IREO<(*M%lZ?Alz6;pz)NOA3t3G$k9>glo%b)NN1MOpL zndc9w+euB?>I`@;hz%Tszzh_f%AJ(WMyKI)rsg*Z0!M>ODuVq2O!1s0ctJ{aU34f0 zQzD@VwRo{w1j>ynLDFwogOm-AVbhHVJ!`FGXggxr(81(5QpjGJWcFbY5Dc)KzY$Q@ z;b4^hu`^?rIGE}|Ms@Obsbek#U)S~i>@=UkbINb~DMcDwsV6rnYLf8C)ie?LT?Z4O z5ESDUpzqwU(+9E|(GWHz#_aS{-_w-C2jyzS9Q+UZ1sWxBzv>Bv60dDfAGCAeQSqVs zk!)o=O}`=|9Ew#B4mAqXSPnUgA6gTGJeq1Bw1!~q4z#*8UB%G!hNTYK|A%&B6@n!x; zTg9`c=8$wh>r3oj!bP|T+i2NhTyC-@WKM(H+Ez+rp7aADiJYzZzFltW4NDma+bh{( z6qj#zAQ`6tmrYe^vYhkAJ(_BsSNSaS@iy)n6o6%ctRFHz&H|l`w6=Ff{1k4%?V%O|dovXQ zblbK|aQO6_2gZ6Q4TeI~%o$DNM$HCynOWXhDWYdpRl}xJfncfPB*~7>Cm!N5 z#!fN2ltIM*1Hn=>Aw{VxfB}h0Kdoro<*s)qT)|eiHv%IHvWcv!qc3)3*O(2Mz7mZV z6jV>xZT(JfZBvow60M-uWHlh?aSVn{!(W}Bcd-A+=5AsT7he_ zr6?XjPU;IxwJ@52o^zj@s!}|JDF0edDDbkv|0lb3?n|{OG>e(F-wUnFlF}L3vP5Er z5+I{|O#-IyS9nlplvAEhe>mZs;`&xZ>!cI1IZ~t8nkI98%-4J^rzKkrv^B+EE!dpw zUTbSlr@8YvREcPQgY;Z}UudEKUdCs5Oz1^{zmq5Ux7tar>mQV9;iWF+3qU(shXH*7fk@Xfcll@f`fA6ZHX47&M_lve4wp{I|Gv6g6P~7)s$7w7>HD zf?^9BOB92&vc3YD!gAZtB1LEWALQBp54+HSTn~t<{?pX{^C~I=a5>hf0U9!}jlzRg z#6-Z$td|9a0vAk=9o?ztkJj#gt~U!N1qCAa5dAcM&p(IbH;eKkx-p_dPHSy%**{)Y>IikzEPLy7Dk2v?5m|2)zE z@?PMjFCDEc(Hp{XMNGpPuTPD=^|$@CKT%33Eh5>wWe)07!^mYW=Ni{s_sk#{khPS| zE&ZjUSW2h{lwFow?90@{EcULgL!~7D*D;X6Jwh>MG|r1gsO^Ri_`H3Vmy;w} zjoQdg_ORw57=QJ9q~7SOg`VkWc6WjQdG7$=dp`**Yj< zPqn__fK9g2#@_M>FdOTT)V0G=n(r4XpP)LVnGeE_%pVlv0 zkSm$`!=_#qnim6w9?a6|OCP=byEIvZbsFRpYUsBfJp~nKERRcg@oDa0rL$^*6TA@P zR;mQ&Jvb>E@SzP!=D)jFUxR@?M5VX4;XMg2r7&i`G>d;A)Yn!2U|NuZ1>CWrFOTZq z?UD|#a^nQ1H8jLvxn-=!=Umnvz@$aje_Z;3;~?s18M3|xD3N8IcyRgm3GC5dm|XX> zu6OX5{PN_t-~8!O;J~MQxxOSnwAXwqKYm){cC_)9iq> z3cPRkT~{ec;R8Ql$ma9A%>FHGX}Cy2(GebRzY(cAZO~yFkF4mCl<=Ja1Bf#H{I}SA z{AR+*-ZQhAGy@BMq{3=iC-QGAP9!!xW*J6KyHGaDgCKj)I%jztk*^Q7_E;Sj`7{_v zvX&_IV!*7PT+ph(Yr5Hj^&eo$d#~|CLFV@4G8GHw%r5cjKKDgu>(*nrVSOv5alIcg zsafP67rM*N5Sz(FR)aBD7o!DK)8US}5$c0QcO+ZqvR-TKJ4QkwfmFJxH@u9EZ7cgS z_qZ})H~dN5f-eFRxTD&nta%(oMC@-PJ158Go)}m-LqK=obkFc;qG$gY*;azY=DD`} zPiHh_+fxC~kek!P5+L&NF?{Ui_Y<@ReNMhgy@!)KMNgbct=n5xX;Ph|)MM#{aWC8Z zp+LnVI|YLhexpuq+G#s2BZgs#!dE8Fa}*$A1z+){%Hk}dMMe0?=HG9V{Net*6MZJW zu8YVc!CTIa+vfzRLx|*Vcvu7MR+G{dMgl^*9+!NRtRakzx%do_V+-!=L##(Awrp%d zb*El0j>d69vw9P&b29_Xr%iH>(uf~w<8oC^gzpqIpC#zLJ;^>;*)WbBGRDhdV;tX}MMG zTr7=rSgH{kYgSw2Ooj>Mt^WFPsy%&+9F1GkbxNMVXN+~cWrtPUB~3NGlUJBgi;|M{ zWBfSl3A#Yk)lKiwSwqhHF~@u+-&AG#QoWN(!&KF5^zrc^lx|~?D=0VD5M|T;1LEdb zWpGER2WK_~v}F{UsyuQ*v`2o~Tu~=J>v8L&ls6xZpO@K|T5=PZqJ9Kdrp=xha&zJ_ z3r+^;4?4TqFplmzWs&0vNP+0bcXciqWS#~$P%q-|U3-7-A6(2Tc_Dzo^_35e_?0L6 z)`;SD;<(3YW0=WJ%bLmcMe68vBP2x*Uwa@jFi6BZ!=)p|Lu!XD%DkAMw%w%{^Diag5CzQLZMBpFt(V26p&jDNFaSOB8NOJx>q&n}=!w zST+_>`m&Jw^Zkp)ueYQ(-F7!N6ST-x@t5P8hc&oGF&&ULr=B<5?>VNyzWEO)_J2a0 zVv9#eeNvVNQzv1(MEAU|khZq=dhQd9%<8#og@9*TTOps3583FANQciQsf&LtFkR>b;kol^)>m=hX#2D|`2@=roT-jBQN7J&g_BIRc+ zPA8&M9?fp+l^ij{B9X>oP4W{d(^iC{YG^eY=RIuuXWV#6AA>#g;B98vjatU%SZw34 zU3&=44~^vCnEfWvtTI<=!SYBJ>x*H~9B6<(8zkGA!X`&see3GVFx~x*I4H7*Q`J{X z+ndC=ukm&v2a8-CbNkJd2I-U4TBfE_8lX!`4o<7sD>zPl`%-(9HT*=?4jUzIx>%7RWP?YrRDmAJ@Vdv8rMD9ii0QH9G2h+|oS5HDylu zW@wk8{F^U<4Lk=0GydorO$d2j+b`VIn$c#Bwual3X1MBf=!*Ndk3H3H{XSMJE7A$4~G5-)D+6@+>^oduGgHdfi** zp~9&pBeiYW2hg=d=ibY>iKsJ}zi|pNr5Mf3XRp)x~~8uXUbW?Sx&P_6a6UAeYQE7GhUJbH0TH)ss)OFk;`s#dhwQZ<#u z+aW4XMj*l);#4x*Y-3hd&T)Q4df84&tZoyj z7E^zCvb1nJa;l&A$2+JLG=I%WjT2&c%UWWX$(%ZVN=~g+_OjhOn(}=ap9HBx++u|R zR4##eg`bLYL{XRjAc8pEo&Z4SVq;y4G%$45iK&=>xBUtCnLw-*lcq z)u60EkM|6Oyinnk_|l;8vLkVC@JuQ!H@LmZV(Ph&<#rrT?u-i2Az|+^xgR*W!@+@b zMB^Y@r-Wnt^ugaYD(D7p>ZBnSV#v!-D?`DAz?Hd+=Ts0(=1Sr)bL}*=beNKz#njlO z8F{ad3UUzNe$@F5aeZpbiYBl7EvcIG?%=81R}qX2g@W#Un@^(+WV(=6Stq*mATdIb z@cLg36{9w4B5BOVgEldb$59Kq9q}5~B@=mf$~p&?3}i(H-m-es<;!~bzE+?zKk~<0 z?paKYXf)DVIyoERrEeh2gwEFurn zV7#T%%(18GbA7yJRK>1bgwxqmfdPZCU^VDRwA2tHOK}U9zV}OY*7TNh+*kaYqlc{R z31KJ|>QJa+j(ly2Dpo6lW_8{+uZLa*H<9`K3BnV*$bi99S@SOx#_!L|_GbLOD>`b# zXLz~=R41JRkZgJJo%pnyZ}3&fLy2Fyo{v^7* zYwiVXCl9WF??l!*U2>sDwS9m!Es)$)MLqfIJJBAXe zJp5!PLnDG-px6vLtK7@B9Q@W|n+LX|Qo2yI$FiqmO$Tm7D*IDStxuBt%Wi;mLKyX?)uy)O!Y2IBW+NQ&tb{sPwP@ z)-1`1bRj7jQy@$@mCcC8)mdDsqPMj51fulCAW=< zr(7Qerm*F|&)n^6e(H8(_?3s&*!X(o*MkoyNn%}&H>b60NkkhXT~!#(I}hJ62bisw zH6I7O@%`0#I?Lyp+qL|vCXQt)_^pd8>*xn>eG;4N4`K)*M2DC_8I#|BOLlyy%7SP8 z#%6eAiY0rlr2OYu9NoA*&t*^Ks7FcTeld5xrdq6uQCqz6AlrV1S!tPGyInn9fwF{l z@nwfBnR?Q)KC8#e(5*3t_t9pX`-eZE!OnZ9(^gjRnpb-uZ+EUIjgw!;>Tt!q#l;;& z^AVOMH{xpwA1l%Ytj>3FNn+@f@T|3*6G9{v#0d6Kzn=c}T_cXn3Rio054(K< z5F`Rw9expgj6H4Cv01vT#d#UjOFxz3wkojhE2K6&MegoRQN2r3=K>Ieo~ytg`FCL! zif{se?FH<&j84i>=mP|yxC3ZQW@C>l7%a39XvL+rKf&wgcl<8i`Wp-RfROWUQPzWQSk8-Lo^D-03ER!f)R zLZ?sKfHHJ^#9=aR2c)?qSj#9<2`!cBFd@+P_b*}u-<6s$;P_^3h^vulT3BJ4weAouuTU^qbl0ADv?$z*k8I^+G+kCz$d8*1n zL`;lfG>60DKq)AOL0OiIY$hU73}+X&EkB;T**ypYzD`5c)z!Au7s$jqY|Vbkmse9AQbNft=qsJBwopsv z>(v(CsxW{R{nL9Mxlg`8zL^!k=ZYfQ8O-%2a45|RhH$&CI>)({W!K*75R zcGFAcR4R4YQnlGbnQwJ1Ff+}~&6OO@WWm70yH32?G^20X|MjGa9-XM4gym|fA8pgD zjPo~95}Gk@BZ^+EBiSNXsN%p8!5m5vAljWn`!1mo(k}+^^;t5qWbG=dL9! z*E4&PF<$j>V#iB@QLM+_TEzlrzxy`<7ffX+=&O1U5A`Ffd;4wwRWtg7e0vJ=!%#sQ zw3q*&DUaCZh-~w|{Xn)m#FE(UasDlIsU##M^g1&5NeM_2tY)ArMZbv60gvesz$pzp zlKQ?hiDncPn;rgs_;Qf6<1fAn%!zH=zg7s>>%dk5BX4UDd$1#|I9j8~(M|Qo zr?MD8Chr@Xey{|BaDp@iF?_A9#LwDoP|bj=9?3q5XgsYp&x4}XLah29wl(An{fPv zmMbbu$aXawJ{U{UvL~KW1w~n#rMGl3BY|> zR|$hUWk>T$eAoZq{ek#ro@boHJf6qHVla=nU~OMI=^d&!P&E^%Ew5EPr2l4Uas$ra zp-@UN{DaKX(Lqtlb$RVK(QFK#RW_*7u&xUu`KOl7uY&B02>ZE_I2Y4DncYon4n6}P z;5un?7GFw%_?{vh4L+%Fs2L@ry0xll_vB2j|6#qp!3~z+_XrONde~j>u?en>KwhE= zAic3nJjurH!r|Q&X#MY;u&m$jul}&VLrn1cit`c*efr+2y53Z32>-_?7)A{YYh@*r ztgbjUcWcVEWW4)$`K91@TL33(Sjy-rkb*Z0ga7LE{VSND4X+$epI*?Hzbmb)TYBG1 ziyFYuYql|#6d6@dJGUG!yKHe$uevM&{#_2aJ^1PJF9hip^Yqf0SLN{Fr^ngf?H{m@ z&Lz{By^~l4Yx$Z!?e$A^{^q&{f1Q=erE1Q5A8+OB*Qz>8&3ElaTp_2ZPXF8HKo^E8 zyfkgqHbywSwc@=q40&%Bd-->w0@nK-_Ai41(QjBx1^b@MuRsv2ZK!wN2HTo<&b0>p z&X^m_&MM)1sI|T8#;_{4DjezbKR$ph1=dd~@Z+|&ngC^tE%=7PgO6$|vyb=ExSBBZ zE*jP*^nK>&H8$GuN7u-u8njqX!7f`H) zaD$J@fa{HL1dBNoN`k`vs&IForc5cYk6Sc;lO@rf&I<{1(8H>3YG9dJ2hq z$1WVJDSp52jM#(QT72S}ZM<&-&Pau&^(T9jy#9X{G{~ItdvbwPV7oa}0{8pw;=gy1 zGHh?tmdA~o!Y5F5TG&EdEs9n#x9{Hv@dLeIs0tG}E(RV1p1W%}1Zp`O?&k(*bg*~{ z6~mp5{CW=aD0H0ni3KS@@0W`gXHxjyeTk~=$J=*Xc9Mwq+n^h1!EA+sz!`^ztcKD& zFZ}R%#|Q28_XX>33_`@G~pdMf?>>OrDpj~ztyIEcElEWFU{qF%I3qKKo8ugNi> z`16(!#U%K#r-{$+Os0caVg%jN60_B?75#p%jn(n-FB!;OAY>l+06VyvF#h=Hjx)j}zn@cK?@y8NwHU9wdRn{k zX%mkPF4B8^kiOK0pQR_$u`*|6wAAy~&wQ?|mGlfNoMKHjBd%h;ihPQQgk6CuB!^vz zDN3_@!TrY{*+n3_b}_=dvstqq!<}6uX!_{)yi-DUfy?n(B12^%GsFnii35GBS6`JQ z$9GPm4aZyy(cviJipz(bpXP=IK#izONpDWyj{3K;0#_Mo*6T%%^gcoG>^YmU)gqc+ zHphdlyNSl1c2fcpt4{b6sy}vPa)8pkOz8DCLtLaOQ^Z>2)9+(e9FVt=A_q)mXvih} zFyQ*jl!5E+UDG94dQ7s5#ih?}5C);J7*0YLAewdQtw4BAHb|t-&2mBd%e?@tDUp)mQb{| zL!pfi9Ix)J#lDSg9vmcCxQaf=AuY_}SEIL>@eS+OT9x3i#DO>1ctn+G!@guFcLRcY z_&orJS}WInbC1u)m2oAmd5vR6fdes|GFMDFb~ zlZClb!oswD3?sfM4fY^ln{qtW75ve^O{t(}hsbKz38Z~#WU#E4hb>#x?yNcR=@k5y zz^A9s!9v7@O|kPQZi(6hq~#QH2UaIR7QdP4&7|K*_a)@apCsK~KS^w0r&4eMCiL=Y zirz^pjow~TGzzL?wL#gFeUHB+(6}8um4E*EgV38fQPI!YdAj(b;GBlMC|Mc=w>`(p z)g1HtugwWTAfd0o{bzozJVDUe`Dgqa(-mQ9h5}NreF{A>xG(v%v~Eo5gWt5iJ=(Wn z+@*C56az6JC8!3eMk_1&=})Zj_L}D};H}%(Ul1?|Jc2{-w!%XF<50SB*2jtx8?#-e z#i@kNti2{ZCLG|O2H5eYsB{B~IU{x?>i~LLl#@jd2*{KK|AeX*P;zL{w!R#z z;cE)5MSkc$`V24@80)`sm=MW{d|f2e&{)UPFf z#G$hMT9Vz+^M)~cQg*&`NfG>~zo(rKFk|3umA_k$w@(DfS{cda!3Rw)n z7-GeC)wB7RNVY$XiOj^2Y&RByRsKzjC}M%ZbZ552gYO_G>MyC}O$> z6&V%-1XTpD5m@YY>EJ4Z10mS_33##Yp#3+Q0DTQW2N|+#!KZ`8srcD{H^%9p-~tpasK?hR~tBDegpf9eK7@*_QHOMbn{PjW*849%a8lt8@f{>l}Cb$-=5)6 zw{ZJ&5fc7kEdN|Y3jp(E+X7W7!sUeIq@}|6Z=Ukq?!}t?b^uC+c5<0)W*%Q}#%F~Y zG^^4++vP;e&T&7BaS$lZ5#pGW0=PqHHm|*W^0?T1gGprS*Qd0!rc%8LxI>}IJRA8p z9K$7mpBG6JoY5yi9uZ>!mJ+Wg<}LhvzpSbU9e>tW698fT?p*Cg0$t@9Cep850h4-WGQYPIkVSH<-h>N+NQzGuR32J|k(&Eh{7rdf=^P@RMzazYQw8oJS(beRF7_T1jXshUA~9B9bbF9xB(-Igf=+|NQB z3>s`qj~YWN=@^&njxsvRE$PvbY(uEWzg=RIbA*uZ_QB#>xpB!WQxjVFTD_N&(^VEU zz1fP2-7#3VoM*MSKTN(|UE>0V$6-v-=FZcwr7$|RmL%r!Hjl4Oy*ccMA z%k4%rhMSH=wM5y>#l?w9mkzY6lSseG)jD<@Z!K~1!(jSQ&nHhN%AWs;ZI8XO0WmSh z8r85(oW##!x=DMV8ESmB$1&#`)*5vmjr(@15<3Ji29dJeAPzY;Xew$Bv*W&u0omZbIjebS z81WQhXsZRgrVq2JHuE>Y5*P^9cfAB*82`SI!Rifay!(s{4#Su+& z(pc&XG755TsrC3gjsssI$QlkT8m6gMvyBsqql=DQ8sp303%o(<1Tv*O0WySSx~!x{ z&^MAPa*ms&g=ZrClA}51^eWzP3P2ulzd2W{JGtQT_lP)P_QDC>lP+mE zL6=QAQEzMHJNY2vR#Fo#Cs65kq4ka8boe6GWV^<1)b%u^N%oNi$x|@5_R1m36EVmr zC)tIHZ+{u35}vEXiYpz9seqe5IJpZoxBiOFD4G?Vb zCV|Rf^e2JCP%hB5-8Y<%wt%q$4j1uHqRVBaus{#gK@&j0O4Ao9$$AB4K02z9G3mQ9 zrYakuB)t{pz#tSf8!OC2eD*qr%wwx~F-s$iL3z@&q`5Z9JVD$z*}0s1=lc>g^+w{s z!`W>vvYMTsHx_xN%l?A;yi%o=$5TBBCCHWg-x6^A3 z`|ANL)6kzd;=_QxBu+=yVBe>aqflGGYNX{P+Z>eF&nBXB<+#z_XwpuD8*#S1$0w)Y z-sWOsinn~@(o!~(A%=oGO~|~&=QR5owEs9padY@DTVA=)ljPV=;;xo%k5)@zY#yx# zBSzmdMEZ<2*`6b~>tSu$W*KUXh+gyNymx%V-^(@y7f)4RUS*lg)hhmYcmR0#0W&hW#yR1uts0?-y}W4iK?!Pe&} zt*HWA!Og>(d+r1Qt>-C0jO%%0#ogqgIhnW5e$}3Febl>ZjWCYAW&A~OGf|9g2p1GM zoZX-96yx!jId5$;XQ;WjHaLO9Om!{n0g`R}rPPLP^wHwz>~21Fd<7hx&p+E7RuGsE z5miISnJZWI9Sbo1CBm1|3_&mVNaJ(t+e+FDW4yk!)!syKA+CZLa&hVIFZz@DB2-li z-#|&tOUrXlA`-%`p6ES z7wHnGo6x8LBqPVlq3|C$wr~`9Y^>$rCvis2weO+NJOoMmgW1mF)&Sp%&gw># z2w4Je!Ppv8V-0@Mftygl?P0V_z(r3>yt0BqnrY*HB}qghu0_1H3T*&uWi)7E9W-g* z%^H);UhB`gsBBGWz50yM16|`Iwc}f=!(&L0fUmvSen(lMdz<*K&v2xVWg>woa0UO8 z^(bc0)ao4?o=T2fgJ0A2UV@9k`u%IBu#t~)pYqP*EDJ9=`PXW<4sxLLq4Ob?*wHz> zM8m04OA>?neh$GSzM)P26@26k9v_pTC6t{jE%K6Q_u$5;2ObNB&XDQ0oND4D&=-UW z@_LiZ-|o}kvL2-PjVRNbf*zg7*OQ0j*CbB2$@W{nFO`chZcZndod!}pEYs+U>l9~O ztq#t1^zD6fet zG@ZFT6|tHaxWBnmYC~Sa<*-PwXi&s4%l$S0(vjdWs^vuy+dS_Fwn5SOR>*a%@Xb4~ zwSO`}8%#;jaF%!UDKYX7lboldMj8`k0$54NZQLsB{V-H2Cw%8#Ne8IA66r1bryp$} zG7Zik^bhGLa}P~0S1T2$7L5d{iG(kmZEZHi4k$`EzA+RfBM{Km5CDuH)7#hwPnay7 zn;_!o(>n-RSP}=k@_mpv{_`H3_)E_RCz)Io&vJ3_=*R2@`ouEDXTFwa^%%+pM-n=; zJ0x*|+kQ!Ft~#OTYJ|7Uc~id?O006_viCsgr;-LP)-QJRBMH$AbsO3sVGqlR8qK7s z250@JmWAELFEBz>%eI5M=M?to`z!bOA$hSifCicF^62IWhvl)4`la2jpK&~>*MpT@{jZzbStt2BoWC<$z_voI00>8 zhkFY(A#CHoZK)%aB2Q-2zSVyVpCL|;-2F)7wa1xcA*K5(GZvB6Ru7M>gKjHYr7zG) z<3pKj5c#!Cx;inCdA#KTv(A*atX6Wm9*3lS&1Wl; zm@TIVif=_kmni(&IqXWmd(Wi%RjI*I1evZ@hmhoI&z(NR{cc-#n_N9B*g>p*k{VW& z;KK#7?$hnoKl1gU#x3Rc>Oiu++^NRsbvvSru05Hy@)*c+dq~Ft>v9n0Bm)`i()Ctm z^dNruzQpWMg!1)PVRWd%mG_{3l^Ox1r`XOT3@wO;h-q;sy8@qM?6|EDB4od`U%C))Ll$4s8P2`Rz&F|NsYf|CN^y9 z(BcUD{1T5i1poVXfyUEU&^*@*pZEjylP*oxl?CXN)A;tUb=!-v1E^2PYT{FLb(RYLBFlg0$D^y{`q@2p~?nHfl4HQC$!(qD0)8;PZR8#AY zm%^stz?@|YQ1Nip)SI-+id9g(F_aYbzc&BYaPi**!jH) zeZ<6jYhJf81~F~_G-$TZ=PG*KUDBk&{MBjHlv>!~1y=$d|FIQyQV(QNW2(u`kJhJr zz6ml;*ExziZiA=CyRT6wZeVVAg!1cyP#;Jeb__B@37ZL4-_VLDfiweEGjqq~chkbA zAyf0?VzW|CQxa0k^_3D0LBoh>1THdP1>>21hkc^J^xHFo2WSm8A74+OO}ro%2q9Y~ znD&o8Cvtb@|Lwc5X>0UjOG3cxsuKK7kITU)#r%gU&T*-&b!UX6E(UI--daZPeFQPs5{xXDo+?T(1bCBhtqpk}*>>A}bWJzcGYAp?!OhO8z^G#1LbbL$kJE1Oi29Ol z9LQG%5tKO4iSt55+vqyu=Y0v2{~upp85P$Obs0#LK#*X8;10oqTX1&^Zo%CxxDyD$ zHE59F?hV1+-5Ymzx4E77-ZwKpX4Y!{uom3DRi{oJ*?S*Fx29|Uo51cLY3L|dxkc5t z*ZdEkA5)ai!1^5kD2>!R8(TVx1vcQ0c_RxBTvSGo`+e*zMVZfA-w0S7^;2BV%4CdX zEJ=HzCYZ?$?R2{QuuR=DS6L;>Zt{Qpxf_5>^H1eUXPWt7m>H2mV^^nizCz~ZDN<32 z1XkBD*;1kulj&vrc>L!vn0tTsO!5_(W9UrFN`~pgX-|C7dR6zzymcfmvu>C2r7?6v zerrat^b}~LY}VVz8M{|UVMh{kJo%wH4f$$)&Lw4O_Wr^@UtyTHhpvf%q~t7%OyIc! zQ)ne~vDA6@>Z55dj`Ks7r~n`3fHvOuOfcGOS2ciq@LZVe7?#+KYkt%!L_@@Dy;zu^ zfCKD}XMfkjbpY2o;~lt=QFypFWCL!Ikm#{((e--a8vxej?Mk;!K55m#M0!=AQDw6C z{GbQqa+A)e8^z~j%4{*@N~T3)7lYjQu+lNmojUuuwebUhWkGm%`-^OfRX7tT4#DT~ z;Hdp1#k%}tatJtXx%D(eEoz~`*)rz{pXlYv#d+EkkIExA3cQf5gDZlAn4kJlxznX@ zO1yP9<5$OE2t5#%E6a1)*O3*ay4zy5Zh|$ecpl1yCl%kIsDFLm!!%g4y@q`AqUw&g~%QbuICS}hJVY5ez~Y-g~X;2i#HT|hjmC@|63IHp$B*77B86z%goIWw z&kp*_>)7l3In^*^*-#&3jaYJkaC#r;RJ#pe;d&i+YSu)oqiv|;udi3&JD%~$;PS^@ zL9)}h{lOQ%N9~t>PIQZbz`@KL^;e75u1jS{hA6^i$MWw8^-6FZOax0y-_L7fC(mqn z+|<*Ve&oBIT;paT8M^NvWkgJXP$cjiI3SH=%>m#E@d$D8dkp&CpBm?$v6MfQ#LL4e z-={uy_vDAK|9Q*%HL*X-u+3&IZVq$y&(H2fe*cJ1YYOlVvbQ908y+OR6gVGptLUH^ zu;n75-s=JPR~<1@#DI_&HoG^@d5>6y#u2$O*2Ybu%Xao#HDtmc`tS~7uRoHle1F>1 z(uPRAi{7l$sSpE@X&?fX3?CdYB$dSU=!b<-gQ%+p8+Kc`Zfw9eph8p7UfHd^g~Zj7 z5$sP6?;z=ARb{{o!q=#f{rH!-LMQvHz#pGSO;1fByII3&TiUUtOr0qLT>ZLCKZG71 zZxYnI?n2h> zTV*HZ*I0DhjGpOO4DOl3oKt2JsPe#s7`0_FTqY5DGbaSL&hcOa*{fZ>m$oY6{D#QF z+APc)dtsU2zHeia3F~(NpjI^t_ieVaji6u2G_#2y!}J~R2wzYs(N~o;A=}~>1Ws>u z3uLhoE{`ovU=B0Aj{D}XBnqn^@6!*%+3bwiSorV$kJ1?UZ@(gu95+ZN;=)!vS5@Y7 z6w3_hYWAz=hP%p;T_UePF*k`RO!fC;&)!ceO$BQ&tXClgtmlC45q&Y*3)~jZp%zx~ z3S-NmyXmdC1r64!+|E+`lGm}}yORomt+ybvlPI+43pZ~vmmLzHOrX|NaKyyj)?9w` zF8NYDznu0pspqF(0LV{;Qpf2I7>X*Be~Fm3tSa}O$Ny^4^;q+#bGH}%joqm>!^VPw zr8AIxxwo?bJ%Nb9ik^O`yNjX);ga>@FeSA4uE$d_I+za4UrxN%n@My}_bTi+SQ{jB z9Qp|CT>~yzQY2}*x5e8Ax872jI`xMk()2MIf0k2#1t8KDyoKH5S#s*;s2sSs)=)u4 zN+CLU`t#JGuJnd(`oXG7@$Fm)M>x01*xGR80DmqQVmisuL;svv=6dXLPcg|%lny?X zqXw2uoXL*~xm`gHqz0)W(~v;t!+@V`L~LGrKPfIQ9HmucJOKI_8DH%~24iz*oYN`; z__T90lJCkWM?bmZmFqEq_x=|kWDamyFsLG)$$S(n#A94&4d|-(dwG{H)7bD3ddUHE ze-2PsF3yT<#iv=g>s|Qv>@K;MUPi38=v6T|ATPL$EOvaJnQwRn#kj?Zc&6eUsk1#o zn-t@=q^i}hej;$-V5=nn%Xj+-W3lvytH#>WcIV8!WjG6mh-cI7Yx7Q94N3h#%usFt zrVk3xicAmKx5J!$^ZASN%*t*3ejJ1I6xcb#>#ERkScCxRq<*M4Xd#3VhqgtbjSfE< z48Wg;^~hm~*T2G*EU>Vwc#p-)84*ifU>t^p+%2$`Slzwr_w~SRkL|SESyqQ?`jHN` z>H6u-)KHbPJ?*7XLXSUHMjlnVXXa8j*5zFGena69esMu&R#ui+*C-yl4S>NcibMY9 zVkQ5Y$kW%tPM>w+qv>oHp0J~25`9WtphXp%tr)<&6Hl`NpfcV}!FJa)x%lOf#A zNH>zF6yL~S4%=U_=5C(H0?Ou^y?Ex{yfMpYy0$vG4I)nbejF9NE#e9JNTwZ)2d$EK z^W{Rqj1V2DIXI=Uf%5EcyctjxnjI?)q0K+kLIqF^buR@Xb(qpR)$os0BX|P+R2*e; z$?0U(pWj6D!Wf9&_nGiqr%p`v*fyQ}F;@)QBiUb1Ht3?~jlU)9_%ZWE@u>aWBPMS= zuX6pXo$LlsT$28YFcv<+={VXC6lP6aUZ2{V)EiD+Kuh}GTN+?eXjL8nY5c9q|6fIH z-*<3arEsoo6fa48(2jBW0-^M(N(?))Viz^lvBT9ZgslbeDF7ZtQDd8CX5z$)z!Mrw zuQIFK1P>0fJu)aRS}L>Mfa@>%+oXQ6x@yQyxyz0Yy%>P9AX0i@(UW#`J%@$@9s$lN}q&@HmgUA4+{{A zN2n7Q6_k>c=5;ShDD$eLLb1YYNc}J0&T^Q5e|}zw>U{txl$r4m%SLW|lyo!$Z3nOc zyCs@gAh8-v2Sc&FhbKYFCQ7m7J0N;4Al35F{kN9@Ks;Wj?N@B->3kcV&ZscKi^($Fz{IZH%k(aGqj%&QQ~F-{b&D=2FchWvaAXhlSI?yaFoN_`F zNStzt?&kix9gP4Nr!h`a$K7E@`0Fd!pH##-pQ^Gd_ofE5V(zhkhS(!-@HY}zpeYPR z5B%{Sh^6Y=JYY_gxEjK6rC-UtSg;6OXURDBKz#oaLrDUFQM_3-uz&_+y=1Tqrqn%1 zAw%b8BdZp;2oiV3_rqWEri#>T<<<+`8Zp<_|1949ar~q&$Z#=4;x0K(1#L#2MW_?< z4BG02iloSpj$W?#k(>LW3L#4NpSh4&Ww3WsQvZ%p8b*s=QK7>~{+iF^NO7NTy--Md zg2+GjxrB=d7RB-iu^1|_>Ssm1;*{{DGQq0FKA$enzTfaiKI)V1E#2l}`*giJdq$G+ zxEdN#e|)^kvGI7YrxxC+ee>bhr@NGLpth600)7Y2nfAL3K8xk^gV05K&-(rtX5&B9 zr53z&;Tiudr%gafErprt2+mIgo_Iwf5UccGX0+b{74U3^cOtxV82AB$Pmh7G`=|hL z->nQ&HX8txs@iI9Eqo7ZNQh6(N38htKv5vQz!V=J|7=cE>|nZE6ti=2@~Ny50Bo%1 z%7xzx{t^Tl)VLyni2rpf^Pt_zg2O--YGUBu!YMHQrhxw2joQ}y_n^Q5jWe!j&g2$K z0FSb&!A6XegOAdq&o28M6Eex9{!hgcd6f}I`U?tbe(Vcalu!8oUW8-vGJyD-&qA@&KS#;P30Ux^U&p9DpW##`XT8xXphvFq3A37fA7=+eW$X= z;JR?K6+6z)vA7}j;Xvc8GC%<6v?}4aSZ6P+TP70NwD((4j+SU~c*tI|d()!3F9p_j z9O_;QFniI^(=X?_e8)$W95yj8}w#=v~@dr_>TZ8W+1?SqeLIqQWBGJ3z(i1E87D!A{auB z5J&E5L)#;4>O3qfXWBgfT|yv=0)U24m+*AN`|0m&9~J;h!j{MqL-HB`Dk=3N#Kp(g z%!r)ek*ft7$tK zHLz?7ll~7TVdFJ$Puy0FoK&8I`qO@AlaEQH!wiw*ji9+|{?o8lT+-ZeNN^9ymw}0@ z?$z0Cq$Bv)-!Ii*DddT-AssbvcvV6Fo;b=0&|pY*%1QbUdr1eVDWv|cd)=+!3-rAa ztzcy&5dTejGAu-Yz`*uV0_RbiQi2X?q)`}Hf^j#e$<2exB#EoFOD^fM_H`b^927t$L|ew{CfAuwiwXE3&aa#JkSQ5J^O34OLQT*ZjGY@h*Oc)Vu5nrmi2+? zGS1LD@-Z&}nk>S7=mL)5ip#kwsmLX_59+DvM}?m-e>2^04?MuZGhmhQ@6`gxN&}Dh zCJqbQ1D^0+UZNRFZyCV;c*5cc(l(|blL%qbNAxCBoNYUFv6Zb8l`~BY_0JibqWV&l z$&hjDxtzCvQk=K|;KMeqp4xf-Mbw1+MZ|%C^VGre0v4Evil6=x0NsG4BFL#}Lh|F? z1%B5zWI0Tw%DkV;7XO9?)3PWnA|5!SCoS~+ZQ=3OheiMG^q?%z;)hgGVI)*DC_q+LECk^{FHi*a0)>bE6*$BSx<@-C&W}2} z-1(LSP=q2slniA50-Pc#^B}{xodk&rwR}(wVPA0+%U&>&=J=^yB*I zXJU<38$5r1M_b!fM_SsC4U2CzYjyW2CQDcWy9og1>$wnm{{uqapoJcshVv6VsN1r@ z0MLU_fs$DOfoEBRAWO9c<}JE|VSpzWTA4MBgeaGp^J&K58#T+LhUcH$97!9MI%#X^ zE^Vq83~Yz&Fv_ZzC6mKRL}i4)lJ_yf-NN;~kX%g^0y>}{ z*F*tB-GBp@3M!aUi2xEp5O6?G?Dg*+1x3|Qoq=%ad#M#mi=7A&_ApM-d(`M#R(~=_ z1<^G&ZBpQd3dZDna`1rYXTi!S1%-w4QPg-CI(3fQ!zC$f7~&SmoZ1p&if_QLciZKJ z<|#rANc!abzlZk9Wkz00IEAz7w(yc4A@D~dNvst&DZC;w-@uNED;M7bSiL7fe6?_* zzR7Tp4NH(?mqrap2Q;($?~@+@g`wgSEAYbr>40Qq25@n`9TXtOMqrhUG9=!z#e$RQ zU4jC@v;*NX&;fLt>a;FKTbqaz8(K{mQ^z;*&PAJRnkiALnb(N1*7McW)75D%g|%}5 z$+~obCfE(S#fawELbH;IT@OBe2I-hCS>>(b<8qi-H$u;h<;0AVB3kN`hW3$F7a)Z7 zb>Hp`<#t_(l?l#Rc|?M-nq5r+tevMXqgb= zuF@zmsr{>cBtrLuxvV2Jmtjz!Wk63H2~F%qUfkKaK< zA3m6I9Z$&fZ|1&wk44hjW2;qdrbs7J!}}h|^w7RLz4YC_kZDC((uKmS3``~CBZQbP z?l7ND4bSMoCRGxhK8FCjFM+t`fsyM#0HuJHVPkXzCxK{u62yGX+EV)ZCEWN{Ui+_> z++1Q*c+dl&=1D(uBs~v+pjqRR{=-|-iU0cZM;a&EOj&VN$L76SB5{uo|0$boQD-m&DP4VY8+7~BJce_Qg_sv8p!ThUgv~90+>HsX=7%P^ z1i9WM`H;GX#-*whQfrPXDKS9xQk7Y#ohw2VYERD{So<}4AmYyHC_n!jRcd7+9-PO= zKC`eM8CJZJ)R&MZH5k+J!i5EY;wnH`GVCHkab*r0n|&)U1a+WNcG>&KxirZ*sg?} zuWt+mv((>ANsy7B1U*@%%XGwWVslKa9Pkd)ZEUpHh*W-u0A!AP9XuqzvuS7+rF>nM6yX&AhvyIKkx#Co*`a zL-)b91{*vPC5PDHrmVauay~4fjApOKr-!|%BCc5Ymmp@j6@NsA@)8}bDVObGtgd9$ zY*4cT8S)SVphm!O^PIU}zh_7@9a{giR4NBC;4Q-mZ|J{34#~umN zF)h@~&ub}hrc#;V6frtKELnAEwSeHfYCu^X5w?#^p>co)iX|`9h4Scd{o)WEUX`Dq zsd`Uj2XaaNP06Ay1*+PJ0Ct_vuSE%^rNh!cv1R!pQMp(@B7Z*@Mh6f+_;x0$!;H z$CDk={|i%uDAcoI~ru(5$8L5`bh<_|>Wy zZNBUazvR3C=^S-8+p9A+PBCZ5I{80% zRZJuI5ACmXO9q>C=G-$Qz~q`*b;>sCvt&3cl(>60^r4G@+hM+TYKbHL63a|?qVn3p z;>|;eX3d*o^$G(m8$P&`fAS~d83Z5!u*{M}v{&^Q)C8vrLd6UQoGDNK8Cl;-x`;~F zB{vlj;wGr`Blu(DLk|nLaXR^stJ>Y2SlNa{d|VU*Fc~nh8y?F^vRVe7X%WceC?jeq zd@?>7dZQ^Uh6eHdWVOIUvJl3-uR0FIi1@J7P$NOmH|1ZuhMT#-CX0orGb>5ZgUmL* z5&B2Uy@Y6#R@F3p} z1jkD)j;QtJ*=9aaVm-JVH(0%M1vmfZztGD!e5UW!WCmhc|-yNV3HhE55AR}w4^$eW7I_`k`$80quThaZs zc?mQ)q+*1lYZt3cX%pU?I@tn_k*Af?pRQd;SmeG_eHTO00O+2ujbup*Ph7#c1_%(M zk4i~N&0}I>&IC3%$)vkgxn+RaO&jJLB zzkZr`1c(HYrZBqfP$~{UlDz05?@Io~a7QAUr*BeE%7dUT6vZ-Xe=4RkBQ9z@facA` z_Ox2Kp7=~TZmc(6%_nk}`aWp0PBKP*&S`8r&j4+sKmDC!C}20Z&7Wp|oG3jnlp zsomRKv%upRFE1biV`8R))ds`aBxYoR*He!wmzET()y>Zo``}nc9Ni?jUZeH7HXRj) zhe&gB3k{Bvm@TYMs%Qv%J$&PGZGi-YQ&yr)2qGp4I zC_Qh;8C6IjYU8dG~i+u4YXs6>V&EH$Meox|P- zbu66;|A`TSiRC?Jr11M*a1RWMMx3wv%lzHtDmS4Cxx)MthPwO$E<@GpqeU&7xys-5 zzkYR>ng?}}<}U<9i{U2g(D=#E3bS2jF<*n;#eU98j|S&yp#I2Fz(5AtVmF+n(O{}a zh=_1793Njqkv$o5IDgNa2fL3x&<3no8RTX76!FizuqY6eXd+@F%JPl;MK>mwFd$!o zL)GZe&BX}|O9`6*aL(_cwBuwGEyD*t+`<;(?cgW7cgiQ2^dHaE7d+qe_s%IA)rH@FhVhxdl?v(prFK(Y!SqTh|os%t95lUu2+3VrrXs zJ3|iL^zC!umhO+irqO8aqd3d1O8@FA$tTGmPpNhD`E-+}me8nS;EG zwpFJUkY9Q;h>zCuR`7Is%_bhnwQvK_WY?DhVR8ZjGlWkEHvCAV3{7&rf0~x%fT4Z$ z?92>rI!wRh?5B=C*)`hjLLtL(;8dK~$(i!RAn7n!mmXLB6U58#+gb@Ie$6}02D1T2 z&V|a?_ocjOk}v0KAepA^5~SUx>Ja_PU+f%72RJ6mS%e(psH--_Ex(A44@@p|P_^cx|zYoVVWdz?1;Sb90p!IKxhamoB~ z&^uM|c{ok;MVbSBL*qw8kVBF{aSU^QVp<0t$_%b3z}L$zU(KZ>k3tt5R#81;Ko}!K zbZgyE2Z8AA8oj(;05ZbFw@^Y{2DOO{4NQs0^{{oFHSNY=lYtGu^Zupt*7RtKFM{@a ze7>noP6e%FWKgq-WDT8LtTmhQ5stWBcEq~W6ca}bI1i2tRu5{&R=dv^6Y{rajNF@) zwt5ckxj+qeB4#Hm)dhVRpjWYqtT@;8xVj5tZ?4av5pA!RZs(0iVsA9WClG#C7T7<- zFgjEq0^^|QJP3d%B5>>dgeDz4g@`;E-k z7YfjjLtK+7x?0*GQ$88byu9ADOAbueZOz1d%_%KEH4E%}J47OYXIrYJ8^?pjQU1od z0V@iwL9buv8JGb1vPi9LDRr?Cd4Hd*HQd(2FQHYwm!j6NHI2pN z09K+0nFyGL$N;^R>PGaprDF2nY z4|5+u0OyiIPkyl5Pzw5lbwT>mgGMXT@=&tzv$&(iZ0w#R`K@8w5Nq))wVK)xE2!WH z?-Y}o@y<^?a|MBH;QgkU5(D*=FRz?@KIX$EbCjK0YF7AkeSf#$@L=DY@4=8*Gzw5| z9m$weY|gutpR$L!4R9_&VL-LeD9|@b9K5KaXJTZH$f)_Z668gg@_v%-+syu=nZBbw zE<9_pfD{}!;)?wOqQQ%2jc+qZ=hxD5f^_S8^jQq3A|2>{xr9)(`g9_o7kX5a19F0k zX~Cwd6r*6r?P+?=>JM01^9&Z%KsZ;8s6#s)R0`r7mjpaENtmPqGT$%+(RuKqO6qCm_^kr*zJo+)$_nv>|u8?G|a9oTOXzI*k^#s=pnK)mt{VD!K)L}HkJ z4yK73F&izDdB3$8tZK*Jz0)mq!*>KIPV83(?oYeoZNOl8;z;<5mLpZoDt6CbNdDS{ zho7|J{1$=GzC&3NO2PRG6rC|QvrbPVlkhzjPKCQ1nKYWPIV#B+MTI;Jl z{}X%d%mwE~RhgWHpvmt+@UA`anRL7qd-l16Vcv8HeRe(Cq(v_Mn{`f^a$pFKnsYBj z3o6~k>2@z;5D>-dU;wXr7a*wh%Xq*7C`T1>P54#4a5U(o86RoKH#bOMI7}frBnN?0 zXI(g905l~ao;6b*nuRyEZuN+3C^}3+aWFIm^#m>EU?hnEOIiDJ<7<`FV&u zGjqz}tS-j;K_I@PBr>{5U01FJr>tX?mliNsau??l?@P$fu!Z;9M-_}3}x z$@sIs3YeG7Cz<)kq8*(Ey==Cde|G^iJM*?5q5{4*Z@EY=NECW@ zYYVkv^QkL^E!`cnQY=3IqK5V4$o1e9(Yb&ga_%2SY%?GKKF#MkQy$}YD z(T^g$eBTEM9N2h)BklfCA%EdPFoiEzLR&r37Ef|qnS6&E-T})K_6s_EjvvrW2qu|1 zs-%I+DHOK7gr;D?*9O;SOJ2P8*GAVjph3+K5V%49^82nbstJnT_x(Et9xV5(y1H6L zUq5qERasfP1ny93b^=_RZjzWxM`@Fkxi6|@c36K!*ayTI6nD`d5j`I4r%{Z0jSr{W z>`={Ep=*=Pg6QCH<`mS!QMlS@2M2FN1pIcc&eXi>y}$Onf??(-N?%{HNDiV>z*mq5 zwIWdAEB7FMazg&brid+5VNh45_V zI{%YFzk*F51`K-DTHyZ%D0CviJ5aZMmlXVk1p~tWN(`*MKr1)sGd-tPjZP8uhte@8 zp6CyiXf^9Uc!3blF(BM7H=R2M59`W>ZEVHkia5repf8BnG_7gmoivaiN=jw5x5J{7 z?)vO)zRAtG%iQOlngp$zpa_=gKZHc$nFP{01PF}#Nb%rDbXX-nv90f#*$R)OO;yP$ z@dOCsKApa#ZWz^_D`KKChex1|1feWRP^PMU^@7&ik ze7|Sh)8vDar+2t+#eh@|1TB?Z#@ zPkg!37*Uhc6&KJcj;%LXRJJ~Qoj)=q93l$N!y1%WXfU@Q779C<1;++MKE(j4U@YN{ zJ|HOG0pMMP0Z67d$jzmC0YV=D3+KQ%weR?0Cu)P*n@`*$1Sx-;Fu^A(>SQ{zz7zNo z#VTsgaoJN8146r7v$U=3I3(7TK%a^DmWPyqsjRfG?zQF4Mc2qg4#u-?S^m|*jNVvB z(WeWkvhxBJeUk99n;+?qTeLLGuKc)5mz;Of_K(IgfY#5+72bt#t9)7Ny}ai7vIutb znUc1FuGg35h3aSqpT~FZw~+Jv{9g*8VcV|Uep1`nroq7G0_!~$l zaq1Io+9~A2?&8phDZBeuPjIqWgM>c$`D2Z_VV-)1^jT%2Y|jr$Ikz}n4@0iYrZXV` z*u7Ms%WS*7++dx8&c|VMG0|yHwmT}kpxpAr0Za<9$tl1%&diP|=2ryyYj;GVtgQ#* ztbT_OM=1&({XTw@&gOqiN=TguL$M&xlhxIcn%7kp77YsO6w(~Q#^%t7Dw=lY-drA; zrtP(cR0Irb-@)oQUoc7S7}y^UNAmrOe7?qLa$XAhjB3h0FUKq4>By22t}Y=RL0lIK zESj738%r)31PAw7 zU0pqIuAAu-R`MPmo!p7pT$bqI;4gp!>DP+oEL*h|^m+2^d5-7pN&fev@Rn;E_&^i{ zV%5)>sOasl4&V-u;r5p!4y$Nq`SE7)Xs$|U8(}kM-zm@uxM29_&kB^sj#|bvo%;-1 z9XVjI8(o`Ro4B4YZJ#P~IGPMUn8a8u@lrJCUEuWsFxJRvz*WGU0 zsln_ej;Qd%Z9q}Be9>j$%lXkFyMwa&`{2;x;(46TvxW!RF=ief1G2K8xdn5g9;zj} zFDF>p=eLi29)5Sd4-NLXz5O;;D{I+1yVh#OJ9N~DQe2ZLauTwYK@d*`(fzsw<8<`L z-1@7sDefUs>bekQF5y2cz`miP>#({q*CFdd@KN&3CO!t^KsUs2PFls8%`+GBHYiq1 zI|q&vZR70zviq6R=f`V8?^jJO5Zy2bhr~UP54~&`%J04;P6gxGik`T8#W@8z=%l3NEDvoonEGg zEod^@Dx~eO`t$2rLE_2rnghQ3X!ES?GOzwQ?w@NL&G&8(1=#lU)IC`qb){d|ekPV1 zjvSVEVWq5=xGqgNhDI@;WmTPQOfase{1kkg7krI0T{cImYd)v_1AMDX5NdjSp0xUH zEe9i_-If0#5YOfhPbEKB*s3_8e>2Zv$ilI^1Y%s>(pMF7XI8XNSqRCb>UNrky%^KGE z!QbD}<@&H8dK@*gs1~c^gnGs~#ySEHBfJ^UG}Q~IP-aKn&EfBZGK=wyTt1XFtM zo_9UHwOD}KfyZ^+m6MhhacXL+{$?Dn+-lANY01>pORsBt;mDKN;dp~E!)LhQ&ZD)o zO%4A;$4*MmUQ_RkJm-<6Qc3+_Hn3Q?9f*WVdn zz7!%bGaKOqNJ~p6vRQSZkwVqzon#hEaSp@?kn`aFSD+U)}ygqJ=eUSu`@C7AP`3VNRK`e=4Q>m2q zyA$Q4O8W}plH`w@|2&+J$Ad1lg~{|wZ&|lyoGZ<8ZcNE~zIJ*oI7H1kqusx`{=C+* z+-@wR;ppf2oI}{r*SpgSJkk3HZl6pnxNKh2413ZD9+MAzw+Dz2uB+&LL-PK^GsW$! z%1U&q4DSlL$FM62*-{qqR9_7F#*d^SeVlVM6OrLv#%hbj*G73}^M6tu)12Dvtfotf zw%+d>9Ma;t5g_Iafmj3w|EngrmbHYR%PZNd zJz3UaSU2`_N~t$bjrID8i}sx^3zm2MbUOaA{7FBeUw*f9I8Gz=PYsc$ukx$`f$iBy z=hp8DLb6I%%P%9b7)Hlzv9a&$r81i9jrL5rhb9Th)~1G7n#a-HF4;_?LTbqSa`)a3y#9;j47SVnT(0~a^Z?tVeDViznN&-~7kHVW>Fkd0lblwxmv~@&= z&y-1uMCO!n!*PWg#eQhybtU>t`>w`@w{F4qiIM$y>0P2WpC+Q^c%H4pRc1L0waRKX?F8Jv#a6FBB`k#vDEZj8?lxNo1fX!xbL_1 zo<%@h=A}_n?buw|A}9GND7%>E$BiC8u*^3@Re-5ZNCx_=n9dYJ2MtAx-2f(I_2`~( z;wj|Dqo?F~%G7-3<)5|j^HJM80{ipB>-BBD_GKXlJrTO9$Cw$B&cjA!#)FCl;#B9Q zP2{B#_7W{i5;elm2A+q}M>6x@4(Tz|_Ycyxd-+1QKC9Cumgy0!4^I6~UbcE}Px=qn zcRU}j8{OQ~ElV3D#G{t%uZ5#SxV54jz4ch*YNZ4j5;EfOg<*#R1Y+MfxKyVovuZYY zr`b$3H59%Hn<;I(;D2sCUmlq%NHo0~DpQNXsF2Ot&uNArz9z`!AqbVI%`L>u^S&Uw z+uYKJ@CkoB@WF7p+uzY~{nq4WUP-*DypOSQkk7sF8jMStpu_v<_)z1nA`*p2{X6@R z++Frbr^1F__iojL{zX3o-AgTlc&5Tw8lT54MvlM9J8`jknUvF8OE-cxPk!Q_sB%-4 z8}>M1@w&5Z!3sXW+o#QwfNgu@gOzRaDO) zZoR6P_ovHxGJKx0vpY0vtO~Agws>M|p0AQbTb@I!hLOcP=F()-E+HE=XJ>B~Z%7nB z?O|+memj+4`o^H+aH$E94^y}U)YWJGQ%`)Z`aEh@Xd*+jNq0g|F2^0;uKImTpY~4` z5zuvd!f_wuf2Ny8vpabQ>=tE10 z2N@$bo@ttEP=pyq3j>iG3erSp@!Gh=-t{a`X%D?Cq^yfSv>M;O-yRsihxJrCqD`n3 z75f}f6M+xMb?m+5K!Gmd3`r3A4t!wd&VQ{_Cyy;h`<(D7x7fhlvNk2R--ye&;+t|avF$5=JZWv^CGLZ|w z{`*1Yf#L8^rGE-R#h^F7jGv)gncLP|?nMYaigC?zS z1eHX&UZ>%5k@3d?9>;%R)O1`^ux5;!30!9x&}-C}U5~9fbb>0@31`q!Yt^X@)3&!T zHx8N^x zvBYOTJ*+3&NcB&BFi5voV4I%Ndj%+$W% z9tN<};d3D`C4Hgc(0Gh5p5QR`$Eon)=vT1lP1Sa*MBL$Wq@K+;4km%&A%y5%6l*`= z;m|M17OAVMsxDhLGa?9hzC7ObYnms-xZ@t$i}i9@8cXVMNpi&<<)v6p!)@!nkBD-7 zqd+P{;4|)a=X@%im1H$$tX$wD2SbkqJnc)r=BZdO!twnvSW$davV;XKru5H&=f?G6 zcwSG7ezt+=bNIRup;or21=nnW8cxnjU^S z6;=|~`?_0F{^bTDT;D<|G>qe1>)9nmz)KBWVtT_nl6gCuKcW^er3(yqQk6-?DTI_e z!85v^pcpyh8I`zuhq2qd{bQJ2K5c?GyAg$MX*}!giZd2yBh-Z4c$}PG9&5XaPUIGg z_iESoAwa={zroT~!#I31IMiOG0bK$|u+(cHfgA2(LbPQ!*}+Bav!n(A*CC6}%cDs? zuYFk!-fUaG;hUS6iyu}O$s(uOb~kQahj8g9(<+qtb%f={HH+w^IfBeK2gVJT9(Khc zil4IEa2*U7&8yBPOGu~+Dk~#$O1wWksHZW|Mo#*AtbRMjEhedRZghFiy-RQav1WlZ zUPpyAg;=A%1EMjJmDowmf-=`fhSfMN9_OntexEYHt9UizwcJY0BrjB2^Qc|NqB)e7 zkA;BNNI&M6mya@+pj#W|ullv(FDib23yoZ&1WeHF$a*E;tnj+(MjqBKin-E1-lg~k zlNtEtgh*i~eo`8-{-Owj#aP-JQYCI~zg`_1DX6PkTAFDxLRM1`+r|v>c#c&ETf94J zIOMLjm|~_Blp~#ea(s&CR_e1UCDvgBCa+1QJYrx zsWl=wR?Japn^E!8S_w&A!97M9F*YJ1I4s7*{b(*vAW*Xa;e%_14Kmj%nX7ukE%&k5 zBCdnnn6yZ(x_W<8Y$Nwob5FIe1%EC4&W}!rRaI&I@zGcVSrNPi#xu4}<@Jge$9k6H z%zm<;OSo_W3)P|rRm-#ahKEAKWxuCO)tl)k%ZCx0JYv%q99XXhx>>zdhXr5@#EFeT zT#eaB>#17kCnm`qWJL1if#6n0HJi;j>F%CX`>E?QKr*E7KB(lG zR#jV{FKZsvQ9EQPVH~+9ynYonR-|l7KtXl6YLoHq=xjnEaq3Sh{wHQ5ANqtTz*sUM zk{uy9KF^^8VXyS>Q3z^AsbncOqVwx8+%MZM^<*RGCk6Kg!jT8tfl9`&YZ@KRfqv|L zN;Fc2@4d6RqG1xs-?KfALVzFN(3Ynx>pd5ZrpB+iHp!mw)uh z#XT)ebyudTIPx<0p|a6as)VgkOlQ&UV7OF8MI~A;d3dL|pp7aF~j+L{$nq` z5}plEl&&gzT` za|jvXn+E(sNEGF65h-yn9pW)E&#P;YNv7wbCc&wRtA>~RbHHeewAyDaEW!`SLL$QV zbH&bhpE!;9trnUCyv>b`f6K|!KW*DSK9hDzx|ba+*z6Irodyn+rdaD1xrzQ!OYqnJ z)5)0*TBB;^z&~aACy~iv0d|-xw84J*TdYK0)i5VaSre0F-Wc!KW%`{(OOHjXm3~W` zDKz!r?H>~nJ*Pc{FlbaFzZzF_E2590nB_VfQ*7$BT@$M(=Zi@;saB2jKf*%_zr88S=;m|I|VHaEu(o{3;kBx@*$~LM^et zGTAgiI=a*}_x(3XSBrLHxuG|-V=3qw)R8&!OUzqRi$l|H9Npmr**wMb@^o~E6{HZU z>Rb^vsmWB4roQXw#}Hpi0^PJ_9tUy8<`pCY7+h=m+q*b(y}f+?1rF1Vbmfb%tE%tN zOQr4hsa{_UKN<&={5dKtSRt^qMXfA2(8YDgJe+P~()<@K&CEWxoeRxQ?uoPr1V~q% z632(>wRXAo_{JXufxyVr)Y7jcb9)4c13GhDPnHE61+pggJZMUy!qArHI$Q(Ncjmpm{u6wgEhx69ju5y79-zlVP%EtgU zF)u?JVL^qOZUrV^Mu9T!{yN2)jB`Hj9DQYUe2w5qLF0_kX}M+5)L}KvevgBX0at9W zI;ZHOTDp(=>yO2{HMocKb)x&J_)USzQ9RG76HcZ&V4_z@PDc=?()*+j2?@r#cMh3* z7S%S3nt3`iUK^zYx*HxJHN8X|?L~!m3z-=Kj<+q|G7-mM2pMyL@BMP?r;0t9qL|T)@7OJc_~jaHD^EEpaqfm-h#a)}e) zP)QLnr@dIVGrB`?^nnJ{3MoLi6@euAiv)HU*JRiP4|$H_Y~MLWLjjp^9$JNZUW+|Znv|F z?lI^zX)-)cvz+5HATS_KgJbO2+=I~bHXuhO@1Bf$@GajN5(>n}R-11R@+$iQDEE7= z0nn7fiGVh)b-da&`2WS(TR>ISwe7-migb53B3;tb-5ny`jUZjp4YC0N>F#b&M7q0E z8brGOwb93OzW2P(8E5>CJ#?sJFXo!_y8F8Ci6JEfg|)dZPy^)cts}I?^)}M^$a|TZ8MGZ+L0#>U2|nIi1sV?_VlFg*vt0+7UvV(4eb!~gBMRPYese8*b&pr9 zNXEJontwq*HBHJ>=}=i+N)G{!3WWi<1gM?GJ;VxAAW-SWGBPnOcA4u4(RA|;$MJzr zd02ZiVUQB+dn=LG$~*3+hov^~AAJCTLyRCL^JBhlt0^go*~#BZvW+vTgg=!dyQ- zRnfd7kV^Yc4^`2Lwb_Wf>jB6f+3j7wV8M9d8p_xuw#3`>8RxtQDpGjJzb-Nw8sb|D z{~%;1sPD(ez5-dpP@@!L*|zXTC7`zZ&weZHj+vZZIwp9%jlxD3lK8EXc2O{(NI<7@ z;Cr&jLG21>X~-1fOo^JYJY#$jk5NqJ$*gBT6o)e}PM=*YN`N=pts^gE5zKbzX}z2q zl;6|!oG8{8;n~I;3`jPT-8J-qDJSmW*K+@N`4k+WRcLIuQN@ycD0HL<{=C3UW`h?Wv#3QP9d^K2D+ zTQQil+@Si(L8Mwe!MS=tzA}A60C`HW8^mP( zSX5ca2e-w}uA!r#L?zh=l+9h1?6^dw3<9fyV9!(x2IAp?o($0*`t0Umfl zqYiC#rA{xJgYs8jj#Fdw@j$(XM(XY9wDdGV8iAGRm*6l zmYCI4M7bw*t>am;0l0}9l}MwM?PBN{hmUTvGeVT;H3zX#bA;iBs3r)9V-^xp<5mya zVv>VUh_raLw5L5F7I@ITL@#O!QpGv~%Atuz1<8pe zYb*$`djUkp5Qtx-I#V%pY_*zuLB>)pAOx#;I6%!NBhhE;XhHX9&}UN%RaZUj92zlW~)GHdL_)P&%<(OiS9Fxr1?~?jI3~(2($FrA!`G$r2@Mc4D{Jiyh2g`Z~^#qh5$Pp z+3Jv@M2Q>EKoD(oR(+P68x&rcKOqdLC29DiNhRpwMFqq3cTUUwgZ%VQ z%5Dl_QoKgK>Rv`IG5r;3nqcmH*aRs6(`~@?r$=A{PA3$BJn%Yu)MTI2Reo4#w<7{e z!lhP61e9d!yECVn>QbpTPHDs`3J`HwJvsHH0`fT*wVFtHq9$Imn1#GdVNn`kqO2G+ zKebws5^OB9>)b3ghx!YmdkRR5QJA}jfTC!YqD%MyQ%cOJ5P4Dz`NXq0aQ4FnIEb&k zMbmVDj{?xmrU(c13wFFyQ;c9yp`=zrwsw;#gr%H`wcvzyqYR`0_NN;ZCN)&&w2MKY zfXK=4K3UzE&hVXlic)@1{vb*YCE$yNQ7F;M*lEirE>?>V*^l((qlVG+UlYg*Q}94C zZG=umQA^*d(Ew)m>n?yu$S9iMsaR=pB}QtFw_7?PMJ@hzQiv#$m3iVag&fSR>P@); z^Yq!Q04z;pEL6%7XsH3@IztW9@j^FN@o42+TT^csPlUcz+Dbpga7cEMb}4piZFdkr zq)Gq_pbPy!>cJE78PlMU%5`LPb}VmPCKT67#JU_}-G12|eKwAqr4^b2G|G%t&0}2R zC{s+KcA+$4Sy7Wlc}oyzO~-nC5<(_PHbqGqv97cfX@Ub69?G8~=5B)xy#ClYnDq4^ z@n_VGGQ%OXq`%&#PD{Z?mP04Xld}B`$y?3`F8lggXjEyVxA#suMk?zREC@zY|7Zqr z>dySuuYMC3U(nd zphU1iqJQ{^^MZk452AErLfM*=K6}lAlkx>qWsx4VL^+$wB#5|c+caf#YCK&S*`~ix zDFjKfMA}^`gThLk4m(;$m7yeEU(|%p?KuEVGXF#hr3sIw zAFdY$_PnLCiV%2mHnApmyH?Xr4X!cDHLapTyW@sA+b()a?wvC7sR4B^Kag4CB?_VztLUFWvln(UIN&T&Td}F8Sp1u5v;W=b7oK z$SM>>b0wkC)NL_MfcDU~a>u^-jgPCGMxi2dfFOX-k${|53KLj^9uXFBAfpzdP*iA1 z!ZdAbv5CNd{uSxX!R`94FRZau&#&gIw7T?QPM!txq8b-RKf z2I4M2v+E?XQzx!8{#FA;;+@y+kZ>~4{?(B3Yld2hsvD3~h7}c)lfvKA$>rCOX9fkJ zI9-vCC-~VXPlS!4#Z7;b`$tlO^+}w{Kl(%&k*B4!yYq{1mW@_%&bWQI_q3giF*o!q zr0@&~WIg<5`W#gN^8jv913>z&=DAjIg#A-8lp^tO zR(1-pd`{28l4QX-lGzRP^$Sjb>|ZPVSMKJ+_w!any zHlCSrvS+}~@{~w&%%5YE&&Pi&}A40dEb^c%u;DH!Mf^2LrKgFgHLi zN4=@+iY%OiIP(t5AnZx7O7R!g!Q%r@YdJ*u7Rvmd-w(Hx$})CMR1o%sNRu_j`>;3&#mPigoggx-CKo*7+?2 zbXAXx+p)Vmv0ngjX8X|DORV{l0gs7r220l^zl?z+gH*oCy*!l&>d9g2tzm1iOocWB z^b3tS^{@<-yewF(AN~<*;Xs)1yZcPoNd$;XFZIspr21mGG zrT-!k1%r-2iIoISVFLQq574#;CTY>Di5l~fWa9aT2kg%6d6d-zqe{7P4Sf8WRYWJi zS$O(*$dbvZ!iL3T#yuat#O54xzB%s|P{{tGK%MI`j`z7XO2W@Q{PJ_tlS7{S*W$t< zddzW_L@Ee5@tCaY`}+2MVv)IDipX#^hYMqZ&&XNeJN zhM&t#$&ia$L`;OL9Av&o&rB+93`8#5{Tl*&MDUp6 zEIXpz`zi4Xi)|YuAtm_Ao_Uy{pQonr?FIWw-eHly2$vVlI)eACztSkdl8Ew{)`GXr z>thhWRoTYNv>a;KF@=2S#mH|1Ug?|Hs2&2?e;)q}6FHbYH~AVN@96J5(IT!$BC>*e zbkGuypnQy<_?PbftLZ*+>QT@Pq4B?WP#6=vQ2nA?xXAcpPNJ#2V`D;Wh_XVQBIE>w z9IM~0)$}zp8q@xmI6|Wh96I2#i-dkJvxx`e1~=%h$x9Vxt{N1!BvaxdGuyy}x0`%D z)j6QVGseZ8L?BS|c9?@1?WUA+D3&*kdB-H^uPDd9g}ON2QR+*l;oR8s$%2Pg6kC&9 zKtUe~=wi#z*t?i3td;Pf2rHcd7@1 z9?Yr4-0Qf_Xw?kx;%LSHabdo^&gxs~z*4%;KGDm{wGr3refg~XT)LUWz7#_3wzBAD zS8XIAv#LH+p)mv>lol~Ua$S_M)pNj-|Ax2}n?Hqbmkk8$#j$Nqb#D&`9Gy|^gS;IO zrdnV-QbLlROfkDFn@0>VZ|&QIvW=DSEF)Y>=~Ff%VzVb^T+XJBeFM9y9r2sx?IK_{ z1D^?=0>Hq1@iLkg62n2vND0VyRev70!uBga1dj3y!la;CkhWMXjS#2y?;2)-aGSXS z<%Mi5==}$pN1I=OZxVI0P@yrYk}-z@ohOrBdGuKYBQOBTo&_uy;khHV_{}avf%r(Kwz2lQbrrim17w; zHnq1j{rWt(de6n8jL;vjTInn|<7>K70o_X1pEtHPHq{8cP6V>12kYM-0rF#StF5xn z{~87t&K7zvr^1dngJvQ1C+9|`^uk|ih*7SYBvMAms^@v!_o?9JL1)ZpN|y6z&`Zo|Yoci-c}@1gBEmnL^diFaPL44rYIc zB4kc-9erL{Ib^s2xRz4~)v@y^Z^QLo()qVsz}iy1+ZUJM!0Krc4p=ET(v6oB?od{< zdLc${7@uNQyz9ld>hW#;lF+XOh+%dZg6)Q*@ZcVlS$hd(%B+h~Xsq>3joMVEH;qmf zs^6A%z5AW=i&r#iSh{gD=vWr$3L2xnF3CGa>Sxsv0TM8;d2{Cdny0pg_u5h8ET;+m z#5eC(orcX!@}KoUoye21-sYIq>`K@q2Ix_)j&YPdAx-a(GNX#?tolZ>e|w(tjuCQ` z$`M~7qZbBived|7c-XQPwpjsuKtrE8g?%5Aka{zQNMImy8U4{BaSoyVJ3%f>ewse2 zv1j2NGnQ81w@wEu2C!c!O~q=V)KOL%+r%0s>#+BWwvr8v$vX34VfFcwy1&Mt`-v+6 zxq7fI>&LI?4wa0Aot!wBk#Te@RsV>!8883wtN;mo*a`u^RiHl=#%x}e(ZC)PQ#|#& zsrg0_u@|i)r$Pn`OmR{-yYtTMkxHy=N?=YVSfG?pfjr!d2`Y5c!kHqzLGmQ+$^Bbo zYldzg2CaK-(VR8*Gcc*K)7vvQI|LrenTivHiujfY@&U)$G57D*__2%Hoz>gEwES^G zRo+`P5!`vBgy7Zszy{mo!pnLi4c;3RGzTQ7zzWu}eWQdyDY7lZR&N7@2w$~%MTx$z zZhAl(i&&nWLrwxd(7E@U*;i#SeHokxVgCmnKqf>FV=e!55@7gVo&4Z?QZ9huj-H_) z2J#oO-&Au(s8&(Hd%^R^lE;bzdmWF!9aR97VtPX*XPz(uBvgb$08FQl!+*Dp6~+Lc zGi1`=-2?tZywOsC6Sj#%P~i8uVr_cBvjqXc%oVDNibf6Eyd+bE?2cTdFPu)Su;-IG zPyeHlfF5=vC+VQHURBjt*^Qo@eef;=i*zRw1B?12+5N*EI$Wgr*YVmx$Qq!@eIqj% zhbs2w>0K%RZ|@4ujP;=bFSIVy=l>A!5($qv8P37PvC>BFu4XgLU zClreupFjb61f(STfQlyrmevZ5FA8kr9lW#%i;r2Yl;RT_3IC#THJI%3w0q@b^?=Mk z@XDdqc8r3&rKnbv)tGF6gPV7yJBiU@vIX;Wy(=`^yboX4#9W2#Z){0#lCH;gR#|92SHM3TWtCH(50d%ZG}(( z(!??9S|WF~ks0S8-!wWvTcfTsCISvAb&TQ_BF9Q*dlb(LB&R4pNmUFfD99HuP24`9 z&?{=aNvg9kKmdc>`DGiD2MNpBV_AKlwud5mW{VnZTZDW zw;gA23P(%uJGjp*Y9HCUat(cZiNCVHyza{(_$sbNbT^#VXrJl*GD}vowP96rk?kx^ zVq^fJDTs)j7Hl{D69Jf(ujObp3$K-(iSk@0G1cyAjd;Mey2~SE%dDQXc(ZcdHyQOr z^wLTa7wov+B1Awh`@Ev)3-jPPSVH8<{pN=)44}?dK^^$=-`MT8F`lN<#YDnT**>#Orw4ElN_10H8HqepJ1DK z00GIVN~!o&2mri6i?~lPpi4!g6mtoYVQL;woNzqC7luCR+PNC%j@L=pq7@1mjzgM; zt9JpS%;C(29VDnjEMv-sO}xB#ty>yE-wh;R1CnHq_t|CUmCKqaGg@6a`d0hZhWnx> zhM%~H^yK2)JyPXJvaa;0_I+QvyR6!h{n`2BaoyKs&$j1t%%>|A?VSf-A1PkY!iQeW zKF7TvtyKP%MofHpTb$%^Kboy0l`uJxX7;{pr&y~22VmTs{(6AV_Dw0ja=m6-woIB3&9xPNijtnYVpbp{kpw*)2FGnCSX0(yiPA2kqKvsjOz zWseKIe4(KqM!sn9hx*D!&~dtd4i*%XPi(a@9+L~^()VkVyskWFS$nynSYPS+vQk?J zI&+Dnd){;uYT6(L^rN;8@;XoRXLugv5Y^#Xg0AT7^)pP^Z=gaw*!O2ac{`)~JA|`` z0+{-q_*c&L-P|#{v)6K%x;VC1&T=X==7J{1BN5#j(`685f~LE#HeKNAmneyvk~TiP zPZQEN?#0B>*$nbVTM=rr1@7>ZcUcS<^56J$@mhsF4V01_?czUi z1}uz9AFl!h`8YbhFcN;NpW=xX`h;^m&hR9kbGjbRb|sC+Y(fpL?4)7a%jiNothoj1 zk0G@*^~-XG53sbW>@7xwSo8w`x@hT6;;ST<2Tf$>d#AHnvMUSiM(F!(+lBS$rOWB_ z6_!sosW>m(4`vbXfBrnV7!lfisd(s%hScKnRdFDBJ?L^}s)D%koD1RZHpXm|i1P7% z|K}FZQJZw5^GMryYU4X#r?MpopMaweRfXgO-Sjrv+*K!ayhdOz_D9x3Ry)Z5N!5D{Jis^Bcd7%(Lu!GdRI^ z`XECg)R;K)m2d?Bt<7wv?Il~~Dr4T|j{%7`bSLX% zh!>e^Q6iLbzk~#tcsMyLj$q-I1^9El(YqY)kt~L*V=xT|MW^N++V~Yc=q-%Zzl@1b zJ%MHft3${)2y#Qaii6p?vvhp?BuxRRxfnm0U!}k1myAT@X#PbaQB}vLC*Po|daa}CFM_#PCL}Mf)WdDd;-JO_BhyBAxTzN_(;nY(n|304OJ+~3HSdCv!!iE&aitP zuh)higIKSSFx#F#k5JeW3RdNH=jSHsGKr)fO=*3Xcq=xL@kXFl_i(RPt58O!L?HKbg3=-!Kz= z;`Y5kcgGs}z|a@w;ITmBwf}4OnlX7}_IpCDu*W@1i{*;0+4YI-JKpkiL6jYAto7Vw zZ*U)#Er5~bHu(MF@&(r)S|zDpGD|ep7O1yot*8%nz>)MI2PI^M^+*r#I3BXTp>Eqkq3_IZg_p21|w|SvDP7MYC?s&&84=30U z=YaIrg5`CzU1B443F)6gvhMc}X2+5-tf_6Jgi2%XBNm(vWrnl4N{|Y|Lw~E_!;3sY zinLqEZXO7e=QpE(Oy0s=>qoVl9brbFexrf&emqEDL~{C`Q*teFrY6?((!V&YwR-0^ zhl8nn*P^v8>ccF}gUiDAQFNcFeZL$M6v@%BQO!CW9l8DaZ=IiTK^_|iXcsNL1rz*- zI|Yi^`B7y;rc=?+N_BCO65NSOoOw}Fg$mG+FmhiZzGd<6eGK}p$8~-b8KB25;9|#{hX~wCB`sfn3aIzjB)V6Q z`F?!dEv_vW9%q%5R)|g$4_dkVjZ>E&$S${I&Q|pbj%Kx*OJx!>N>w!r?Z^^}|KrFtP|DiMR<$4Y( z*WNFN?zw+@J~+xNM_zxvKd1@6#88QiNWuH|B(PSPfWTs|?UjTwGdE9prw1(JUTUvR zP8=!2lxfzm-ut8V`$9!~nTeHgqAybAIes-HgyFqkL7j48EW2(NFPb_uVyN4LdJ#lf z@sNn^Ce|h> zJ|%ac{rwxx8`1{yU=2X&6o_LnFb=xi%N%>WHktf*{0#8Qvn6#g`fQ$}j~9QVk5N34 z;iDh)Lx`VJ5O_l~ONtp>q2Y?+wy`n=4X)Cn^%9!xF%H#jgI7*80YdG7?MfA9`J)wI z_~Shx=`(&f=3KPM_cEkN#%|Yd4o0gmbxg0V@?s@=YKjne%^3>xQ{=6xr5Wr@EqX}F z7)uzcUIQ}#-Y5hKR3)hC7tuBpy>hpiQ;8`e9HMbO^wF&noe8})>blbb-iwU(d_7k? zxHUCd@(ZW*{?LGB+5WV&+L}7Fd$i4UCTEE&l0z*5FA)0O^{!2VW4>53m^VNNQ@8C6 zG|ml)<>!%XxOo@O${WLA?h!;=EOPe)RY8|!w;p?6Y3_{E$q)qIFrN~_U7Fnl3bZ+N zCs&|T5LWZlIx2Rj#9xnAv|JtDX#N$B&;8(Pv`It$jSbXFqpwb_UYlbUx)UPjE1cG~WUfH} zw~MB70hvK4+}1x_09t*~JpXR&*MTm$s<&|<+lqS*)MJu&SR@S0U7Ob(t94?bpCxaf zd-l;H`}Ry3`b5LS9mD(1l;!7QMnUE7bfx=3X$xf)~ zqYG!rHpb1Z7=zo6(N@sWzT%Jfa0H{Ktx?9CgBat+a2m_p(3=G9UQV6Z>zRZ{j>||N z4K5}5{~|+6Ak&;65>X*i^kzrq&FJ>hy^0!lUC4a^aj;ju#%;+@k3@Hr0=?8yAEj6{ zcMsa{A%54P=&qAgu&$X$i1uNYc1ekH%qbd}5D@Ki*VkR8csuG?Qkj9^Q4v)r;igMT zpg&%-=s7FS#PU-zO}_iO|1AbPe~GyJ zbi+F~geb}zcsU`Fjt-+-$i)I?6wY+l&2@9Bh+n|vx5 zri(k?1D%Iu9DPL{FS@r3<)-2sOx)&(txHNA4>b*clotU_;~&dSfZnKFgT<<2`R>n2 zjE+7c+2u=Ir!$5p^D#m{Xpk=tIPoavrMZ=*r&_H?yemIDwUzVJc29csMEkeaVRb3) z%}O&jd)F^oQI36&CaNKnMJTi=D#!T1#lCw+N|F)*Xt44`qW$A>cGWvCTaKcK zbQ-z>^F$Jv9&l%#&BIEbFQWg?UWzgAh7nNqvENdd`h%|36!B??yTjpd%bTkU{>ElX zAkSr-gATTHxlH~aVR(c1U1WmNb4BbLVzR&TNMk2SHs>8VH=%aOp}5l}na3c@yUh1? z0$~x|qxSCOF*g%aeHa<-l3?CgfOx65kG+(;V3Gvd(oIlHY5Co^xN4+MyL2))_~?sl zQ!k^N`?aPFblc&he0|R;@qj6qFyH(msML#{wT zmk|P*-F1K#dY^T2$iH@!dyeQ-f&=~H-q01`cHeVzzVNy~x`uNT9GLVJ)?_o`_SatRtJEOjKRlhtP5e0~--cL#z{jZV+ z2;a0(@clDoQ&9xDA?f$C>k0Rd@o(rkhv4j+l6=0?_qkfkVPGV=olcIh+V95q5mEFm zH7|U!(8n~TC6;-S{?l@EpQ85_$6MbeM>X-d+sLbtb=MoLRx~bh5JZYD?$F@fn zF8c`@jB7K=XzBSP@v6C)+#x|Fg2{lCdkYbpKA+io@6bX~12Hcg(mdYaL745mq?I)cSiw9Se0bq(tt$}b>}9dm=>Fm9>2)vv5VF(UmA|- z?m@X9%I_pWq&9jc3gWH$!(6W6xy*f2uxy)Fq;3M8qv)1wWX9@9u9&aO1L{_ULeyG~3N2DVQGP9oyDdHd4<8zDotUQ|Q=kh{tY zXcW#Ur`Okb9b1#xp;0l-xye^MC~i;hTFK3C=ig~w7W!SwAHb81TkJTF$h(PrqqOCF$ekaQiMlT;qy?gbT^0^iFpCP zSuoA^#hbLrw+R;P=vKwkAv^u^S2Nyfei&2rR#oHewMRBtrY%>zvbD4Grbyb%D4Q-p z%aTiy6u(OMX1!~2j$Lw>o^u|{sraS1cigOQ4P8qL&;U5c#$v(L z9F=%sy#PIEev3*6-zA!7N4~^)M6zM=UiXn`%@-1b%0@%}DMQrmEe_$;CEWVT3TXfy zRsexsH?adDW}+@3f4-B>^^;BsJ~_TbqV0$e);N8~YM2tC-ws*(ucgg0>8h)FOQ z5?x>k;}-<-J2kmvxI*kYxwPQ4{&3XaB)wEV8Li!8{TtY6UGiTCqhqc&Z%rCr(0{V6 z(G_pb=w;6o?;A3#(&N@HOmiX_5ip3vhExM-Z`Sn}C9xft*&;dF6^?~G?O%#81uU;L zt^RexxW3Z9Y?PiVq*|FOpEsNGm`ahj=0(a9&da=2E_j3LZ+osET(;#WwZSJ$^-rD| zT=BO1TL}@ahHav19xh7wU9E7k0;gFlprFD4x46@rWlj!Aphi5d{{Joli2tvGhqJpC zG(668uHvf5H|hL7wKAwrdK&Yuo)#8i33Hh6utIXOeDC1`bR_rzS&q}^ZxMjK#CM8a zghQnA2bT<#{xA{#@1=`@*uYILa8m;~(I)d3)(0v|m8PyZINvem1WAFh2GqYxO^GUW z+IRL*Emp6|cnZXtuM~uSm*==>SVvbL9bwN&ufsV^`gY}Hz+rX2uUc#f`pru<5i(YD zg*~sOS`r)_9JgQBr4wHwgtHDF^3Ov`5`=$(D>Yroe?dS8FzueJpPOT(tWNGK8Ugh6 zUZJJ&N1`TZFxy8i%R;z|lGVY0U81wBfEQBs`X)Is@qNnnf>iS@FbqKoy0Jpy3lRyz zU~25I*G=Fc77Vhie`-f>!Te#f&dys(4Fv0`A*%#rj%nds|uUGp{v8s5g@z7}7L2MK=JL>_x z6KpdH1+m7;$}45Y*UJvcILfT;NWl(^2Xi%#QskPuGU4C?9tFGzcf5ihx)Tf9U+&H) za5)aqj0K?Ffh%uN<-kEb@vdtiJSRb4I=?9O5ozn_GIzcw~Yc`0frs-4rlnFd-Jv~0oBk74jqgB5E%0&O24EjA_Z6p zJA!MFPc+~ET>neVSU$&mOZ_(xuKcfAG?gg9Jya&2n4sbIur#*PAdLLlh^39suZv?o zaatd(;8(u3%Z>v!6{s>k3!@1BL*wFdk^v zb?-~q;XU~PQ3Ac|`kR0?yh)(*>OiAxuqsYMZLNZtD~T_TzVvi_aPSnEq=a1JXPWx4 zJ0q30SHI~ggyj4aLP~XlS&#xsL6Y@WXKy5hAA{lLSH-N~os7~**6p@seu{}dGqk`=&If$UI6D})8{}R(VaY!H z&ZtOM`3B6~jw&!gh>`vq&%Ycf=p`h7hC=X!YQ~5YEGHXYIwiWndjgRx^N~-?{I&M? zWPUZo6L+>c(_=L<9rwlPsuLV20O(``#Xd(MK0!s|-<}L^I0~4^Vl7w5;K_FM?Hk`M z8UWFQa&1})=^n-DHV`F|tfe5iVFFDX*n>Ig`&8cmJrnef&s@5p!G=)yFVFLTo^e(x zdTb$2Cc0;VohQ&brJc+h;JO~~PHv^5MGN(##u$4e%I%Opg9qF~wOKZpnidY!{qreC zT-(4{gmwJ4<)Z%?+vi!JptfiqVi|3e!DG+TZP#*`T;p?J9Qq8;e3p@6^&XpX`q|_4 zPUei)29VzY9?n}K<(!fK1av?*{#Pw9Du*!XqJ;gD<%u`ilUJ3xI3oDBgQ-rn6nCotKw4WOPg4 z^3@+2NkJVh*D>Tlce--2a($dPe!QPn41p*3B-Ye!Ao1iGua=z&C3uhZkp5*i*wX#V z@po$Sg|k^()lJ;`*x+g`#jJ?l10350V?50nkD5u9b^~f^>Il^ye$O}rC~y}O1k=W) z9)*Qdv>G*7C4T>x&4^w5t=%Hct?Qc>YX`d`BV{whju#s2-Aa<%-$97i#wj%sMg*Fg zsC*Oid@=(lK!eW=YfhBJzdtfO97*Qj2M?^PCo7Mn3e|Nk8f1siqYHE|J10vLKyiD( za*8Q`VyOiJ9uVgTiw(#>PG77aE$}bz0#YNu{|$lomen6L??}z5t4C5jNFtXe+Xf9@ z+=2$PcYOZ5OlwV|eMEeGypZaMswdV9NNLUURIA$AV4!iU<8ht3?{pX7XQW3YeE#n$ ziYyS3hhIQ+tw~sV?!&WXJ01Hnz|C>Nn-$~AHhP8vXWo%EP5Ai?1nwz{RNAWR(*qw= zI`bc|kZSdRstJ5iqt(s_=twIu-u)iluMp<8)gX#9qi^3PdG7#{i6*u~tH)cqjmh=+ zxfi+7?!Ue;``@&~FAYobV@Rn#J^9(cKDqPw#YZcMTZrB(VTX&|>M}r_5tVpUA_RVS zhwXLx2L~pZwn1m*07hzdm7J4Bc+4-Liq#?YqPLUUUw96>Xbr#3;u)aVfNb%C%aftB z{1dGRL;X)%5K9hFl41Lo!+#I^hE(%kHAtl5}myJ8ocjk|F-Xd0OYLI7Mg&>m<9+iqd~|s)a=h(YnB|< z4Uo0B>*aLiqmTiQn{$|a%m7eu9LmEf#b95V2mjw>BuH3U`GJ*AOhx(79N+I9gj>{JVdz>(s)wi?G^e?_Z9~soJ&;#M@^iSbR zxtX|)gg=eRRPzxa8U|GeCRA-Ra@8-G0R1fQf6&hW5;j1T;#Fk*<2w33qX1``dw{+` zliBfmf1|lRyk5r-l3z-rF+)Tu z$D_bIEvJqG{0IM8;9C(H>Tjw34T%zDGR~no{BPMqCj}gkoQmGD`~n)v$+!UgzS&T- z{@bGQpRyqZ{$3x;+#oq8D$ts5^-0#G4XGfmif&WuQsrml46P1|XRSHj7f}9E!$Z2_ky_Z|jya!W<%=9VCA}0z~cMByyBX zPDoCFC|{HXxx9$ERA3(Oe@5qiwo8>3nWi%IunAwPqsS;}q(~8BFN0!nZDmH^5 z11t(T{l6Z*umt+a?JX*I*7ET0#~49m0@HOa6tBKSeT)V3HS;QgnE_aWM9s-c;7wi4 z{jchiE!A<2j~LP^<4I4l{2PFjVh0L07>ak|4DYr5n7DdHLJ676TQlq1@YpB?~t9C6N>IR(!-5CT(NKZ1LGI|+e9 zZCx8w2OlO`0zxDDCUEbmlDa-h=13?o2uInmRJ%o^03eNod)nJ%iVcHd7^%a@}*;;}7gNv{@sg9)BSPi)uAFxqeg~z(F}`s;*`bm|p8}KCH8z5fQQdve-0Jw?F-L z5C~dnybB6O41fU!BiduY1=4^O2_U8X(=q)&Tp>VcKS;)6Lhgb`1~%cYM6%NL9S6#? z2w>{k*9H3=9(O!`)xQ)B=UsU~!>?H=f8Dsci2h@m6ZJgI%V?JZ!$lq zjR;&a(8%4et}Bildad3$E4qXs!?&v)!x#jM8AEoAmZO81S@}mN8awv` z&31>MhyYO`Av(ko2Lnda+la}00HB+$551X;QpWDU%LnJIc#@S8JUnZS7P$p+7P8-O)APlQcu`=sn4 z{(CHp|C&q#Afr_3+I5g-DrQ!VJ-lx9Je`A&o9Yh5RBS^7ZV#HX7);i6`KB7P6a@^* ztc$a_cnDQ>IKEdD-|}2pGN}|BA>q0H6;Wj~hqUN5W3hR;Uk1y&K#JZ$(s-F41=Hkq zIKSFF=5B1}xGUi->ymjzd47=oAoFWZc7ycG#D?KlcvAT?_lhyAmG4(xy{|}INvoG@ z6qh58(p;w~)-y~!(GvfoqiCj*P*PI9@?Ep0rMoquUo`debK}Y1fZc{u!}M z_NJgR`~qRc$J^`mJps^Ud!wP4HdHxa_5ZdL!_hVgL&w!;5PE_?^_ZDnJxoS`zR7dz zMVITJIUEIR?mu>HBtP!`Cov&K}WaD!Wy3c!#p2NimVF1(APOWqs}n;%hvN1ha>)`Ci3qzbC;i-JIu=( z7}gHXR&Hw)5B81^z3bl%qpv(hr@y=DJ9xDw%ra{=uR~SqYIOx69(A*DFY##i_hUFcd`*yBFd9_6TidlkC^QjRzYT5d^Nsw63(z0M zy+rQ&?t;1|&to}*yB~IgcEIb{*C=3+kv0-@NkQP~Gl5BY$wfKH!+4pw4#uhKMm=WG z=t7YuAywQg?!(M%;%P|k#BQ>s^IHiqNpuT+KjGo^L$cT!U^oMC=LkAktaG;=-v*HX>fG~4=k*L~1tgQ9@tgR|1L=B6zFAsL`ks>e%B`yvIBGZ9 zm$NlsxGc>kkKg38ZA%k7fo46Pg!{29Z;cUQlf%NxsEe7#Xfnaj6;>Cm4;Zy9J3Nb~ zqmb#@LU;SBKVbUhyLp=Lbmy^GDC+lUUId7qm6i!%yJ?gJxfR!`He)4;0qLi@Tb58Ht{1G z6fZIdh|JFq6B3G-b`VPh0uts8_2{xN3qo3e5%Ws2{Jp^f>_$br@8ixCjzk%(lZbEu zikX2SC3O3^)9og3t!x=I@BQ$iSciwear$m}-FyGV-ultKK#sfS+lzf&luZtpiAs6s zmXm(bvWDwzZ+WsJO^=ozz=ai?U&c&^itF~O3ckCypGO^hbj4n3zdf+5-wn}(wIXvr z{G~hSkF^F`>uUeGw?Eg(PTJk8&_ezH`UXcSF^>jz&08cjy-OQLvK?!1Q$5(YJM2~#iwv*U9Q>lkpp961T&S zn!C3LdE1$;bt^N^=U4E8DqdaACNY;(-ISTJB9NiM0;d$AP4RE&SCJ)nduOUTIIAdN z+{oK?roA-(?mTMSyqV%)>9O4&s>$d!7fxknq*4ACzj;|%?1aOQT-9MtT@g+^kfV3h z+y#Nn8OMq%|3%?}`%L?FR7HOtZcI|%`3v(7_L+I^&F%%0K->DYDf=e>#kJ7qamdBK zOcfoc$?0y}H-O;jtM$CRFJHn=$R8-4vL4KgA$fo<5w_dOaAz|avu))wSjt~0aJP5H zIOvJFn-@^+$Z0rBakpUy1j4Dy8}++4s=%9h`SK>I_U1>Urj7Dj@ecil2SO+I%~s?J z4#wk<_h)3z1h%TGy@L}a0|qI?ZnMkj3~4R*8kyganzZhVCCymX)xVSb(6DRrFD6P8 z#VZt~yv{#!!Sa8OfY#>}+&EJ`_3T{685Lbe0QdPyuo4k7Z%DtFeOD>@?(H$ClYMYE@Lm{8SslmV> z{>BZk6QOu-W|i7^nLcH9YgRqBJ&Vyp3ARb>p=WcUBl8x%Le@*Oe2-Ao?Ol^zd*W0c zr{r@l?pHfzjDyjNmR~u=cQjPpyiVSfC5Y=b)Rg0Uvl5Gaxpt%{9<&|)&D%1WERiQ( z;Y@>Tdqw}9@a_t4Z?PQl1s`GVDrK!Q!n36zpD@^$z9?(=yG>1G1-!Wge#?~s zguJMwwmjSDX*j7huq{?ch;y5hjVGxE`?Jm-3;53W*x{}vSjLW z6MY58#m1Wv^(7xyfSDp>$-vJ}GQnZ*9{V-h5r{%4$d5Suhiw31pBF+*Y;u2VEN(DBC_*zG)YIZ<%e1 zv!qK?YpC(Sd~Q49VztL!>ljx=3o>UQSJ^iM@zBm|bJ4WOe)ij3C^d$&k(jm9JuPDI zFUQPJb5I#$IjCrBLGzt;Cz**`>oumy8SE5g6DG?PdlrwfvPNI6E7p4JmRgVsu!I#S z6H(hd$3Tp;f>y={nAr9deKP2`MU8B-*dQ=@4?a@`dr1NdV}Btkh|dUznZ8p&`whIA zCxS^yqE|4>Rc2=%uU>|QS+ZR&lQ95h_2lA(8IEqV=NOnOK$eL-YS`znNdluV5PEr; z#cH@xu%YZkvAiVks?`_MrAT}dq_z?xBJn)6G6}pc2m_HwX7UQ=i-Gy+5)AlyN9UzS z;6$P#0!q|Uf##pas^`2rSY%uEBr0RPw*qa7U;4jV;$7B=leSBzOh1o}m6d~f79A@- zB`KE^BJN&*RzUe*tbh@$ssrqa!vGJhvE9=av5VcGWETB!d)6>KLiYHY=8va5yO@Pl zhZXJRUNvsMY@6Rk)lXbjQa~F!`;FGZU1=^|l%$WRbOPcz@s5M02J{83AiLdyG=i*&>2w!Z$!2QIhLSt)Hs(+gTN9XUB~rH+-F^G2;( zS#YxY7L8>ljVl6t!Eq&?r$HwZvJW{|Zg%d?UPEmuocb`hGWkiTqYmYFqYtNJj&Wzj zRShgH6qGnhAH9HcIiKC&!?N&h&3Ubuv1Hopkp4QS|GdI!t(~<22Brv&;3&vE@WFa1 zTl?W)y(EFfa4>Z0iR#QFvTs)}1@sSQQhsx*x=&Ao(2F!J+^Gq`5>YjJV4YLLn#^ph zp1}q5^Cu3F*54)N3x9ncZzheu9NWGpE;nR55?;WpGfra+h0hV@e}+#C#1){1GN$$y zz?Zb>7Ox}p#8UD1tAQFWps5NGVzhmCvA_ zb4nNAV5BU2=%=zM03^k+Uu2Y#?ZITT@I?udXPKo#c=~(;a|LM(XS$MGS z2T;nLk0#~*zhAa}`5fdrXX$_@UY4Z?=C2P$rt=@fE`gJUr>29yP2->V<3x4^;*@s# zgsikyKhW;U$@`K6Iiqn+f0uJ#PRCC&T@uX!Sx04Bk|97_o zhc$yU?1|IEEMI8XkaHhBj?|!vQ5fi2x$Che1H19def;LdAM!X1S!xId7~O*!8B0nQ zn|SGM(gQR#?n`t~mLmzi?sewt7CAbn<4VuJf&VLtq~3;w{8Ae<0p!uWE+iHu~Vq&e2^}RhR6$OPI@hb;!Y(#G_Xt5+ba|m;)!pCAn{|U&85ijAah` zm^LL%NRnwEYH6L_`L!4f1TFWS>|ZA=fc}JGK>FN@Mh1lb!;$h4%Gu+R&nXs9x$wJ&|Wa&Nb7@v?t#~ z#edouqD0QZE7Ti^gNxof<}^UhV10jE8Zr{r5VZT_x_6(~?laru_@me$r6(JTi;kjm z4I%9tM4}6+29}yWhqk06Esf8nLzOb9*Mi$I78YdB-?-75D;`cY$J)hUpf&Unj}LqL z8t@CexxjYK1b`c4120yIy$%!-4|Wd|Gm6}=m+3T>P|DisPmgz%XFHAI2@s_tX}O)0 zTui>s#OJ*>?%Jx%gsUyoJn48Ur-Qi-vn0zWVCLR2~YlFc2xE60?<2bkJwr&%tJ(g9spl6W5T$P=+`l8|2 z7P={}eT^vrS4KSCaNl6>$MVwBpy)E*%ft&Wh&9YxS)8v%I>@RreuM1hJKAGQ!;Ff; z3J6;7XxHY#d zIy&&^&!Co_t90v5y?x6`ale*Cl?dl=yj#iol}P=jRfthH)@SkeMY;Fn#=(0M)#M4oSQV| z!l(Vy=@*7VYZ;EBNo^&M!4HVjoU)CiRrL2xWx#V7mVh^i4!kQ#XWQv4U6_;RnZ|AZ zGLS{c)u?fcbzp!qshwirfvePDXgIvUajv3Y4tO{8krxA#6I;kL(4f|de6l%*9cD6D z@Vt+t3({ix2RBeHIl!x4mbk(kE>AF6x0&jPV^!_$G~$?ZHKX%zXaJ4Pc{eM+_T zblA<5!FZZGuaK`Q)V*qRa&F`9Q=L&V6{%fI^~}l3TilN^a6ixozw-v_A?(#6+ykU! zz{QE7GbGr@`{yR&ZBJB< z;sYH~kHSb%wV5{Ze%_6e4VIemnB?TM1Ib7Ne)leh$-%uVifTdkzuks{7X_M}8Qe{s zFIYV!7viqEWaA}e8}4fFw|~mtK|dp3QBdG*S4mI*E@AUU=%L`&)oDf7yb2koMny&H zQ<8V@dIq>icyKdW_ylW^Md~>QWe5jG5=AI$Fu#gcl%gyj8N+fq4{G$@2QHlL*q-v( z8t7Xr#uAZVn@(^bwnq+W7*?6KwGN&Ha^}d0g*y_S!xn{#anI3O63N0xU+tYA#wM@W z-j7En=Gcf21Dj+)9jp>x z1x3d_5EJQtIck}VdZ#T5IA{rX^=ojs@JmU<@(!Cu6 z#(Ds}d+3qo7j(D|w_<&PaD;9b=h@MoZMV(2V!i?neg#kN>)x5QuE{urC!;@ojcJJ7 zx~=oWD}_99t>oJ{%U7vhm3`(a>Br{N%V7I6TNWln8X*k?985u30JD zsR-$+!~|CuTsxv{FIX|=G#5UrSiphMYf*Mq=U}r*8x1QG+P121Zx;O#-ab)KP>t+q4|QH$~fn^-o0{5C^_)_&HJ(K zNt3={IwrsiFcxSOSV7zjk~aAqh>BGZY-iU~{qPz-1m#%!Xs&<0a42e+RDp~kLG&h*LouJrWiY^e6+(&d0%tTl3 zW+t9S1gC2ib{I{Kj}$dgcvb4nMyN_gI9U$Khtf}ExU3uK8+17#lvtVBwY-^<*}!KK zg6G{H;F;11BNCTK&d%I5$Y+7alRgPz8N_qf|dFJ8nvG+-;F{ z!rXI9Fs*(1h-sqIg+I4yv|=z%+tTKOBy-uD-N&s?#l}DTx>^>qUWYvmmEc={x}!Z+ zp{%UDc`%>Laz8q3vRbGnry$?^vkB>1U7bQ_`{IpZFG*t4`v0A@tEkUei<0jtBsJmt5+WLi=iq2>@${6_A z+3G#L6V6wYBuMfmf$j^~BuHjB?3*P5N~3+@%b0W*W+q*?tjoQdtvAbR&u$&n`4Qzs zZgom%l#ZCNyDhJ}7%zQJP+oBCNdIZlmg8?C>9Mb6KP0}FAVkfPJ+g|%ww%BQcWDAm1GIDS72=2XA$u`?;PG($t&ZDi+=ZGy%v ztYl&#A|z^^Ua0YSWlnd#B_+k{evRPFGVAc+#5!d{%6jP-m;LMuixKm0bnPi)Mcp2~ zJKTyLGc>b(s*?QHt*xyy3sa~VGS&Te&$<=AiJdg-cHF4_{=n}(b7;8F=@EIkeXrw% zJN4qu>)Z_i-l{mB#)TA}qq|)`Zuc`)YDGM1PpxJyNJd7e9EY=9x}aTcq8yOaU`vG1 zn>tm#1Jk?wF zu1QxMyXx7&OB2B?)oMKhpC8t7;`7N>UgZG&D)O0ymAfzHQCR!oQ1eK9*$^_f&>6~X zI;+`?PxP8JcC(}tKgP9hUSysv?vfPFMMZPlT0Ju?^7Qrmv3!)^%j3B?s7HwJkaV_@ zWja%_JU^4~GVD@woigN>8qo2+$EKL&)B8=pjfS|TQtESqJKJL=bfp$jax+_Cqq&7` zRHmWA@2i$t@ny7{7Zpm)g~uxx@hY{cwes3=WQaLppO!uf5iJ>gVN$_l)x1rueR}_W zzfw&-`1wT)<@M!};>tu%i;@eO+cgO`i5$kp7evmc((=A3i)!YzQ*9_XzBB- zEj`$oqp|*B>TJHnH16}eI@ zEKq!wuU7V(xRGP)$7ONgXUdJ6wet_R7p?P3{~f2DN4YHv98439>5Aaa<%O}2bBh2i zTghJ01S#9s1oYt@g8goe@L!qpEnMNm^(wjJjLNSi7`L@u94R*4-R^>!9ewvB?qDQ# zU%dDkE9dQ8iqFonBr-2yy^{u+C3+#Z=1b$A(?Ly%3so z-7=fZwO8cq9XHBdCJzc1K4mEO`jG2uXU3E=JRARL1#|g+j@*8r5x*m+N>bv$wA}fS zbL7%^{`WMW0SqQn~lQJO7vp1@72j&(4ZVJR)Yk=4byxRW*m!=_FWa zkWP&vSJ;4%yj$3B;DqcpCgh6WHM-I zd>&uU#U+TA*V5~@0)9_!>bpYib(gx+Wh@cGEXP}>HRSdc6_zNk-IYi<1P&X|1Tq*` z9`*BgC2(dR6pX{UJnuHzraZLLEINO(3i|vLC@Nm9wmWc{?~0eD+I(!%l9JGG$vT&W zV_wdVv2!}U!D$GibRN_QJj@rX9t3QEwh($tO?u5xapjt=d|M-|@{pS#2{?_p1I1iKI)%1c^~>_IveF9-s+;rn-B)+Y+H*en9B)aE+_$Xj z19TD--RqM3c5cp=^D{w|EeOMWp1pY-y(!LI_x-PRC%cPw%Chk3zqkM}N^G+O%?|U9 z4dD$(Clv6-;dC`0lM-E~q{$x6;@H=}s`NZr9mt)R<=pmgCzjl_EIw40stV@5sfa@~ zEVq`t=er;@01Ly{M84F*SYjmRuKIlH8({0XigB>+!EmK-<9aTQE+Zw;jKGImYmF(f zrrBNH%*1Ut_H@r!!-5yMg(qZT&!IddLiYE52Km007bV^2Yd)#ZZ(M0zCX_q7ePrUU z4Cob$WwX&8m?HY`oO-X%o77SYdo7V~wq&KWrsvvP0Wgj3!mF=SV}AmpR)?27ak*%7 zUSgHE2vP>w$Pf5YI+7^b=} z{56leYaZW8)eMie2TpL?=v*dDOG}q_2cYzXUGlUsb}7ZS(k*8jHQUCi+3~V=plqJL zGJdt*%Y9EMtHXll!%$*ECbH9T5};gZwONidhzEcvQ5NMYa9u*8rob6XN-S3%1&XDt3gp_VqzoG)Mm9Q4s|(^M-o0 zX-Zn*6T)XE<%@Hb9h%*Pm?Ep|z>ydex#AUoL$fF?y`v(ahVc=xlW z{?DO}As-{l^FQ1G{#g{DFoQ{VEQ#A{9Fu6N$?RXN<nG32}`E?)q~sskCT;cvGqLT6x?W8wON zI!F16Pl4x2YR*b4Ct)*1XOcA$tb4hosjF2SpFYwB<<(^+H_#{Hv}{sii#)H(<+L-V zTsHoc$QKVY!xQx|SGN}yqbJnV-!nfUY+EX#uZ~r|;qrZZeP_EjKG(M0`7ChVM{d$> zPrSHSRh3Qu<{oSLPRRL8+o{s}viYw+EZU|!$|l1da=3-r+LtoMj9gIGrfw@*wZcAw z7%Eh)FUV9%I=MrqGX_v~53gY>sl09lVKlk-La$>jq_(sr9vrx|G z;KjK8Se1!wS^+Js$L^KIk@o>3r8J2qWA{zHuKnrTepO3Hv51_mjf>qYUxqF#uYZ%- z16_3ttZ213kYi6bpAaRxVqJSuI>*xM8RIXnDt?lsDnuS6qJ>Sir4$Y?&zKvp+6w3O#yIf}OG*)D0Veyt&;`lD$5EXR{ z>Xs~}clA8FCnZRT^9#$A>W&Irjvtf{T@we9qewI0!@2JPZIXOlqfdf6jB~wpZQfdc z;RP7jedqn#e{y&D_0YqZaLUjAt@jBnZN^zG=*=w7!38A8) zwj_g z6OLs0-+m$Q!>SE=(fC9D4JQYrosQ@=+3)%@Y^qT0Xqo zg6KBK9)(`vDg#XU9{?BzVi-?dGg>Q<+ldBe`;LCjY27eE2txX=+PK-~&2agW?8ErG zL@!i8Dll}g4k{e;37gWO7Dsyyv%K@!%yn?%1J}ODSKx25+Z`R7=c(q}BreAJ#~MY%;m=l%E8%d|2r!7Gv1*v5POfU`yGJ34j|R z(2{O$UIsWKfQc0V+*hD(A}acW1%gw1`jp8Yy-a+`fbIegBcTC%7*;a2!np&ZOvpBZ zS^uiZGoe#2U+{B3fY`tv^@-}POjBJ?va5p={4XF%$nPl(?*&u{F~I$gJAO!L1gezf zn(4erD%`;Xbyo<0BocOsNRZa?4Edz+UzP#gvY8>F!bs%60&jNSczOo|aSiX|oz5rG zzdOfYGNJ-h0BI$tX8iR(e(ouMm=WJdp)M2ueO!P@Fi<8ScuBDBe{JBxf84+@L3&zR z)hJoqW1N|ThUFz{iY5~~0&GMWR6qiw?*BoI0owpS(g+e3?A0-&F zgnIB=ycwZY01S@_Na`>Hh65;DIVEwb%0iew{1;yOEOEdrGziW$x+S+h#$9)@d{@_a z9WYq_aSsMiI?z&ZlCL}9zs`yvoe;8gtrzGnH6(#=fa+Lf@(BTz3hq$n1NznJg9~tK zR3sadFC21`P9e;y5L*IZ)DZTnV4V4$Uj@R8$;BrG&S`>T94VTb8Q`aYlnOku*#rhq zAIe{-^6QT8SMV?yq4f-*t)c$gdb0CqZz_4NlhWN&WBp_)nq)xnUrTBs^g77oDZ`r` zMJKMD^zdL}Z{h>RneG5y;P(PEf&pdYh1PZf9R=cFg8d&q$HX3{#Cl{t@0agOgFgdt zb-jgAx%v7PEJawEKf=knS96Rn_@p^kag{hK&EUWTNX@dqDcpMZwG6!^!!LLHaz-GJ zwWSAH`GTYjz?BOyO{4tU=7ZSYH||jmahl&`d5e|V7k(KprRYYoWK}`IF^hfvnM64Qs|BB?!`RR#aK7NF*nuTpcjTo=9?6_h{Tw?def$S zsyKW5OsW8HlwuNjBuXa_?E9_?O;Rc3q~YjA&%}Q3{{5O^GLf5C07ppCaOUoosbTN5 zybmcaDo*!|*+=Qd01x*c;ssha9@+C4LUZtMqBrCs05oCAc}#a$8)Kvd-CgfS<$q8d zFl{JD62*teDF9P_&?jRm3?WaLXz3~>fuG^VGH&k!twus_x92DhXqEktu(_Su2Mk2? z&0AdZa>y58|K>O;KIC0Xxz03SA{dyd^km3Uv^p>UXx{@{R1HRpJYf3qAlu!`u$&|? z33^B*x>7d=W;lh}voBJ~J$A8D=~*{>lL~Vbh(_!_B41@HG09(}U0h7xqJi!-uHxtq z2#G&KzZP~2M&uB9d|A#N1B%O{NrU+VdU%ac5#)&lx}+BafyDp(>7ft}3VASx#|<=+11 zTj4Eu1oUFAl`n?269ZQa`20*#@f!8&C~b9alsI~RBI2h-0^ACKOoC0LT8A&|GNH)0 zfM%=r_%I<9e^>m#t%V>K!F2Bu=IJe`Y=637rw`cnO``nUxy8^uLK=-ouTdd#>itwU z#eHu7>$|Oh6cXJes*YLyB#G*9U<49d8k&F{`5FR!j=LG{r@rtjn5+<7M%=uRXHYB~ zaz)6ZAq;P3TA=Lp)e7kA4ro|fVE-h@sCIS!?pQ#yYVv$eWxxV0GZPJQ1MwCp6MI~? zNh&X8papC`_z%XKr~4IyASicMLWc-n1(8ULPpt#~l#3)AUhs>oK@oKKsTwOU3l#eg zwt1>9{YoZX(b%a12$=;&nem8bJ(vPQuRZJe6k{0lX;&&VDt(O#xRJMU7C6)n;198? zJPpC=(7}I3e_q)3SNR7B1m=KjcvBwvQvxRhuwOLJC)Egwc_umdqTS%ewmc{(udRIt zl)5jD#(W?*EH=ppmFmSo>mN4vFV4JeF*++MYG**)wS9KIB<-SWkuXkDB8Vf5z1{8O(ijSn_u z%U9ky7>73uP2qd${$@q^(YkZl#PFNZtR;NbR$bd#c43u-;dc@x zSy*ib9%?FQEi%2iLncLl@;ve+xaO@bm*`gY{aiqNX6q6+WdBCz5D8i_!umR^?mCY@ z!!185T?8!OGR;c*40yp`6qBD#3=sgtv+PQ=BgVW>b}i(LA}E#`q1VXzdX4Gc9dHm= z1x5(SL7rg;yB7#oX(*yGndI_VFR2`N{GpLVkJNW z(G{*Ld7_l5T0Q=P_Mkw^z)iXH=KLkH;R(sJx#uqp%e_26xgci5VJ)0{6^t+fO&7NY za;po6L2HKbOsqr3r)L=#YH`-eR6P<}8B&gm^=_R|Txa-F_;WoE!&&XHKe=<>gJH8n z#*6`AHCPmMX3I;IY;2>y|mwg92fcyIgakca1iw)7f*~bla544x7Gb z%cY759tcan^g7Kr{EsY}g9cs1rwkQdj ziHlU*V|)Deo7n@+ADIz}{wB98$9Wt95LaX|V8K7{b-2B@@v3p7*w3wFFJz{(B*c7R zX`t7#xT8qW@0mX;Usd8n$znM=e$MdL$JQ#BgSoW)Jz1>B)n9k)kJ25|blrAbV0hmJ z!fyTCpX!~g3xyXnoZUzR#a4*2dK(SQ0eqDsOPI%xjFIi2*KJ(QRyWg> z8)H_*Q418cpcDrKC}GTbm#$WupSRYZv(G}fKyeo>C+cjitljy6K4Vt>&>oSbwD+e4 z;W@W4+p>J6I57j~kpo56jlO&jAMah6gXSTku;ylRlYuY;W&mZGO|Y{n3!e)&m_Ii9 zOzYJ6crIn0Yd@|^Jt3Oe$L@o=Ms(ZN7}Y4@N!PLC2{6fo2@7|H*)&BYM;k-EEd6#t zqQHcee7|Cu5fQ_oxX8*a)MY}Gf&}kiv9l*PZrQ9ZYmjkAPPmL+U9U`I?9iT92}^Pw zzfE+qD?GdAMr@RlpSv3(>1#yP!#N}`*UDDG$~;fbqb z)@5(Ty|+r5k7{te8Mi1UB3Fwgw%!JJMl+(skjEUcQb|}ll?Q|6MV4nt9j&E3S)u%s z^`&YP?w;TD_vF(!7*YG9r<*_2RINyJPG-w>vg17cE{p@@Z0tCzb2aUow`nOSZj8Tv zh1bC?)FHzbbAE1akE1~Vz1x?AxWNR`RTKW?F%wn6+fPV#>3*Krw)Ohgn$ByZ!XXGy z`%TtU(Y<5#c0JE6AEL;Ydy-ap15)2K5^`eP@V!keRv_r)>7n$9b3%TH=EM9f#MFQw zC4^^wqi}e)zLoF7fjpWto47tS!2-Zi=373dNzaO&5#G`8QZ1+glly9+2Api&2ezZw zh(w&$_bPO3>(yt713GHwe)rJvU^o{yzNB<&DilAD(T86TP|n<~)1#em7hU$3-)v4; zkF~XVS?0O4lzKl)Ga*0Zb{)7=9)y*wR#lp`mX29PO;ij`wsfx+RvdCx&90|n-Dnr; z--xnZHvLG>>zQKXCVo2nzCEWLw%@kemQ<#~Kc%%s?&*_PQe(*bP6z5aiVu@NN?7lW zGQ+2kU|BqjNjTXl(K5Ut+*#3wEg(nt?89sE<;!H_6XNSxb2WZ3$vgG5#yc0Pgc|{H#*{FqQ~XR}i-T~)F}Tn>h1=77gCcKNbl-U zkB#hUH#AxnBuzZw@+Uqi5Zhd^O%PW>(UkP>v7*R5$Lyd6574(~+E6v_;fJC;IiPbv zu}S*uYtc;1DZ_>)ebnq2FxNoz<;X*yH|^rsL!ASO&vspZ*bhWe#*z<34ywr2meOv=q!O9>D1uc#%Q--a-nyk*Y)nt*plA=sBa|>Ix zUu@E}uV&>=Z}$!;S3Cd^{V6*DJR6QV_7Ze&SA%M|YA3Bn_JRL61m}FOKboDe(2dCv z&RzUG$yuC<3^6=@u5Ikx$GI0fZy9FaxL3=(n89tT_4%b==7u5}QR%jarIyMSwzK^q zpP5(jJ0}cIy}b6vyi&>!v+H&2LUCe$aOLox`;Nd>M>e*7Yw4mt%+TI}u#49ImYBOK z5?(XAm1}~z?E)N?%cYAC_z#*_E=uSdI~`^-&%B7}66)%SGNo2eE@zDGmk1TNeC^r) zkj~Z241z`H-?kJ1klb*k+ROH`Q$==ufgwYf$LslRc+MXIEa)(>~qC~ z%Y9ifV*6X)blRJGfgV>28;(ErAWIeB&1EZEU!*KW4d(6luPYflOe_G=3w!F-GDr@Pb` z<~)~ADH8pKsMBBg4C=rriTs6}TjwncYt2_ii=J%5FI6tsOa#z zrmk$fTy6hxiyEn1v5C2rBwYR6y6CX0qSbDz*cT!d?;|->LB!CUINNcQH0^KpAbz ze~Y(GIR9>*;dJ}8o9s=!-uQY1_F-39{Cbg-C8>cmM;N}(4^9^Xs9AbsE(c}Vx7v8L zR-9EXQ#<6+Vyvb^pxscuToCu@`L_@p`>FYWZ;_x8cHMZ*;+B3#+)?ox(Cz9gv{wyC zJsg$Fl_fsmN(uJm2dqgr0DXUeC37;FbV|SALhWSv%(X4Y+wW&TKbJr#eyHH_@wM~z zM{E%-N=(If8!fF`U8Q;#w+};a+cT2z|0-OeA#s5V%qR_V@f@Z*-+b-aR!-IKp{|Kw zkL2k=N?wFNcD4IXtG`Hn4m05e*%11KJsSI|ds2RQ`Wop0+}J3w z^E!8c+`eA8TEMQ)I-GjpMd5fDD{5DNLX_Im)Cy|jw!~ev)$Do6t*^aTiIN<6@-or= zO{Lk}s^c;r6WdyoURSr1j30v5P}xFI8iVIYz0<8$KIm3( z47wbVLe|!6JN|F1wFy(cQ|Hl-5621#f|lja=Qd}_V$&}GZRk{HEW&a!qJm;OE&$bn)H zSfjnqlnOmjVKgpli-|a&RQ}=u*blo9MVp>(KG!DC&{`9o5p{SuF7u-^vVlna=K3*C z{i}lhiq*O7xsU!Ek1vpM(X&etNg%Blsy5!X%+Xj>RLHC95~_3%(2pm>*A^H~VKfgH zuV=ZBW*4~jMOlGlXd-c6+u`8Aq$KiGNvhgoz5Kd|>vNm9f&5~3^OW`SA2`L+oOr{} z-Fm{7XKT+CbGT#8)&^|bllTsA9_;^6?YN#+kt8@we*{<}x}LcA#%64e1_t!p-3tuz zz2vw(N$&Cl9KNX{WlMRw@?472aVI1BdCp_okD?Dy=LvH^8#=64-PbJC;e6Uv`xU#g z{zmPF@JzqO+%|Rb$w4oBfne9gQnGoPzD!^}FWe{ry+N4 zyUX2urj7foE$?fuhC@S$Xq9$dx-OrSV1z-vd>q#9L>snn{f+l9j@}JbEP{#7takwg zJovW31e$~AQQt3)E*5WN4~?zs)JE~|`T9Q+0~uJ(b^f9(TbuxSvxkne8bfWlZIehM z{Ui5*#^7}Ojk>CJ1y`Dzqdz>U%*|@&TbJJN)p$OilIt}<=HzsgZ2(;%X`N;sL&Qx}t*u3iFA=Kw zLb^|m);F&@Oc#{)Df+4Fdu6|~tl<^kY2RY~LTD>;`Xl9(W5~cqao4mS3=P;^`T@{O&Z{6ABa%z5hcSDVy^jVtfwA_ofG72Rxjp_*4Ar4CP6u==%)i=h{rkR!k$|Bg z!7(5{{9}^BpEXpjVR!@Eg1@R1=feOd_5COY!Zz03)L&N|2e<&7zyPnJI!>j1&3Lec3YO?>Jf&Y7*I0Mf0tNC(2 zxN$2+Q1GfO=(%o=RV8j}4xAK8WLvf%y!KH3zJwD9BPL97{WiCfWP}YyW>GF+9AWt{Hh$?+dut$F_|RJ8T4S zi_bvep!Re6RJPe}vH^3q#JN-BF^Mb_7c>Y6IJ`f{N632f`~@UOID+=T??~dnbFklo zW)PP_AvG<{^Anr>N1|3m7NYEYW$usx3@Rlf>~;iEPBnN3Y#47f2)z@!GOIyE5CtU|M>tmvA zt_E-`+;uD`P`D6+9W}_rwIQY+xu$GG!ZIUG*3ky z;b;i_s~ejxULz)_kSo5khZMU4s&QccW(oQ5wtFhnUg2@s>L=;LMDYl6J#bnOTZM6& zGhdNYnW9R{5#t;#bTr7{i1hpo_EytZNH-BbfqFgGh}bR!@u%cdE{5?#De2=a&z~v4 zN$u$KYXK>DRSwJ^U%ti!Im{L`u?p};o^XF;6o{i5mAyh;EZn8&x@BqE7yOjC9^;lw zU1F+EG}Vooek0ZU`aWsvnpQz;m!HsI0!J5s{~bY0-unHIHJMkug^MvNwExMy%hTX>-MV9lvS>GVK$`g7t z^o}h!(y9fAqQ_LXHHUJ=X(gP?tGH-o0W@R`#9BbR`End z)y%pajA4#*ebCn*=_){bYISo!L(9mRTeH=P1U2ovrC;}2QlW8Z_1hcexWH=#5cVAob#0GCA_;b+ zRY=2=#yA#RFuP(**osO=Sm%U|q}@HHSFcT;gr>aszkTmLkuRPGazu{Ep*K0Lw~N*#JRI*6zyJ9VMXBw2)-ciu zjf(MP;G%Yr<5mP2T<4R5-xy5AJ-DjQz&BH%`%Lqdruk7jG9e`43MWVWs*>{F0>?-C z!wu6upVyDGbtua!`5u9?Ha||AE*H`BFYa&Z|xH8YGb2GMhiV0BLZq!VgXl zS=7+;->5zk_>Fi~kcWm>r#j+-bfnV_qDX!V&jL<#9&l%zYObqF|Ds4@Af~~{3Fa{N z)3Qi&zIxC*cxaBZyj+F zLO@SPS-^ZFSQ?n}c^Tvb1@MhXnLk(X9GDt-?DqNeo0xy3V0r(%L_(hh!U(~(XV=WT zjG_b==$P$=o@#w&(0BRfb$*SW#KDXjl~?a4`{8%EreoCygSYGl%gNePY!ijXxj`r;9IaH#Rm!GK z4YvTu))_Q)Xy)s1Qb{sTv{sZpA#PN9N(YnH4ih&{# z-~8?YnEL=R8*qKL>Okw?;dSPj;rF5K7pr!5EBOvTKbz~SR}k_V6yNf>0e~_6U5SZS z_dzPQHqcZiD0wu~*EJJTNfAF<4@U1x%&U*Mq=;XoMZUS|{Y7lgGat-3By?CV^Y_%} z`XKRs;8DnbYXzfZ$m-2jOZ#S#7=h&jsG(5Lqa(wVdKq#JkUUAJF5uN6OF z2Ph)Z0CMEDeok(GYvp5gqQo4wc8U8N$7M}c+p2>Qj;gaMD_9xIw|aInsBfle)Di!x z{&}dG(+JQKqOT8iL^r8Q4o%bRpE$3J3(KcSR2KMJgw|h7-_NrEIVCG~@+VLJhoF)r-x!I(eF7vHk^NV_>}ROe|R^&pA|V zL}(OQ2n}nOgdv+?ge{M9mZ3-aO3Azp-%`KDmU6{YZ^su4Ih9(5Ui!_+J}=F)P-SC4 z(nSbp+9P`eo58gS5674-?qs9U)#{^OPm$bFsqyjvf|rf zCGl3vItnHjP(MJo#xI29m-W$nWy@X~R3kjx?uDzQ9Gv1ZwECR7#4YSi zIgTx-P)M>v?5+`y-BE|36-Ls-hSkyu9cVV|uR*=7uk1(cgF6xcP*U1rA?X+8G>f$- zSWgED^)>J>&cK6|klpY^H{k9M$2E*?U(ObDdL&uc3OFk~%zgP8dmRa!5EJKMtC`5j zb#+I0_A~$4S)>Nx%-WYD3AY8x(0PrEcPeq|^;jGzkMo+uE0lK&#xmRHypD{tkmtQX z%N*#$Es6mxGc`LX*>*Qg!!-J41t#N>2^tbAZ5igNJglNIY4GlP|_ zlnBpmryTphpsBjWK7*zUt-|6HPAgy7+lvMDXf~p%jR0q9^KjWV&X3w4N4bsP$BkE4 zSF4dwe3J*WoXMkWfe16{I*51g=7}mlk8kfn%Ip6cujx-A|em?CO)DYjl zT^70o{;MpA_f0m%GcVEgcq6Z_Srch~tqcrgy);2Yqeoq^TD7H;nK{^P3Av>%i6gNi zPEm*N()RG(TMkoT2Pv+HUEFpbq{6HHfM$dM&2UnG&6!K_1iI5}foo(zdbS@V4v=*< zv49?Fm9k8UV`Sm%ZY6~Y))%OSRm51MFm7lF3H42fo-FP0d31`;nNrt*2Kl2A_}U7; zT-(R@L|p)-6dLy0oow)=QK==R)N>zQVh%kS#75xK-(%ELuIhf_NU8dTm8SLlUNZPq z2a&bd_kf#FG7?{Z?((RQzIhh=p@34AO)EC&-OHp5uYq-+gGvXF&X*P4`A0Dpgg2e%g4y&AL**nW60AA21?*_w6 z`o(rb+gkifyRhv$*udPG|29Rzn|ZYom3|BFGf>(g*86QVXjl-?HR=`Uj``*D%{D0nqP(F5O0xo>;h{ zuQj5}dziqZnZTTFI0B?v7qL~f|L9~Qx%L4toO5$%SpdE+cl$_fVY-I9XyY`XcmnYC zw`x!D#jp3v!Q^}m6@s-upg~h;f}8~j(aftn%ckz60jKQ4j#uxPx6hr!J~agS=d?BY zngx^^y?S+u#dkA>dL?S<{d=NOx?S;8#t6XmNoO% ztTmDnS*Pn_;q&+dY@|LiX=9`3xoh@AiSdVUZ-8bW2_V+sz2Bss7zm0#XJWlhtbM2n zl3a2ws3k}XT6@xORsc_Z37QiQ_((@Gn$8wf{;1IT69A3Jg9d6=5&tHA#}fdvOBINI z^2f1l747rY+Q^@nA)$^k%yMN19 z5a<_FI-7R--KXn7A3=LPrCaOPaU7`P2|S&)!=0qY2CWBUpFz8wP2aKE8!)$%4cM1c zsTkF-?Sp?I$&n7R4VKd_tJ5AHZcMpeKfCbCrodMj|3@Lf+=v_=hLfzne!VX#C|1)R z^J1IK&>v?%#X9^iE`T2GT3s;ku%N!Y0X4~C(kp`|IYnTZ6_GIke>BMfbJa$SH_&?i z{Kj(0xb0YJu4Tt9Q7o#3#Xsu*koVrvaJOCmD5Dc1dJoaN2+^4kgy=Q8MDM-ViIxO0 zdS~=b^xh(RCn9?9f~Y}ozTJhqKEwFLj@6;juDy+cQxYpsVX4Vs}=h8{8b#0jVO{`(pXZju;rmQ zT-O88dVolON_C_5ZwqEGlCSkzzG?<)3}w-sg9NVXG1i*oT%yzKX;ZIvDi7e5f&nhP z%l<(Cd7x-xtuBfK9Oeitlx_ac8F|3h=&SB%xM1MPOyF0)?ofCD{Z=ReMwN%d02cr3 zRs@8q>|btU?`*%VzxYx7#BX;F!<|5K9`&GB5QFcX`wpJ+!K+B9F<4Ts^HNmvK)fC4 zCm2y#NvWOyR|%KkfM^n5*F{8QRQ>1kAPkzGjlDh=hIzC{ZZC3oysmk-9JmMQp2({V z(AkL%i?YgSYMzvNdYaCwxhVUC2;MQafrq+X+QCC;WT8s_APr=a9XMFfUw1V9bb}w~&EUsq);LJdAOlAeiC-ZpUQM`{leOeNS5{2v0$_h0<)-0?7bKsdlKMa`5AF8%uj zdS|KjR3UDmA7ni}Tl9{#P3Ng_ju0#iWR#Ee$VHBh(u3IRD( z_LoS?Vs-tU)7S><12euLy0)9O&i{Nz~+4~x&N=}}47>dOLqxG+e>=|dOl$+>#r_2t{NE7G2~Qf!0wn1vU%Z@+_I=+el~7Aq^m7s|@LJY#U?HKg7+A_G3~KXw9)=}Bxz zt-1fD_}^TphX2b5Wg_Xdcxk1NgTqhD!~e5Jo8Q=5^J<|I;$$4_ZAHO=kUUt7QGXbZ zd^ej~f^^vSa4$QPLj?{#031ouV{-*Cn(Yp@2Qt8DzQ7tMB3L7CndWw3mU#Il4roCC z{UIQN@8Y+dL~u713o+3b;scZ9~?9>l@I``K8#1aYX_m3 zVCs<^;;N+NH;?zy5J8ykb`HwDk)$JPPa{DVa3vw_#sxisbUgx?ln`K2jRY->|Cv8C zCIOM_RvwH>zMRBu7#T5W+3-^~HGWdxQx)n|_)cwNqdJO?yaHJ2(<7iO%qOe>iEfnz zwIpN{tdQzVkg{``=Z?IWrU3FZA)USM=#YOy9j~C*QDY2+&pfsBE(Mv*)CT5|{KT`6 z3ein+!%dEV0N0Ps_;;@qpaentn713Mqs9C)Ykes|c^!?)-$i@y2cSO{kHZqmEyv{IYHm2w%Y5m;X;K04rcC;;lX7p!m0yasQ_! z%PiaXiQ7}JkSk0T5wvSwC<_klEqF(aOwcaLJw%$wxcc@`ameT|!gZ@K;h?_6++J-H zMn@rwyrtgYe_MPh3)IU8ah5dV(M|zro2$(-nxo8aG-j**IX1uui?crDYVlX9%t`hJ zsE#h5djfbI0sa%^K)9QyfbSneW-BZGHGWFBT5v+WE?NS!HdU4YihLkC%kdY1cTh}v zhsJ{tpr~N3Rt5Vyh|Y$jp+40_5ULJ%0=xh90UDBQVtRe8V#r1Rv$lql$g8gy(x2=1 zRA4egaV614q9>1>P0$Nh((E64WB%Hc&Tl>od~)h(7@zvcvkB=n9o^P9v%{7$EZMzBH`XPvP}GPSB}++Qa@ z*=5;`pAY0X7xwm@JPr~p_Nw!?rq)ajVb0FjXu+$2+H6pEbnhyoU!BxWNcZ%%9O(yL z8)JOcVic#OD&>m}r(IPUU2Y+G#|iLnckG=OM>Z4GnL3YmmibJg^(WO=#7i*cH1?iT z8K`KoZEF&1`*Wo~b~0|ms;A+E(O%q}AUy|3=_B&uahBy)V!cuVKs22cdWYA@2r7$&4amq%#0QG#Gg@ zLf7UX$CbZuiM)NhvZ(9z9-~VcKM!JuH_rR7BWPR{6WcI7_deaV&Vy)0$-@ zmtNcl6_Nbu=lXq|d>lVeA0MrAr+xmnl4aD_crhLsu}_w@^Rqk?4h8~MIh#p+b7$5u z13Bu5wiiMCMy2=B)K$(-j51oU5VaKNu+DmkVFsQ^T7He)$Q}%^XhZumJ?O~+f#INb z;83$5#o~#m3l{y8iyx(EFwax;kk^jB3pary>a+tbTEp{bS3{obHzC#5lNVQ3lOF?_AnFAYqs(p`eK`COFFizkzQ@ zDb9b{j%=j*_!|_r3tD@sI(*AAF%9I{2TpsdQ}zrCpp%V{4AAHY)>qAo z=vSv93?wmIrIFyhxd^Kh|3KF_Pkq?FrVB}aWL8xv?>Gf3e8gCNq?4@Clf*aUMq*Z1 z-)H%u3}H_J!VVY==XET1o#%i?h{9QI+Q{o<^Q0cuj#hSLK%a5r#P1PkR=6@E(2fGo zN`}N&O+aC%WwG4oE&(xBw>>I!JuX~@1^u=mp#=NEL>|x{Z*5z>5y|leE6l)sF3eeC zJohJ(0O=Kxm+xphtg3^YX5-2Yo9fqT$iX!R(T4-6eo3-%Iy9~O@~Eye0-gH5jesMo zW$P6b^x&3bQU42xVvy>)_fTD@X;eb_CMvb5-MTQ8e1j6_{eNVa+KzdAWBT$ zyM3~ohl5KGytK6J?GmliSVLTGAbq$gqY}6c#^%53omn+Nsb@h?HY@7HBOf?M%%4b8 zGuX7c;T-SmTMLK}VXayoa6wYS8occBMtp1Eg^Eoz1BWcy2NYw ze@+x0J7W?+b!2Wqs1rh2hM&0(TK~nOprh>ReNq1Ytex~R?smS!4f;%%T!6ls?#XV= z`qBeO7vO6iInp4;qUbaa@zr}Kk!V*D=Sv#{Hj@lZo8l9n?;WNHx%7>i(x7YMia#~` zEUz?oh==CrCUVdXBCLoPZdAyPDb=x)s^9W!zUfAG(|Vpl4BF@;c(B6BD=JZ%%Uo39X}MWJK44ig zMBfYvz>@pLPz39fEgecYc#H9C-RsGtp+LJQ`~K5roSTZFOCM8=HOzRiM0+;t`E{zf z?An6b^nYZedq2P{?9?-vaIif4{_3OBLipVm;1-j9vY;q z&k|=<;|6DA=s{#b(rBn4OrEv!O(C*X#V?6$TZ|Rk79#Uew%=KOO8j+~J~r-Obwk&4 zQ%ECS+e9ZmKJ3pn(cb+rKFCt!+5WkwEESPBY1CSYMOfT+Tf#I0!$P%$%8@{;z~x}` z;6qsjQ18y7a{CKyM(cHrW#BA-{*_RY!OkR6LMPkTt?u^v(r zRw{sxKi`j42wu=bpM=D1)L#7x)tdN>HjmM8ke#s=T%H&UZcOb1J^4R=-#05B@@&ZemOWszx#3k zu6^6&RQ0)uAk^V(0M}jYm73#oQ=Zt8Z1BbHJ%RYY&?JPi#$YF$JXy5N-$<3MZ*E*0 z47?(ztE^Ua99w0}KrRj8fnS-kRPQ=x5dA=fFj&7qMa;O4#0_Cv=}(+2|4t4rzC1id zKNXe3@YcdteG%Y|q10p^tM!z7`ST4lkd3qx`jGho=aUfZ@u+YW*s@m1tGASj`+K--}!i(Y3U1dF1n-`lUQwO z4zCH~kSuMPveP&?IIiEiV@p$Wa+aAyv!#^AZewraBl3hgxvnX>Q;s;XvV0Yjm6iR* zM}e4zf$|#*knz{uf4&((q}Y{SeWyp@ZzillcA&&F^e*+Uk1ai-lc?)*sCV9*tydq< z9U|o}QAcD%Felp>;6uFN1x`940Zx>FcI?ny6B#Hup$(I;wM&Rp`Ywdn z=4UKe*r(z|%*kl0KtE5C6g$GIWn( z#(2T6sa(FaTNvro4eELCuc1&cDUPF(w z8t@nHgP*c_13M9pT^rDAa^H$V4Q-Bpjtj|tXD2`&!m>X+TACug0VjN>$D@kr*0i&cS9!M!73 zlRqCq;4Z8e=<%^R!)0U`lLGA+KG=q7D}qkMgv6YEzoOSMvP-}bRH-xSF=l6DOF`lv z!yXQ8@nX971|1cvOdSQZ8%j>6;gU;1=zu}!Aq0(BcW6Cs6>41mPqex&zJS@JhnMo4 zG$xI49AKj;Mef7rQ=4H#=*S=;5MlG8rPlLRbOePI{0mAl&O$bkTJ<8z`B$F|pP5Ld z^N=qv!L8^`*E`Ho<1nzVuP<7+1E?QV1jZhOep;XTyk-kn8OJpvTdA5Sj2j#ZS*}HF5YIn)rnkAm&aSP#?sq z#A{95*76H4CSf#wr-1gHhoBJ)9~EoeEMllV2P4`>yQCcx1*T!Yu!IpIMH4lx@Y%fA zLM_PUI|uyw^`t7{@e70yI`hRcr3>cCPA7n&y*Sww--c&`@j$>E zrgOA2?e$6xn%I+FhdjJCv%w2l;~@A1=V&y(JBTtS3xh4T+2W*FDDkr? zbojpQT2POB;`Vd-4aU-Jm8CGb1f+z3iHYg^9K?$%hV+4_eM-j_C};+flBkRRhP7O) zNdZocMdLeoYZ{=bMv(OOoP74q;u$9EO^?Y-dBWV!+EMx&4=-h{jr9N8x%?JoX#+J*JFlQc4bIoraHMp zow3$~xEyij6%1IOKO13#JklZAPYuf}^$wAS{LRWJn^|EEMF{`>XLPb9fU>uwU&bMT zwLQf%q^=EL)MEe3YuJ_vaK1>AW=xr%J`$sbdZWICo)&P*VuTJFsuT2}Lq2~-5@a?O zLs-l&L@k?W`za|&|Iq9U#-~VZ4Y;S*nf;DkuPFe-sO^LaY;pL+%N%Bljo`vIog4mU zjo^h5~bHs(E1SD z-xR^uNq1ut9!r@iepZ|SB7HOhpbK`Zt$ckR0Ux)PgIm#vsP1o=9W`++k6jr)#sW*!9;8E(o8b<^c4=gb=7b$D>GE?Jw0522fq+dgA87hsHmwux>=Wkc*1R+fVex3hZ7E5 z!bi`gej;NwiXv`eDsNf z{160#6(xP{Z_Yi+%X!RLmz`}8oXU`#47ueKvOD;s7@dG(7gKD!c^!e&Y`$UAX$5mrY z8sd<91n9^ODHtXC`ORCF7N5*5e1BXz+{d(ThW8eHS8ohTHVz?y5DHDr>Tfi~Ohzb$ z?0apKDqW{W5|;cx!{**U%RHhLNQYzZljMZ%K)bfxr%XAb?~V>3>;^A^AWyUx|Izdi zz{%-%STk)&;AiLsDgT27c#AeBJvrP-QG9~R+Gh;W{;QKlKH3?0C$)EVQV5n06|#Sf zR;1V7$E^#??WC+rkO+3OMSYqM1U(h*hz$FyL!STDY_6@3%pbgb+YD)QK1>J&V8Vv5 zI~e~P$};;NtR0x88#3#FJH$Oy=%700=Uh@GJivX|Ea{kW(J8(w7wn$O>ohN@W7#t0^wyU@baI$Md^wXY@SC`xQ2Q~4T45@IC&swxFXKDbev&{L!#aSI+8|0oF#Z6SRt; ze;MZ;PZVBY`5aIkVg&Y{g4OSBGQiv*GWXp4{MFyar#8ix@SFM3pZP@weyH++jU4eB z9f>isL;S~@1KLfF#cT%-8D+EBmmq`f4zUi*_0lO8Y+8Ar~Kc1ynww3LeUGBD0pA7gR+qt z^payQ^*QsNIASBnO(F5UU-MN9agW|7cbVRCR~@J~_1T#j)$`=1C1V91z{(0SiZ5(< zhnf#q_RUNHEu@S07+N$AgxGac9S;owWB?`L+9;Bcqxx_ghOLqHhsP%hiwJTJV^bm!4FQha2`OrqzMKQ_PakYYlyuGLF64z(#?=qqb|c2i{g z{Ahjf;7it%kz9FJgKoSsFEf$gpog4N=~&lEBQK{#%r|X*S(=dM%9E<*`MhfJcp^0` z_@GFmq^l>nR3h&qWt1o}s-ukRL_^kb$6`j-0pN;X+>^;t*$&aZk3&7ro4mX8dhP-{ zzc~MSG=qToXr>4EgvnRY(w#ShNiORxz_3;_?_LlrVTBt!mJPQ{*|6wnjrbPN9tpX&pm+~8Z#%%w86#% zFzXPiG4@g;n=X~7(?vHq#IQ4gyv`rVo1$UFh1b`YWaik$CIaSjg4gEF#-$NzNHf7? zJ)QazV~XT_Hr!WpnI69%yiqY7&vkT?ueP3gv}V;myrVWvo7MOib;inMuHj_8dMw)_+VU)D)3xVZ z;G^t$8h!V#^_hJKnVX%#cawQkt@lf`Yryhl4g$5+R+Az78S+k+!eV8dbid2CTIjpq zP;1Vl)?Vxb!TEKQQx3LW&%C!(rq>x0Ba2ZvZkPP`;Gimkd?!G(T@?C|RN%7euLuD@ zi(gW*%p_=CY^t>H?QF)Ul4+q3z1j^;IQrrg>2uidn=}AbKX~@ki~1t8h2mwdbl+F$ zsgHXeB*8dTxaZ3pn;(HFK2Kgo`TGeZh)N@~U0zA&F9`t8@+NzdMl;BR^Gl@dLmx}Iwf>9We8dS2tKPQck4e0CTXu+iNGM&MMGeJvM@mab^_XU8 z&w2VL^$iaBvMd6Y9t!8DqgpJ1WqwZ}`LoP*s1mmAM7z;E~3C-pxX zMO=rTPc>Ik1%*kGyQ_<(I80-#o4sM198nPZ;Xn0+A>mUO){vseISHHIsmCfW!{nhP z@%2R5X3XTK%jAM(X&&vhBYK+q_I|Dn9)o|%NMMv|cGoQoMSQ%s?~ks-OD zlhUaf>H8gG27XqdlL50>f_u$otHN|&8+|_Wc-PsNp95(?Hsxk}>0FkpxWe2Uq)ivo zGn-EBQ@^(I3=XD$yA7c0*85(cU(6&tUCHQi$kuHQ^<4R~;-%q1cc#sJxh^D^C`f*D zc1|2=biz;P|7=n>7kRBYlPX0M@O8dCpYVQ!YN1vmY-TC@w@f(Q?oa z(Ldj4wI6Q2uPKx(7aonP(h}DCv+Qj78zb%3N3m1%rt2=yEbotN%S-o-)hnfx;5qM* z^Fyj;8Gjlvufr|kfe8A|X#15tFDkB^)9rUQ{%67*c727yZhgsx6dunJjhuCfRh6MCs4aT8|}@ri%IN=&p$8zLP@^c@AD{O;-6@mASkv3eAQs zxxNvT3K#`>3nD~0CJTuhRI)ybqEC_By(G|lWm|do+wxQz<9)h*;|&xk1R8R4tz#ubFN)5a;e0XK#4r9 zb*!RYS_7rlnkF)Ka+XZl|pLOm>J}T0&qY9M3!2wHk>k1q&997cZIHH#} zU*P?&@x%^}XzB<a|Mi) z0Wxlb+>m7AU%gh{SP9uJaqP+6J|bTfiej9oyt;XMtki}!n)LG|K1-0=Y|LR96KwV} zQUrB)I`d_9%f|WOkmj(Sy;Cpp?U0Pe;kJq1d6~wVIAFClsa{08)T+C{&AkUaxAkO$ z5C>(yQ)u58z22_3`eJcF#`LT1LVqxc*M7@WheIFU`O0-s0rJk5@yVAF6lX17U2t}2 zcU!o5HOMN&$a+X2U4PcI`t<{;Ly)f5w+h}!&s6p7;(AK4~(Krv2{m|ng&rfA^^#B+puC%`nX<{%L(svd!(hSs5%41 zadZ-T+>YXdmUJYRuhWt!&fXdi)%K>3@mL<+N)Wti?|Jd~{A$W9%{Z5}FXo$`+f;9i zA}=%=rkEzrlq^R~DZ=61{E~!KHNi;QR?@(1SX5v}?tVn`X%jW)g|^(W=exaQ_u-(h z>qNOwpJPZPG&R-cL+{Z&lbG4Fg&TCO8fTfGX6q5i6}{Eo?e%6-!j_&T&(R`OH1;q= za5$cKHw(e~A(t5zb>j5g3(}O=J0y#zR@9f~ovZvPa$%2u z-T#Cw%jS*23SgEOY4u1xt#@;$#qhf}A>^q0MRw#=KioUq3|5dzkyT>NQsCTmE5UHI z(%ub*=!VxOv-|dfIe0lM|Lji(?$N!dj^$a_Z;&1qu*@HIXSOQ!zlajrCwXwboRsge z(v8y{V{w+u8LR(^xX~_?Ibq&B^LbH{RK)R7t}%mP?PfuBrKubjZ7&`4D0h9L%CXRG z;JZ)GTy0oVpdvAGvZ~RWtep z2r+)yk&5(&4TR3oSyMUfC_VWgFE#2pd=@&dI3)A-3C5~rgPBa?_sPmxX(K-(N?fUk z({SICJ|`r4{unTaC*<9x7Z*}J-8@3{Y!E~@N@=w5&#tChDJ2&9FhEX9tNLW^WqBAh z8m+ir9C0+O$e2vsnwrmJnZ3@tE!=c90vR1os<$v#U}`G=UeLgHiBOGI2t-0EX_R5q zfQ7yes#I(ex^Akib*xsisY=PWEk)sI|9ZAEg1ARrsFu+*aYEfBdc=Cw#x3b4Zc#jK ziVmW?n_ufrNKf{l>i5x2QV>Ox{O7n3Q^m)jB41`KF;_EfR4`UEr5k$VZ$dfhhmf)H zv;k}KCx&!SC#qw-o=1SzJ#g;q_1ZxI_3%a1k<9wVJ5kLivIp%2rlXMOI=5uW*rb*d z&y(}M(d<(QLL$UOY~_~}-_d$(B%QeN%x>IWhwyq?H$avoIr zgui-E=9@f@ELEB5g{P;%rMZ&|O(XoHmVJ1_y-#S@sA$^N0DCb}`7L+J962SCh0P^KOyMx&b%=w_OW zd4z4`MvG*KId3>L6bD#sb+6S4Y!5}}_&%I|$KJc2;aUg<)4?z8gssFX<1W z&5U|~t}Y$#`~9-$qg|!@@@s~6e2_;c@swd08meEQHmy0HpK*Z>!0v*7j5}}&U#~i? zpX*o9dj`)!$-52DZ?3wJgThj#-O`%}D=Un7;;gxSIBKu4TlD_6QH+NS? zBQ@bW%-z8&#he-e9JD>dq|h84cP*l&lO@5nEiRb? zae=&92P^DL6?q8mde*0d25qFza>V zY)0WUR$JNiVuW?1{ir3O$;%kzaoDoLcw++j5XY{aFkwnha=aHQARusWHOf7rC{zz{ z=_~y?4EREvdJ0geyYBZNM~D=ApU3;lF+Z0oaUXw{rD=8|{%-iDGIN@#d~?MEi-PTU zHq)VS(f14E2auHr5yw!@?Fyy9u(2riQRfnONJ`ZxR_b!3$M_tcyhOMrgR7(*#-jz# z4T;KxAnQZ=%YM$*u8s&1qiYfK#WntuRiheywI=Y8jxmjL&tl0}5}!|M8d@h(R31Jg zLWP_cb@P~o?Akmpw=WJrIvA_Bx1g;?eV?Dg!z0jhI1gH{cbexV@=7t*)H)H*b023l(zFI1YIv#>mQ-ZXS2&rwSx_-Q zyh_$=GeF?|+amHjQddWDsGfLy-{(7W+vud1Gd1H;UyzZ)Hp0#|nYYbe}pbho!N3VU@`>G0q@GHdUP zCToA8k4Ll5F;``e#IZB8U7`AMWml#qe(VFL6^2l}g?j>a zvubH3Pr7>xQg1w25K2Axa@(+_JtT}}GnmfC!Bik85?CVG?2Fi?mbNG;kdM zH+}(5yjA-{$)6z){knkTghn-Yb;=Bn=+~6)B=7jLcx)`-ghgqZkk5*sSMAU!xfuNY z0z|8IUM|X)__oG%PS8o=);q#Gmt?cjB@bmbA57*w`ccJu!pOopd~@_!oYW>(?x8HH zmc`gasg{M~MylIvrmmj;!%-VM;6+^029&Ez2F9dZ|gf@BX{RyLDyl)6*$^`*0tt&udkWdeT&ec2)Ug3cJCi zYE$~J?q?Bd(;CXk5cSq8+dXL&x7`!=`PSbTo7)2y4l8@TKKx8@01y$MG;D}sgeOo^ zub`-4uX0}pdVoloM_z&I!*(g23a$WPhn#OF|NW+di|(9?UJ}w@I^VGz&D5pGWT*}} zuhnfezO?~BjGS-n@aAs-U|Fan5MJ;C;sd){l??E7%y2DgT=ZRZwN;QHap^df>;?-= z#9tSC74#k3e}(ih)M`NUrDwVjQvLDX!y1mt+sPQ**&DVx%xoBE@LQ?P9HPD4VlK~h zcE+jN$d97WPci0`avZOLmOvWFQ(#h4UrqI}_252hkpZ~z3`AR+Zj-d&A-gnQD|o6W zv;YXgZs_JXPSdWgi*KFkALM2@X1I7URTX79>D-6qP=rEsDc>J*xd2G$(jgg-HYqlE zsALGpwfS?XD z@I3`KX{ypx9-eb*jpe-kwZNh=j{U6I-q|b1nFHn+EACZSXqlaiF{}OcS)({YOMnzJ zMT{lJ++v)db}aLc7*oD7CcQfQ^TO%vC6=OUjGt#))WEVRJs~#cOt-NenY&^;5J4jQ z2G<`A$t!jBvI6RmEs~%&sg-kucTeBF=1-`=zRGqXr*D!tm@P3k~n=t>=(N_ z2z%eYO_Y4<=fZZ$qsRj;PPDC78zmxi{i;Botmtp^2Q9w~zfx#gq_hH|wv%A+8s`gO zA&HsV*w`Fep0yDjpMVtN|4d1Q2tL>x%jrKA5IAhADb{_`Fc^Ci7iqH;D}%U;NZp9T zGJ_t(U81}VmI7WQ!I*&(vLL05O%(AJ>z2Qf^q~Bzx@}LuU%dBI0EB z5X4Txvk-R%Qu=oE(PanNjqdaY{(4bSCsp`dAG+Y#-wB~z)I`cl=F_};P?F+{5ZB(zp(w3_l+Jly~u z6w^`_JQ*+h;&7id7GDT@q zxsKIguH(P({Zj<_C*=Jvy5wkLN8}q#?2S!SF!024+P35hr1}$1)@FOr*%&eKp;GmWw|o`y{gpF|aT*$h6;V-9 z>Dw?Ws<)tUjYU|%=|V;Y_r`N@Dvamj8Ij<-``=7872KBSy#*Vl80f0cBXRLL2o!mDd9mU~5`aZ#<=|TsESB+rpb{8(1sA{pr|%a+!%~DE$Wm%|9DkGd_q{)z zxL=phYQ8nL7B2D5Oa_=mBUsNHEOX{md~^!L!ktv8pg=&179Nm7?(G1S)r>Bn|Eug- zSkJ~jChusl#~2dH*}HJWM17LajbiX_QNv{XG!^J3>t%CUnQquQABF7 z6)E75h0Vi}iPWb5L?*(BAWcn8sg)J8x;YiotXh#@W0|wL3*|K!CA|w24Aq zVF&p+IJ}elVN;U~O1w>H_WP$ZgRJ@%Ru~^&_5;BX&di1Y-XP_^ z@jqY;-~htSi6YH!9|&2mSNUzd+6GZl3S(cGK#)y{p3WU!|O z8fb%8RiI1U(5zBn_!h84CZDaaB6?fBeQGCIK>I8?&^BZ21EzY7^;E^eU$PE|eG$x$_+YpE9in#cg4mX{w2F5sA<|1AFji9r^0+5TFk*B|Gl!O!iY zCRG(AUDF;LD?{)71fmlfgG1(LmPmC%$s@Z212l=wc28gSH1nXXzH)>8(xLjTn!m+a zJO(kd<77-++H)-weAx+&SDh}LNGcQPEWnBJK!+RKGvGEhoF&urd(!FX;aP}P3L{~%*ur_$UXxMu z>Fo2U@+PssoFw5){ps&0*=$5C0s?~FY_)tb{|>^D5*+E;-py)maJ(fyYwXxU(#<#S zW%fqr-YEJCD2NX}8i6@7Xb!7^VoN*~7Avap%q7CDk8GQ^+hPAzllznyIUNbsaKi2xIP6rvp zpAAP@Kh39D z4blNGK}#f5-l|rOdBbYbrN!b~@suVU=ls3xg}dNwb(v4t$cINqGlCzz0^YU|DcLkJ zFc}UkXGV?Az+{S}fTK+szIpcl+&m0t)`8?gNV{^L8pfiLxyZt())9cOMypp6N7}Ye z!-N~6_i&+x9Q_y~i215b>z4U4l9De&%)m!e&w;y{4A8HPxa2+n>-*1IIoQu(IwW9 zB?3mFQY!&y!%UF=k%FWIXWvjM|JJzT&6O1rzepPB79qw1ugx+kjW{=)^06zVot{1f z7;?oz2{sDP?72<)`wwGMCKzjVZS6-&bodSEd+D~?I$Ef#!ob1!nHWcTvzVPyg^C#Z(uYw?imyFb}x7 zjewEi*O2b9oR_U93VENn>;gf1`4?vuhRQPf#|3>)Hmbg{XZ7>oVy2;@kX6BtjyV8d z{hwP)N{#DFU31Co@56IUFDcni6>W+Jf>36#$|Cac*Q!y?S)=(j9Zr!uYu7GyC3(EC3`f2KqkrTdn~AZF`O#GUg#w zfVe>=`GKvnvdl25?B{X7DR7&or}K9nrjtMhT62#LdMV6qLw9y#e*~JE=ZmTvnA-+_ zLqc%Q7tC3ZC>@8;u5xDwOaV&M9|VlgExs3V)QE;j*#uRPFhGQ5S~}O(PKp5F$ad_Q zz&uF^D)TKw_8+P)7#$h&$as4Q_QHwqL#$K|YJ>XK?h^zwDc>?Uu$rzZit;Zl15c31 z_uT#5moLWh3~|_Mao0ZjIKY4V;hgY!7JhnAL%3krgi2c;oW}Nl*8x#9|Auz2mm-a@ z%rm=>Yd={%{(idv0`oS|=(uu`f|Jqec^+86KX-o1slgJd1=rmYTN|#q_Wqu~6m<^| zs5Ybn^QX14tI#H6(@rwD^qma=ls&*0N;janqoX6eZoMcu`O#2f`bx1g0IfMD0q@Ee zIMV+k9~bEA-4R7vUCBm#;=OIPr?GHzg!aLKhEmCzv)_H9*o2_>aH#klHl0i`#uuP2 zP}vCpyY9G%8O(vp1qpK0?<_oGC-Zy6NW9zLPJw?585oO|x;PM|48q(*HoU(tExRPGxw7y))87UW#d=IB0!Xq*wQAg( z-ezUnshQT3o;9?hfK*Ad>kEpF7C?`dtBT)9>?|;*Zu#sMtqs>bh?vGv z{$rbsfTLukw<-~$%PkAgmkACj{lt#UO%076eQ{}nMg9%Ao+(ieBKu+!#qK63*lcS+ z-9Bn0#4FeUP71hA1{jMD1`FZj(0?Gc&5U zw<`VbGh~J)A06=Qw#;zMynXKi=e_R?Ao8cyUr0A}lO8~3m8GdZJm@H!V^AoL`Ae*z zGN6b747uMfDy|V1 zP+ZK`kj?bT!wH3=`gC1dfl5w)uC*EjVw`6f;&EPRNzC zlr2P&tIbD+gQ8|!Cl3dcAG>cdd0)=mPvE&G0$|$G2H2iEKkjom^!fA*?}Q0;M{vH9Qzh=Ig+Z7y<(rdhdz-+L6Y6zp%u!;r8}9EUsVE{ql`QeCLLI|At|& z%rNPbclv!P!7m3MW)ExH5_I1FPP3&ts%_L0ZuIH*&2>OQwDd~b&?UWIO{ptEGJxp* zFuC4pba7D7z2A}2I@k#ZTt@l{AkzjLkWW^XZY!?wFT3m)cYiuq>Ey4S-0In%CXF}n zpmkmF(G(W;;s$WV_*A7dD2~fqXZvM~Kvac$qp_qk)%|<9k8_s=Bi%&j&%Vw^Mw{6> z4AnfbcW(1nOpe1Aa}6fbo^($431JI<21YG`{az6ZoXc&HO^Ug-UVT9-hZK0Z^jHqxs$7&g*;^coWi*Nx(uhD#J6L@ z!pWeNcu~r8B+4Y+X!D@_yWPUa1mCmobw0a3>yg8j*lU8{lg?c0)d07dcl+P?jBrXz zw9UYKiyGgw=UeTtz^2D)MqT#J$@)Sx5#(jhG!(jC&>-69MrAl*nKpeQAs(j7x9HFSs6&0+OO>aCooMS)qi=cyYSH#z; z+F;#Ge|>p`VA0^#QrAgX~^uV*zqq z`Mf3k1?Y?R-6-wMu^Nt=w9C(o26~;n4gDDII_O}x|#F;WX~Fn+SgfjvH0Z27HJ!+=u*N`28DEC`*os`n8SIAJIqq8V%-8K;D@X zn9Vn)*p!dT5`22uap5ml!!0?^*rTjNg zovqQA@6)g3h|=eCn98qLmF`b^)#rllj(*>Kq$5Ou6(UOK9Ab46_Et~c7a%4ZqG_F=&Tbu{?ggm8H9JjwrVKsCoPfGajAUS?HLi-Y3s zG5zo^W~r{tzbOu5HMmo4=)(x{9S6sagjYUr?0@bi)u6u^Bk$Ovh_B1B{7TRt7~iqz zhKb;xZ}U@$6XgQZobwYz(JPCN!i{dzZ&0kNR2_VrsTmLOt4x4P1vr80GhO7$NA9u% zW)*kZIj_z&YLA~xg*?(=W~J(KBm+SMDGyqp-~`CiS)ELxy}~xHa(Ry64DX8F+j+rx zr?N(v$}y~=UB1}!Daq;)Xu$Q@P0jLnY9c-Q`+(2!hq=Y~xOsB+>};%PufnjQ{1hK| zgMs&*BSopv;WE$+<=?lHrWAZ5z;oY8hjm3;HzvQ- zGi~LNhh{5HjI?*NigP~#Ytt6RxgWZ?A+&@xIBISlJ1A)Kh0SN%ceX{GEz zi!a0i^%m%8`-JRr<1i;A7F8xvTs*3gzYnN#yPn;h`{##{nEEZpRh|G#=PWK-YH}9X zt&gmPO?`{j!lQ)h5{ZGZ`9&2A-Pl_<|NRa_7`6*k~(H9HZ zx8j;$HtIF1tn;`m&OH|%l#6`?lJ6rS!3m45$I3C+U*7j<`abqUMuR+;6Q~83E^G>Z ztH{m9HF1b8PcPyKmEZwAQ#u@(WqbVifzEFGUsYH@Yu}jGt%cs)|Asbg)MEZ>Qrq&Q z5QB1_7w!%X+R7B^MlWu)+R0WLIb^g&++OY3h%1cf8nw3XQBwz8umHP2GSSC(JDa;w zm;H~$JQkz6=K_D3sF1>z&a33cO)8VSk+=q#Vl&Lw2c_OG`Kudy_xJnW<>HRL-^r7| zcx^@u4gQOM(OyD=BXL1v?DJ4DX9Y9y=rhCNrq2M*ckW0^^6=0^r}7V|6zV``)@DlV zNe}_9RY>Q$;f4AmyK!B=)_MY3NE6vOJ6{t}jT{^`)@W7kSULSjT zs;4?^dYYXT{RGy>sKP3nu?DGqtoaRvish;EV`XDwJr-C7vtU{t9k(s`JN$l2-12Md zgLw(QV1*N4mjk}w`(z}h^Pm^YAOHHVmoia@6^@7}e$l(w4U_q1;F8bwgA*MyFX&2^ zS8H-{tK(I^rsv{u$Q-^WaF~q%3@8ufujjaR@M_EBN*sPwFaSMK3+=0=rUk|VO@4#? zdRP4Us?V`rx)#OZkoyY3_b&w8=&)2$dd9N&Oqf`mZYD~|uW;G$6|^P#!dR+sxjMH* zFWLj5K*7zxexlKL{>NTPOBsR6@mn zi|X}<>3Fmk@426@&L6pKwgUC2p6~+Qk|$IKsUAqY;-{}3Up{ouA7hv4J-N#9jxau~ zZYDi*rQ{Vl3u1%OH1CeoJk-y(x((TBgUrrWoccCr@x#WP1{NJ#8&hsZ_XAxOcf?wX z?&RC=zrcSFR!c$!nGMha$$j_MdLb>8t#ktA^@rv(8u>|7}5n z#2RZ0F8juVBr?Wwf;de5kJ6k>&1#3dMJ?wfy^N28Zo1`TNADi9`JKkV@8`GPoZX&_ z=g$SXWeB`ZiuKW%nVJ9&-6dqlRYt&YZjgM7zNPZpdjiQrtgX8Hj?J_!cPp$cV12#~ z=WT7fX#G&VDX*1+VyUR38xpg+a7Eylf*P}r09}So ztGI3ZDI6Wh!>Aoba;iPWv9Z72h@ECUCUwz#C>t^o&y=|O%~$WR>=}#iY1+DJprpX4 zy`C-LLXH#coj<+~bkp7~w^-;h}Unn87U;OiHzpXgYV%0n`Ch9LX``Idx z1RS*XE6~K%XWZ451Nz1z)7Bh52Lc%X<`LJ#jsK2Yo%*y@$E@PVZnBr6b#B~tR)q%dMcb^^~6e#w1^4@rkOB9>`Ht^LI)qea*oNKYDfcXZc z;N~P-MD~H1X%+7=S>i#&iY*lT53KmF=V{Ty?EC!BwFJiSJuODBUjasJ#0>1}+2#B; z{fY!uNt_L$-%7bLrX7rf8At{}+kaKux_ED86-|h9k1TcCiJde#Bp{~Ra{~_VaCi;BG%i=0#E*2XP)H z`1U4a|zqK;k;fH?iJ33%Z?sEgOrOi*Byk% za|Gl=yB#gAbxRaffhVqqjR)h&$#$ne>cm?X;F|oW5r7aQ)F+NDus(lX=Zk}jkgU(u zdXZO*$e$+&o*pM+nqb_=51cD@h|P~w*<$le!*@Pys+CScPlgh2#oiD#TvklO*2;%~ z?Rp0QsBp#F+W6q{a1XQwJ~W+`0E=O>n0%ikyIV<5{<{}|^R~A>;BPAK=QBO?n}c@e z;=BO%TeA;RTOgaWLP>EI7f2C5G)HVu)Y?b@{pZ%(Btcn?gje)$tYdmEdj~S@T!v7i z9kk^(fsG*C?k5W8yf>CK+Em+Iw>7{Sk*cZ*T)_<@H=n+bn(YyzWXOev;`dg{M&cZC zJNF&!_}0dg3n2r@f+Aw+%|ZaT(nYvQXi|-j3a{DWR@L(ErSNhmiVxj7jufe16LsKcN8(>Z^&Z4~BqeAO(vlCVX4z(Tevm{kHRj?VS4c~W|o)7w# z^$Re3%gP;?goaECcKlS?1zv>mnL!B}#IBkdcx;676c3}i=KkQQ=Il%_z)q=j*#mzc z+#?jlpA{ZGEsBtwH2w5DdV=IJxUl?AQItY%R!&~v8+ws6=+!axeHW=Jib_&y%Mk^C ziZZ^vs z7-Z%W1FoYgkcITN`Uf7e**H@`=Q!!qVz-#hf`rV}T&~w=2jiQ@u)Zuz_0c?}GH(4Z z7L^t+GXkw@>Y+SdIRdRPLrx21l8@by;wQn|vx}Xsp`h=%xGO$6HELlqQr|vMpSnGz zcpufny4$Y8w%G(%IHM$Ntprtb1$2V$KR*fB&~f*q$m)&6k%`z;jlBY|FHETmnXY*J zM&JR0<6*nsVP~*{xZm{MRzlUXTlvA^{oU`oRW`xZGyd~Xhl9Yg(oX9^BG_Nr$oJrh zpvO%1cK@tqoG%_+E58m~cUFBp!o%4*mNro+oU{{-P4A9={iVfhwD%J1Nazva7XS ze(pCqX8e%NER=)7lha=6hw?SQ)B`f8IOMgUTgN&Nq3(K*a^<+;INfO?(U?2Vj*+(4 zj~{{IQN!YHI3o4hoWjQ{^o?sb;Ybsw%RZ}PU&3l7JPzfzx+4*lg})>|O%UOpCk7k7 z(lhmnZQl82<3oO6eTRT}ewYJePg#UsN8odsZ+l;Elh`i*IN~LkyiT)K zz{W)|ita<58vETpk-l^W8xo{hq@M43>$n(xxn6=x&5|g3>jhdf!p#xaZFy{!=G-@G z`^!p3MU~7u-qN>(YODT!NmCYL=ZihpPOiV=Ih8h`grn>6rkYV2EBJ=GYM39wF)lv1 z+Pb!EwNN+L@Z3L_`u?P|aiiwx(LaOlm(qNlW%*K~cJu6C8`z5Dl|y(QHN+V7M}V4P zIgw8H2h{&;!n3=-8&!X)l_b|EqFg@ekp_R{XOpRXZpFHods;*q@RvI8nbjvbBPT`@ct zMSBL1NnUIXByuP-A`p;Lg!tiYt)+Mj>~eC`RJQIASdk|pnT38Xg~Th{zt6~G{3wAV z2u-8F#0j0aOEgYejxt052O5A4hp}+&Cc-2meRZIipBk=fDG-|4m!B?;YUf|{ayk3{ zso7l+URvG8%*#KJ9T<<24?a_F-mJW7kq@Y!lNZjgUk4 z(q7P%@uY#$^81|)(5?Jv%cHu0EmW>s9u>|&=H|ah#YnYUum~04lD)FwySLOTS%!}I zU#qb&fQiWO+#g889<7I+p?Ke)yBc(wJ`R>Y%i^*KckxMb@JjYjjG&m@vomD?elo@a zmR80&ZpfOMX*ABbY!fZ;#dmmj%$A(Xea_v8Z9D)bb0>m3JZ_N@|1g|;FtpLpwBml0 zVI5Q+SoZbm>Of#;?!f)-W;tJl8xK=F1HT%xfr2Yeff%6%VbjIzne!pp9UWFI-pIrX zMKhH4jv(|Z;QZhhsgK^vX2gh>As5IkobpG77p9P<7PqM znt`?unn)5WJ(&q6>mHr+QMzc^ec2==YS=b*9KPEm;|CUR(^OXQc5CEm$g}wxWlDN~ z>ZWL%n0S|Ob}4u4xaV_i4s~DL&*qDi%B|dO2A5oLSGs23B*q^G2%&U8U|A3f6YK5w~{HJl_`t8!?3u!@Q5amffxZ%3N)47?rZIJYf z{&O~GDJ=Is-cn%WY3%g^tQI`l_hzH_0)0gqBBsNQiX6ale0gdQLczHQd z#WwwdA`zUpPPxZWevDV==n@$qtcp_EC*`wU4c#FK^@gx2@zmSDS=- z+wVBCUX%3C+6DZrJ)gj6hZ^_##Uy@Ozd=SdC6TmCG*bG7E0?W`v@m_PJOPQ}a1FSRF#b&)-h8v2I|Vkk1LUgN@Je3aBj$j;(C2JXLTBpp zW2Fz-W-3W_p~|#UIEYixO!xkc28B`R&BzhPmKq%-OoB*MtPv=^tvD3%ssy(09CFO# zQ8j5ZVyR4xPZ6z`&5YRuGsH+O!}2Ej30xWs-Z|02t;E<0FTr7_mV*)8%q3V!p_NHF zEOc^oXS-W`C%tbP2^@+z*>+MMtIB(}On9?YzeH-XE)<#(6!&UCnB;o^m*Wj`se7x1 zm_tRdvEY%h*}5`*QUwi3m6}&4GJ?^K;V8c^%c@>p=!Pec)r)QWEiRY zo0F7;4{xJ}y#XMsB9h}of z{{^e35opKa{1h2xdz_z<LENqqg|vJ&LZs^A=o7OR;o{Gnj?V$$|=SD<6@bK{BUaYDu?E=yg86-OnJp-XeCR?3_q zp~gcczI5nGlJV%%VKlq03`?rq>^f_P7-gB1MXF;#F7GG0BW^eI@5{gCjmYBdT+QHZ z^_ZP2gDlwek}wCe!qT!EmZYARs-l{U3|_f-cD7k+d!L5eWp%Bfb-9GWEVZ3Eww45X zmWNtOvSmmf6weFdkTz&dSGuTBjV<-dI(85T7HOOLDa;5Qm$b$)81a0mzq zd`dp%Hk+Ifdh(o_whaApmlFEeBaL(VRNWmOo=L>pOYCWZ(Ay3jo%oX3kTl|>-o(9S zT*Mf(+#c&S(Ej$>-e)xtNK?FI*W=pVYHM2fmqilfB-0um1r>q=XTk0Pi&q9fRDc{x z_ryBUW{ec@9oCWup_jf(x*K>{6_};+-7|T=xovAo2Q^7r=HPG_YCnEF&WsmZcovJW zGzg}Gm1HaR)D04(x7-DZtLVAd5yY`hcR-1I< zL`5mQ`qyMuG0;7s?V2h{UW3f5e(!+49DmmtgB9ftyS-;;#Mr(H6MKeV5i#D&4p@Q> zS2sWi5L11y6kd;ff-$#%Nom$XF`&`?Vcs++T6hp9rYP%Naz8tG*S<><3Qy$Q!)_aBLj=UkS@nFOKEb7v%$E%TThH!bMPcpnU~tAZ=TU$V2hOrrGd z$)XXy{M#GL^&m!p`{!yX-VD_Z0kXf+FzLM+BHi@3x@%f;vyc#^kyN&>%!SfS-ear7 zAc<&$UxF$%?!8#;A!9ydwM=W?aCS`n@X;e)x}4$*l3>T4$e}rp)dE(rd+hvyi`z0| z)T1^27GBw~t@;?Of!DcoS1n!3l&+WVTkD@X^p{Dc`xm^NdxQAZ)DEe>&Y)eeaJi2lBzdu!bb!WG^U;a5Hl51ro@{=~OU%nN ze6`w2A6p9NKq!TuhFgQPthxG`&d}|#AHrN81>T!vDT>VqaZ7b%Jph1s6^6d#N!GKP zNKsB&bgB|l$~G#58sLAf{!JdA=)P$`+)Sm>`DIh0y2$n>S7`~8CrJ@ zy!`vLYeK`EEG*2Pd%0LDdl+!^OLH2d_Enu6dp0WrMs&JyvL=`TZYD8xu(UTv3dPbw zQ*-;wI_E2Dq1J3l7uzsIw537Cj&YY4AZ%2>t;6h3qSnX_I~)O3lI2ena_P19s&qW8 zX%P~Rig}`Y7RCkAVhWwe^YlSS=6EffQ-doV$wsx+!n9=m zTfqN&PCTbx>oSSmT~BPSiPNCFiSCmB{+?Z3&Y|>CqgSQ=S(w(2Qz2*BEI$+byEPP1 zv*$V^D-E8*{E5p!^LAR_GF2{F!3F~}nG>_yLu0jue{bT~4x3X}=1zx6@<$t_+4vd@ zN5`sZ>_AP?^gZ66!crp_4Tc*Em&~f=IJe36F)r-cX8)W|nAm?pe(C$zyG&YHc1=qB z!J_WLi(|1zk#dP75bd?Vj(H_F#^4J;@xt@h`)kV=>`XXE7PPL2s*}WSF3!i~OHU9O zS083g7|&aX?s`s%dN~7K$v6q`skkL!D^7{!vYbJ?mATFATZiOUgLvulOu0do_^p`B z^5Mq1E)?emJlhzjn$X+C(+^b+F1Zfu=`~dAT}_mrzR>b*i;hd744Mq5%5VvV+>_Jq zzkW)HHUhc^a67ZLaI``TR<5vOAx3ak@kgP>>htWD-xY9*P~@+yPN-N&X84?5CbAwT z7g%WZ$P2;QI70`LCL&sSvyh^GJ$NHv#8FPV`~zpb);VG$K1X0Cz(wAO8N6+%IJoBn zWM=5>v9Ey4j2E7nJwq^)J-%OfFoXrGpYJo}w6AqZ0A1G%ghJ0xqzF%IN6eV$JHfj9U05f8@iBZq+7z~$?vBMS{q~N32X@x0z*9yF&XdR3F{A6<_- z^ZA;;5hK>WRA>$?VKSUPygoRs+!jk87A1j8#4+wAkj?H;mhzrD=!r=ox;YBSv)17Y zM+2Ll$7DGTe-hFTY8@A&?@&0o*Bp5X4W&5qM}8WaRti7vLOL@jNKmAHCEMU%ZrI=k zR+EMP(X)z~XHc~x25QN~<^6z|@i6H&d7@S&ro?B@G1Yon z65M)NvV3y$ZAisWdFR3J;_pA*_-A5QDb`NE<`Qkf23am=F64%N1g0#2Na1MQaZ>IR^m4b z*o+D0(K-L>g83b2wmHL=ci`=B|I3C)m)XB3LFn^7Yz9y3e%EOS8KfgDu#agZd1V6y zjo{luTh7KW%gwehO{kyqMA?tA<|}qiT>sTno?lAKb(W9BM&Dz_BzmAf*NzwqJP8ZT zJMse`Z3Z^R#j9XTUOwmdjR!SE02m)i_@?--cI$w#CdL0t^NBX22kOEG^qcYN^77ke zYW&l?#>NtZNRK8M^`pr_1EsfC(n#|WS-zQ85(RZ7dm6HW`5`ip9@jBcU-x>KmzEB- zz1+0B{^*`c?z@ckz8ZPXW+$#DY-#+}8m4G%uJrZCYW)MGQ}?7l0tBZF3oRo89&*n9 zYvV1aWATi{xTe)-lyBS~Zn5X%Uz*mBh&hfSb3G5sK}G4#Je7V2JsqGpu<^B)u^rE} zSSpO6G*562?YL6?uxR4rGBVVj>R+I50)1*tM{<3x_a&_A^2v9b-^*=XwuED$MfFc% zW9t_Imqf2_Z+CYMI#t!$gYv%uhuw&~GTC%O$LMS+o3GZ5fE*nSFYlm)NviVqn>1lA zty0L?AaNhj@MWm}xG}SU+|BUMix$s?2ofy@5MD*Rh?USpYK`$hW6iW3v+vsc%nJt| ztGf0?Ey(wPqfcPOSh5RLGCE};rFp%zW;ErdZ{KJgTwMibWvp_03+pRVy?%;Cn4`sR zIA|BAFApls2AAh6n1dJIzjnm3E{4&)rDP7xqsn|cgp@hY3HRWkTK5*Rd>>9+10 znR#4wSv<8Q2&cZIbdV*}U2x)R_&AX<22Kg4R zFnsn|fQ{bmNukCLDj;I?q-Q{czoPwEjbzq#_&g+SDGfZ=KLjq+{?aOC4juYeKaa1k&(H+$0xQzY97KOT(SUb>N! zEJv>u31s;ieGq{*y_Mp|2`r!y%#urY0USRKa2Le0UluQUZBuu4#B`P1IZS>*u{Q@| zNLy}j`aa3LNj5RGOYo1%{`Xr}xd=9<>Qa>|=AyaC3SVuJnZYwy{A_gaaca>ulGrH7 zxZtdUe@9pU$i3zGM&`wqBU+A5@-y^Q*^i;d22H*^a7w3B0rq5X*xCSWhkrB(5rGFo zg-Sk!8e?13vBC#L0|vw-Q3nc#G$GjpUupETm&)Ui0igjOM`>U%)z{idWwb!UBJe%} zKsS-(=Bs~$(ZdHr&Ypp@6!=JD19K~*bv6Xg;0crgvt^F$vL@IX-Y5Of;J5TiF+Q+H z)gP{7$IcRRY#?7c;22@r{~}~aMl!m?S_h~O7uFl3c@j*_XFo#`8b*8Ic*=Xh?GW%k9Ha9HkAyr>))f(cv$~5$G4on*Cm0_IZ*CBRI&u zBgDV#j-KUgjQlTlFd~Gk3i96#mg}Exl@->fg&MP@GJ|y_qcecFopBKfO_CO=RP>q9 zn~*I{jusw8R+dKs@rqIZ&8q7M&FfAl=)B=HrF}VZLsP5&CCcZ5b!P@(wx)}mB7_)Mk$M^0I3DwY6`8Bf=0_TXgy?gCVS|75N1z*`v*49Z*O|3ws_B*?sMS$)7G zsvF%_Q;L8DzlH!Jaa!xPgB+v-nt&0J4!rf-7ob*B6_9%wx~(oxH1J4HU)W&LXE@)G z2*1)my22(ZAslc~V{rg)eLW?B>joa68-%0f)&#&Kg3+jns!VW|dCn*)RT+YsZ&GCh zOqG??@P8D9pBDHJW2V6n1Mr9|0WMXJ8e9pnRe-4@;flAK{sN||^ST~*>*Ij(|JAm- z6h+{XB?c-{0YLpfB9mtVQ-4ks@S)umu3+Z&R=HkP#D$fwGg!T4MLTX6Fk;sKqmaP< z+pV7osVK8#z}&qBz8>PhInRd{qVK#yB6~og;?uB%cf8kLFV!;cnj*Oj*Y@t;1H!wx z^h&E#Xr5;Io)fA<}q5(sW&z77uE zEHAhJ;(v|vAs2Z+^dHA-%A8gP)BYaYA~$#TZMN63F-O3)B}sAy{k`|tLmYv(`boPa zhsjL)?cq{AGgB08iD!W6XV(AY-uuYLU&lGFDZ7V- zVMdeMv*iZ7@1!*0dJcG9CG*Go1Rtl>r*@Wi2ivE49l;fjDC}{_j6wi^J_kIT4}d!n zdr8C^{c?kgldn;}e}fn*Y$a;^F3rgCP+Cli0B=={z zcH#jK{p!Z6Ze`HOfRpg1{JeE1uS(Vc-_>gYhNH$n!1Yf5B^Th_X+nPDM|n3qnaHff zAf^>dG&$<5s+Z}$!K4dORD){>wWL7)KMjfN0*;fqvXA*|Cu^O(*OI~oa5|P83#nBZ z(OdB!1%B7-k?Ir+wWqJ}9We7^&lYu-+n=;7Jlg)bx}S11v-3gi%r{y^U7Z~@K~sy6 zN|V+?N1|#Pg3SNdq7cwWj~$=Vh(T^e2Se#BQ!u~#(?LN&8jKHF$omO^$hol`^dN1C ztbd*+h`-4$&o8&%e&K#Gqzt!5^HF5WnmL?;NQ)YgkrzeNX{XowP6NiTF=;}CX#i`% zd7f9nwxd-25DN7#k!qQM>nz;(259qEXM*a8Z$hvzOAU@Fj}6E2i}E_$wAC?i#WuAQfgw4lv$ z-|irTUE(mM1zen@Z`Qw4%M5psG{09?#M_1FR!Lq_qj|$#X?lK+dW!YEA9`|m_TrEZ zU4dNyX8&hczsf0j^}P>_lZNlqMPzEAX34h32;H-(y34b@!z^8PzX34javeViH-qNCbrvbSq?diy! zzi0iX_01a4-oJl8I@(m0ohjJi0zZ3YfEq zJSrD-OTdx8XC}rdyf2xY+>ec6E&RU0M!!bJt)5DB=2)%q_p*RycIcV0)2R=Np5ak( zUz88)wu40B*s8o%Z*THDGqcr}F2X1PP;|)vGi#t`>#|7>_LZwXEzx^i}PfuitbBTlMS+^OErX14y=LPM|&$D-OQBsREs{ zV?60j4l_mZT}6vB-9&s&&BReIbQN?2rBcTc6(pbSb6=>VNas}Dg>@eMh|0LhQj1Yh z9oRfo<+mp(gU2H{Hc?&cW|^+HN)!_~8|9sl$;t6%lMz+C(=^*F(PCxosNX2&68>4A z(8I%3N4mZ{?E$UjqN|EcNlHQ$G{mOmDr_K+k_5;4mcC!0ASEYPsxYYA8S`)@i3Q}z zEcvyr?z|$eSS0OABp_Ms6=Jfh88eA&w^Y4e3YxL0PS5#!%hf=;rz}PKn3PTQO-Xq0 zLlMpupPWiQu25khXpGU<6DeQ68t82JtKHgqmmdH2l0D@gwEHT9=(RKIFwadeKLcwM z&~!j^szdY`bEAlFwq255mf(0rERjAH3L`^vG)&nrU=v^~r~QQ2$1ibihtbDB;7ft& zVXBOXt@QOeRyQoP6yiE)Swku6ru;4{&gXhmib+>bU<45&h)N zqg9slYLpL*{Fe^Ze_Y@U?K7*84AWb$90^hf!U^2+*;L1=g$V{eFeATvf5YBPX}dlP zqpmAWrIa-GJFVVq)wUm6c=I&X4U)zbc=%DCqVPaqZVYHRg6;FX&w^umr9fMTR^Uc zYS6?m(0N&^tbaKD0Uje?LZ0q~5sgh#P4tk7r3DdB_bsQ$D^GO zKeV)OeHI*wAGb#`664wp)Bi=0873fL->Mi`MgY;o>8h=}1dCr*UbJrzll{~@d{_Cz z=Z|}FcnMPlnUg3Qs8D}3E}YkN)?8Qot88&nE^&bg%?gUgS>L&~p?_&q)$<=h@z=gc z(K3j-ByheqzWR!jD|mcFkH7b>x?n1(I7&Kz6Iq@!$R5P+4w?}&1PyF^&-`4myYX&$ zBb-;mfuNT(MYns`0aIYi3KPm2dill)7=}FlV|;6o*aVt0qgTlpuYoks20IbuYykOf z*l!Z1L9AcV>;W+!V-$93f9|1rQ1oLL^bprNYhRb~@G9gM4(&SzeYCD{aw6g>ie>5X zo2G9y75%fWeNEr-5g6&i46Vy_D&&3>hTQ$}o!G6f8Q_)9Qvwm_uez{pmA>nPJ^uk> z*uK-&T6&{0>;RV23;7tY%oNn3%{HT{Yb=}`uk^!bdYgZeH*lv#+?KMer)$~+KTG9j z)E(hVr62}FJQ1kz-7$bm?C+PGY8-=2g^$F#a5=q2S?2f&N_Y{4HY&5&zs-++V=N7+ zcSttKnsb_CQZGe#&S7XO2>se|5xH3j(Awqa!mxM8p(yU}^qRFi6i`}eLQ*#bD`}s+ z_%b#`Q$T(?j&HKn=>NzLAWq=&%s2wZQ8Vzi$j~a+u9ovi0|~>3#~?$?(7KCo2n|#{ zI>mzPamF(XO0FkQN>84?*sDw7tE%b-SI1-%N*W6X+iMpemnL9rOcu&s@Y0zkGUuMG zoh^tio%a!{7dx)t>%O5y|76)eFVNF6-xsHy2dla+o!VoQEk)TmrrY)LfvpJ8iuB0= z>HIW*NPq_7$NCMRz( zt9cWyLk*>9QQH9|!$JmtINbC}qm?B~Pba!kRI6*O$FSTXT9! zWmz#+FM|`=jj``m0QW_P%IXXoOuCha{*`_l){39QC8?yw1)h%S91Eiah&`_~==7@g zq3w>f7s*EqK**>@RvF!P$P;9H>BA9unXT%qfr*RzjN((%bBKluAqBVuyfa`D)|Bpn zO_^8yWBxTfCYQiV82i>V^YIG=0Dk2abz^4j#kVfw_c%$#Au!2wZWusRmrpC1r7_}I za-13Gw|}*~u7zE6bU2_|g(WX^> zXz#PwtCI6ndx4EAyk0_YU*C*#(1ExACK={l%^@a#wwb(p^s+q+70gP>JamRy?&y?65X2FFYYa+nZ0wE7!l^w5-Y*dI zcls#sK>SV;0Nekv@^{ka-JvL|If6{zdwVAx%TFhR-o0ZK&zr3fFsk6PteuS^%~&w( ze;_69>(yqIGX-2J3qyO?abqJ`ncm37adRMk2-y17Lz#h!J{|XwB14U3rc^db)UuO+ z;x&6g9zk;XNExK|EiTF)!Jca9)G*-|^MGn7tqq~#S5{>6=;i5YzL9ge1sOUQrBVT| z9Pl&C?D$rJTbSXX2}Oez#TaS!5Y@s&lao%?bM}8JCgV#0?i0Ey%+V0cWlQ?|-(Bq3 zHcboIY;4-Fp9E0qxR>&up2ne=u4z#;L3&Vn$`D4z3K(qqL^svg7?nAYMNjeQC#Xpq zz{oRY@;xAqe01dza5%vD8cvk2`y3wuDH4MB^}5%G z`L<@EeNfOeL-QP~c%DEsM}5g`9DiPXqTYAOZohWsH2gF3@M0e6ynOt#>5s(^sWNS; z$GEsyxbj+ofq`hFI2p!N3>v9`_d{h9F4T$SnkV52AfpcHKlU>CZS^t?bE(G!Q5 zA=y7}5JDxmU|b+lmfH+?bgzA&OC|sWO%q6W`W{OD2>k^bFqyW-&c*Y9G!jE->I*-2 ztfo3UI{I-?CGtYa?cV%@calQ@AWMsjVM>vFfM75p@hiIqq$H)6;}wJH z`C`+Nm!P~*6TQg;ZVM2U_tT!L(Rpdntr21}90WG;QJKGAKCCx6RT5E@L-2fDe0|>x zJ-q!sZh4gh7m6c1B|XxwVJ< z+(w3{;P}jjxei^O1&FGSlBp5C8Cna}bB18;s6eD}e3DHQC@kFYiXDpR6Go!BkeGX5H;F%G+ z>{nv0Uc#Tf&R760>7#K!tQnL8HkmE%gXlH6DvYM6%1_Nhc4cWd917!g@}i?j7=Bep za~92%pi+2Yf~3BPo+4^;Z4{%vCuBdPLqdJ&-u29c+=)+k(|AnRV-GO|XNX=YQMq_q zr|^foQ||9!Wxm3~-RPOJ{KCb{YaHVssRYZV>lC}a&mTWR=I5im-|N1C8qURxHN`*Q zSWx5NCt;tDZz9$&+xx*~Drb#J@UqKOl_7`w#csD_-hXPD&(KvI0XNKec~W!A@Y@#| zN8DQ~TvK=$a%l-uONs`=%;36spegM=D*<$fWV=cvf8{p@|gH(&p&_T&(i(atLrVhOz zA13|T{==;{oCj)H!@ctaFa5%6lAM50-*CZ6q`?hA!6fE$0ou<5cqS)*zFFNcP_ul?9je6m3*$pNDv*c{lR;z z^=-7cKl`P?Ny7wC^T%~wdla;W;QNfh9t@PQ?fM$x(y~hgDk@%4q%gUxHaRu-@zb|C zNC>g3K4djHjk>NR)n(?wS2qCcKm<7U2D&jnfI{+!XSJgZ_4P8zTRrFAY_&7wK88`= zhVl&xWzx=~R@=`ICYol)%acX$2M{X#Wj~2V`2m%N_IJu7Yh8ruqS&;N-2MZvW4-zc z-$p=uQZ4q*A_xqQTH0L6se*ofL1q97o+D}C&Zgzxbb8)CFw@*JryAgTD>_f&N4E*F z4;iz%D%w;xXjr(3;QV~K5=k#n5}+`eU(d*oKHB2%I`O4t&P(A zx$}663e;GU@WXmW&b*o~iiUv^#+JEna_yJ0Bf|Qt(H^DYvU%PBE^6(fo4^1iPp7hI zKL_epH63zCIjJ+BHYZu6oP`}olJ{PHu-8v5C-Vjp5DpFzhBJ=|{h@9;*JF(vF6Idl zARp=Tgu#HKd8^Ej-DxZ4_(NRV6F8Wo#vmSK7#ysm_D0A`qh#v_P~px225xTucJHMI znps1dN`ny9g?in(M(w4B)Tn*jL6fkV?7u3a0+fn=9r*#gxj_{#X@e{io*My+Q2(+o z21%&|uYab=$c=y|%)!Co0@f8Fr-r*0t9Vp|iM~NX539}fv-F566L-!YzMbrG9bO=!xl z?7=7I32Q%WW7GV5wvSYkfBvldi+}n>X3?dz)rh~^Dx9p)rK1W2TR{{SaN3{~yWMU~ zD={&o58?u#k6fM2CMVw&GAI65lhpc^w8;p4{9G&>Olb)WX~#@CJ_{-Lz0`U0uZfA< z(*G4B0IJMAfy$_$&|rMUzhyaYd^pmj>+P-R#n<1IEl|4AJZ&%0#XApmZtxA!p&RHI z_+0UU0>Mrto;l#uHp)kC)GJ)HLVF_~14MAD)gi(2nrEZXb|NqLr(H(5v$1ga-20Zd zYPWp|M)0B4GFEuz1XqYvi^F(CJpzci6lV6B`7Fxt`oIm{b@O{G&&j%aP z$T~84YcsJb#9yLvQ|f;Nh=>AI4mOehvjE5(-BmM4MFHB$vMW$k4VpgBHVkua0Be;M zWH~ofNHrQ#65XrC8FBQrmA=x^Nc()@r@w+CZE7A4ud4h5U6Gv>v~IrUzHd_Y4-Rtb zLq=%JS<|2wfeeYj>O-@~9)^ZqqWvjqYRQy4fr)Oc`fpE^=E}@>3>H>@1=Z!KpO)SC%G&_5``kUPZTv3U|-wO(7^f$Bg zL2}?lS(!EumpS`&1>IA>HMLlk20t30Y>>H&i%bl1q$zkvP59sCu;)YEj9VTG@`JPv zT+83;_unZA*DJ_}6U#c_M+57)E}6S%6boQD7uyil(; zu`tU&9pfZtUpCV&Qs2<@JVPVxQwie+?KdgBL?Ge~1gBI#|A$iGKqUrVNJgXJf-gJ+ z%}aKOj=<5wyg4~jnuXbje>jh18}QE&F-6gQJ4k;S3W~2JY%X#B@Zd4gbh{vSE~pVqEC9?I>FmxKr*Ny@&o7^17}5@Kjjl4h(Gxx!$| zR+byrQYmZ5TGXX%$)08AW@*gS46SbT4Qg>A-P;^)IkcK8ZOs)*vEz%B4J0@e-8XdlVbE z^lKwCMO$DQeDgzdstuf1=O2$pHja{yB5W@~CH7MuTHxidg8W0r1&7^8DwW}EUS02o z5UJ3c1I_Z?+Fo5Pue`G#>}9zK0Z8k39HLmo-~uWXc*S&v3k5T+HGxxjojr`6e`gLa zTFEJTqXH+r;f)<`a=Az}VOJ<~Q4|m(I_9}s#(5QmX@JG-uDqIODtp&r_9%!|w%^UV zgt%;+ptG;C^W3jS)Bz5Ej~xbvh99;0p4QnXAk1Y`rp7_L3lbus+I)=}n>G32G zRNdC9ZKzQ7Rer9<-3uv$eGnCCWHER?H#qWZVo#Ipm)Dr<+>*Vgy}IJ~=i((}UtSug zOcwFhm4nPx5X;@oA2unqPOrVdbV%dalymbNED>cDvugYAtJqoOHJ^Q5E3Q`nsO0Ay zXFbJ#RZp0YSEpj~qESq!xX&8Q7dzjtjqF(}uc)<5B>SWv4sIKB1SbffRzF53elh*- z2c7OO^#}1iZQY~qi^!hO?tDtMSWs_gJQYdAdIY1-nW~MeE>q;R5eq3Ov!!LlBs`>| zPpX8MWRFCDOS!~ag~|h`FayYtUcQQ1+>io(C8>5ICp4E zvYEm?!Q6YZ&5(b*$&XE87*f@3Ul-sNGB(Jn^H7Epa19?_Epc4(^ZUejv%tYmkJ8_C zBwr699ph3w+39Jx+RKO~W_Y~=;WsG_)pSRIfLiSJ@bI6UsdoR8FgSRv74fpF3Yx%K zEUSZ@nvwdPZKQVT&!4M^UihWVk3ouOg1pC7IZ=1DsBtn$ebI9(HpHX2v5lh642K~!vbdAvlhH0 z=gnT(co3WAu)3w{vsouby32^pyWi@K`eFfEP{E$jq}TLqVV7Z@A(@^L%wHKu_N1nx zzDgW9{|{nufL79lX%s9hJUW{)DTK#h)GgQup#zK9Z1hQm?8QG?e8_5 zuG-)r+6!gxiOb8gjOBu5tp<#%0@~cC*FABWmMU_l`G^Vn7>IX^aDy8Le&tyHl%sRZ z0x_Uw47LE_*7qJXV=qK#lXxbWApZJ$56p;kueaIXd`4{E!*&pK4T{>WQQu? zqvVUc_NITb^p)n1rO(z{B7C1o$Yref!z+-rq`TCo%CvNL=!;2Y-FJLbL5KG zXf+;Ss^SJ?W8DJY|Em_s?fd0h(xJR+qP=IB=Gv#8v`CyMJWzlwSW-D#)%BZEk5vvZ z^}QR3Bf?v@9dkbY#&nBmMM=`PT5Z4qcHYs(k8I$SQXvBh--v&7KH}+B(MdP7NkTw* zjCcg2_w68Tn6TXN=Fh{_VqV;_O!dyox7)_YgiR`JLP&cumvRpWlME^BI;)E7A)!==P{@Jus8X{sd)9h0x9tqjX)YnU)n&3$Xi+Sh9O|`}=^TpY(&CV;PyOM0t zsNw0hzAw(CE9f;$`aH!VI5w}awz1uNB#D@$$1@(2xwfhVze`i5R#zj@tGx#iP#t#Zd38jj-b@Yv{aE$1SBizUR{NolVq$X@t9={Ua;OBg8}n5tB0_G{`eS>IYw)=y1A`M8Px|oAc^rprEOW2|ZiKh&q zxgCfF<$jtd6hJ>HQkNfyI~NRsIGVS1dTf?NDkV&aA3h|5h`@!d}PF>^k9rm_YRw%&- z)BQTN4#@qvyUK&aaHncq9fiD~L&dk2y?WKS+K>s{$k+*TPLw!1-|Ri!K8OYY3t1-x z*aj^=`e&JNf3I3!1&PdZuGVD z1v7-T^P#+QD?9(stMugPAaiZ%xYr&-lzl6h09LDXs;;TMF5?i#Z-*?jYbl{aHG86WIR zoTe-K$*9kQLe0qP@@M_uXgQQAs^TekOz~v?>F(RaK3LP0is@^=SI+zyPvi-*4aXnP z5!<3LlcvMK-sY)IeE{1u4eI^Eu^e=bG*cA0}NNs-3PJe!_CUpl#Rn-hqMV z`Y9=nUGR(XPdl_?-~jxc5ZS5hdZk_Z`B5LRQaB&UFx#eQ9GFWnn;(pig*s&IK?wvV z150{=?>habA>=Rw*zJs0QwU@WxVeW35%aIt)h*Pt(_DAeZv6TX6PkNoG;nbp#0Tm) zndl?(0{9YD%z*uT`C}%-NJVSY*U?LWVF2RhG&?|sam#RREIpX*y8oWJ-#QbKd}Pzo zzmMZ+IajEO+)u#`H6mYR9t-;7N@;cO->iGS1DI|jFkbLAH?)maTd+X4s?2{a zncZYx^DJ>%Vf}jwm;+4gc5}G(l)}GLBXqb4=Igpz>^|6!V@lY@warO;$Wl!3cYfGre`1*>@Dj1FK{J literal 0 HcmV?d00001 From e862a5091e271e8065e9da3c25540612f98a8d2a Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 19 Apr 2018 11:56:34 -0700 Subject: [PATCH 17/79] transactions - fix refernces --- app/scripts/controllers/transactions/index.js | 2 +- app/scripts/controllers/transactions/pending-tx-tracker.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 6e47816f8..fc6340bd6 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -177,7 +177,7 @@ add a new unapproved transaction to the pipeline async addTxGasDefaults (txMeta) { const txParams = txMeta.txParams // ensure value - txParams.value = txParams.value ? ethUtil.addHexPrefix(value) : '0x0', + txParams.value = txParams.value ? ethUtil.addHexPrefix(txParams.value) : '0x0' txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) let gasPrice = txParams.gasPrice if (!gasPrice) { diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index 98b3d2b08..f7c9993b3 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -1,4 +1,5 @@ const EventEmitter = require('events') +const log = require('loglevel') const EthQuery = require('ethjs-query') /** From fad8afb7d128ed329272f92378fb46ade07ecead Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Apr 2018 09:26:24 -0700 Subject: [PATCH 18/79] Linted jsdocs --- app/scripts/metamask-controller.js | 112 +++++++++++++++-------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 7847b6048..ff974506c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -315,10 +315,11 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * Returns an api-object which is consumed by the UI. - * The API object is composed of callback functions. + * Returns an Object containing API Callback Functions. + * These functions are the interface for the UI. + * The API object can be transmitted over a stream with dnode. * - * @returns {Object} + * @returns {Object} Object containing API functions. */ getApi () { const keyringController = this.keyringController @@ -420,7 +421,7 @@ module.exports = class MetamaskController extends EventEmitter { * * @param {string} password * - * @returns {} vault + * @returns {Object} vault */ async createNewVaultAndKeychain (password) { const release = await this.createVaultMutex.acquire() @@ -600,7 +601,7 @@ module.exports = class MetamaskController extends EventEmitter { * These are defined in app/scripts/account-import-strategies * Each strategy represents a different way of serializing an Ethereum key pair. * - * @param {string} strategy - A unique identifier for a + * @param {string} strategy - A unique identifier for an account import strategy. * @param {any} args - The data required by that strategy to import an account. * @param {Function} cb - A callback function called with a state update on success. */ @@ -649,7 +650,7 @@ module.exports = class MetamaskController extends EventEmitter { * Signifies user intent to complete an eth_sign method. * * @param {Object} msgParams The params passed to eth_call. - * @returns Promise Full state update. + * @returns {Promise} Full state update. */ signMessage (msgParams) { log.info('MetaMaskController - signMessage') @@ -670,10 +671,10 @@ module.exports = class MetamaskController extends EventEmitter { }) } - /* + /** * Used to cancel a message submitted via eth_sign. * - * @param {String} msgId - The id of the message to cancel. + * @param {string} msgId - The id of the message to cancel. */ cancelMessage (msgId, cb) { const messageManager = this.messageManager @@ -685,7 +686,7 @@ module.exports = class MetamaskController extends EventEmitter { // personal_sign methods: - /* + /** * Called when a dapp uses the personal_sign method. * This is identical to the Geth eth_sign method, and may eventually replace * eth_sign. @@ -721,7 +722,7 @@ module.exports = class MetamaskController extends EventEmitter { * Triggers signing, and the callback function from newUnsignedPersonalMessage. * * @param {Object} msgParams - The params of the message to sign & return to the Dapp. - * @returns {Promise/} - A full state update. */ signPersonalMessage (msgParams) { log.info('MetaMaskController - signPersonalMessage') @@ -741,9 +742,9 @@ module.exports = class MetamaskController extends EventEmitter { }) } - /* + /** * Used to cancel a personal_sign type message. - * @param {String} msgId - The ID of the message to cancel. + * @param {string} msgId - The ID of the message to cancel. * @param {Function} cb - The callback function called with a full state update. */ cancelPersonalMessage (msgId, cb) { @@ -756,7 +757,7 @@ module.exports = class MetamaskController extends EventEmitter { // eth_signTypedData methods - /* + /** * Called when a dapp uses the eth_signTypedData method, per EIP 712. * * @param {Object} msgParams - The params passed to eth_signTypedData. @@ -809,9 +810,9 @@ module.exports = class MetamaskController extends EventEmitter { }) } - /* + /** * Used to cancel a eth_signTypedData type message. - * @param {String} msgId - The ID of the message to cancel. + * @param {string} msgId - The ID of the message to cancel. * @param {Function} cb - The callback function called with a full state update. */ cancelTypedMessage (msgId, cb) { @@ -841,7 +842,7 @@ module.exports = class MetamaskController extends EventEmitter { .then(() => migratorOutput) } - /* + /** * A legacy method used to record user confirmation that they understand * that some of their accounts have been recovered but should be backed up. * @@ -873,6 +874,12 @@ module.exports = class MetamaskController extends EventEmitter { return Promise.resolve(migratorOutput) } + /** + * An account object + * @typedef Account + * @property string privateKey - The private key of the account. + */ + /** * Probably no longer needed, related to the Version 3 migration. * Imports a hash of accounts to private keys into the vault. @@ -883,7 +890,8 @@ module.exports = class MetamaskController extends EventEmitter { * Uses the array's private keys to create a new Simple Key Pair keychain * and add it to the keyring controller. * @deprecated - * @param {Object} {lostAccounts} @Array accounts <{ address, privateKey }> + * @param {Account[]} lostAccounts - + * @returns {Keyring[]} An array of the restored keyrings. */ importLostAccounts ({ lostAccounts }) { const privKeys = lostAccounts.map(acct => acct.privateKey) @@ -897,11 +905,11 @@ module.exports = class MetamaskController extends EventEmitter { // END (VAULT / KEYRING RELATED METHODS) //============================================================================= - /* + /** * Allows a user to try to speed up a transaction by retrying it * with higher gas. * - * @param {String} txId - The ID of the transaction to speed up. + * @param {string} txId - The ID of the transaction to speed up. * @param {Function} cb - The callback function called with a full state update. */ async retryTransaction (txId, cb) { @@ -914,7 +922,7 @@ module.exports = class MetamaskController extends EventEmitter { // PASSWORD MANAGEMENT //============================================================================= - /* + /** * Allows a user to begin the seed phrase recovery process. * @param {Function} cb - A callback function called when complete. */ @@ -924,7 +932,7 @@ module.exports = class MetamaskController extends EventEmitter { cb() } - /* + /** * Allows a user to end the seed phrase recovery process. * @param {Function} cb - A callback function called when complete. */ @@ -938,11 +946,11 @@ module.exports = class MetamaskController extends EventEmitter { // SETUP //============================================================================= - /* + /** * Used to create a multiplexed stream for connecting to an untrusted context * like a Dapp or other extension. * @param {*} connectionStream - The Duplex stream to connect to. - * @param {String} originDomain - The domain requesting the stream, which + * @param {string} originDomain - The domain requesting the stream, which * may trigger a blacklist reload. */ setupUntrustedCommunication (connectionStream, originDomain) { @@ -960,14 +968,14 @@ module.exports = class MetamaskController extends EventEmitter { this.setupPublicConfig(mux.createStream('publicConfig')) } - /* + /** * Used to create a multiplexed stream for connecting to a trusted context, * like our own user interfaces, which have the provider APIs, but also * receive the exported API from this controller, which includes trusted * functions, like the ability to approve transactions or sign messages. * * @param {*} connectionStream - The duplex stream to connect to. - * @param {String} originDomain - The domain requesting the connection, + * @param {string} originDomain - The domain requesting the connection, * used in logging and error reporting. */ setupTrustedCommunication (connectionStream, originDomain) { @@ -978,14 +986,14 @@ module.exports = class MetamaskController extends EventEmitter { this.setupProviderConnection(mux.createStream('provider'), originDomain) } - /* + /** * Called when we detect a suspicious domain. Requests the browser redirects * to our anti-phishing page. * * @private * @param {*} connectionStream - The duplex stream to the per-page script, * for sending the reload attempt to. - * @param {String} hostname - The URL that triggered the suspicion. + * @param {string} hostname - The URL that triggered the suspicion. */ sendPhishingWarning (connectionStream, hostname) { const mux = setupMultiplex(connectionStream) @@ -993,7 +1001,7 @@ module.exports = class MetamaskController extends EventEmitter { phishingStream.write({ hostname }) } - /* + /** * A method for providing our API over a stream using Dnode. * @param {*} outStream - The stream to provide our API over. */ @@ -1015,10 +1023,10 @@ module.exports = class MetamaskController extends EventEmitter { }) } - /* + /** * A method for serving our ethereum provider over a given stream. * @param {*} outStream - The stream to provide over. - * @param {String} origin - The URI of the requesting resource. + * @param {string} origin - The URI of the requesting resource. */ setupProviderConnection (outStream, origin) { // setup json rpc engine stack @@ -1049,7 +1057,7 @@ module.exports = class MetamaskController extends EventEmitter { ) } - /* + /** * A method for providing our public config info over a stream. * This includes info we like to be synchronous if possible, like * the current selected account, and network ID. @@ -1069,7 +1077,7 @@ module.exports = class MetamaskController extends EventEmitter { ) } - /* + /** * A method for emitting the full MetaMask state to all registered listeners. * @private */ @@ -1077,12 +1085,12 @@ module.exports = class MetamaskController extends EventEmitter { this.emit('update', this.getState()) } - /* + /** * A method for estimating a good gas price at recent prices. * Returns the lowest price that would have been included in * 50% of recent blocks. * - * @returns {String} A hex representation of the suggested wei gas price. + * @returns {string} A hex representation of the suggested wei gas price. */ getGasPrice () { const { recentBlocksController } = this @@ -1117,9 +1125,9 @@ module.exports = class MetamaskController extends EventEmitter { // Log blocks - /* + /** * A method for setting the user's preferred display currency. - * @param {String} currencyCode - The code of the preferred currency. + * @param {string} currencyCode - The code of the preferred currency. * @param {Function} cb - A callback function returning currency info. */ setCurrentCurrency (currencyCode, cb) { @@ -1137,12 +1145,12 @@ module.exports = class MetamaskController extends EventEmitter { } } - /* + /** * A method for forwarding the user to the easiest way to obtain ether, * or the network "gas" currency, for the current selected network. * - * @param {String} address - The address to fund. - * @param {String} amount - The amount of ether desired, as a base 10 string. + * @param {string} address - The address to fund. + * @param {string} amount - The amount of ether desired, as a base 10 string. */ buyEth (address, amount) { if (!amount) amount = '5' @@ -1151,9 +1159,9 @@ module.exports = class MetamaskController extends EventEmitter { if (url) this.platform.openWindow({ url }) } - /* + /** * A method for triggering a shapeshift currency transfer. - * @param {String} depositAddress - The address to deposit to. + * @param {string} depositAddress - The address to deposit to. * @property {string} depositType - An abbreviation of the type of crypto currency to be deposited. */ createShapeShiftTx (depositAddress, depositType) { @@ -1162,9 +1170,9 @@ module.exports = class MetamaskController extends EventEmitter { // network - /* + /** * A method for selecting a custom URL for an ethereum RPC provider. - * @param {String} rpcTarget - A URL for a valid Ethereum RPC API. + * @param {string} rpcTarget - A URL for a valid Ethereum RPC API. * @returns {Promise} - The RPC Target URL confirmed. */ async setCustomRpc (rpcTarget) { @@ -1173,9 +1181,9 @@ module.exports = class MetamaskController extends EventEmitter { return rpcTarget } - /* + /** * Sets whether or not to use the blockie identicon format. - * @param {Boolean} val - True for bockie, false for jazzicon. + * @param {boolean} val - True for bockie, false for jazzicon. * @param {Function} cb - A callback function called when complete. */ setUseBlockie (val, cb) { @@ -1187,9 +1195,9 @@ module.exports = class MetamaskController extends EventEmitter { } } - /* + /** * A method for setting a user's current locale, affecting the language rendered. - * @param {String} key - Locale identifier. + * @param {string} key - Locale identifier. * @param {Function} cb - A callback function called when complete. */ setCurrentLocale (key, cb) { @@ -1201,7 +1209,7 @@ module.exports = class MetamaskController extends EventEmitter { } } - /* + /** * A method for initializing storage the first time. * @param {Object} initState - The default state to initialize with. * @private @@ -1215,20 +1223,20 @@ module.exports = class MetamaskController extends EventEmitter { } } - /* + /** * A method for recording whether the MetaMask user interface is open or not. * @private - * @param {Boolean} open + * @param {boolean} open */ set isClientOpen (open) { this._isClientOpen = open this.isClientOpenAndUnlocked = this.getState().isUnlocked && open } - /* + /** * A method for activating the retrieval of price data, which should only be fetched when the UI is visible. * @private - * @param {Boolean} active - True if price data should be getting fetched. + * @param {boolean} active - True if price data should be getting fetched. */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active From ff689748a74da3531f5c228c13395d9c44cbe6aa Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Apr 2018 10:39:31 -0700 Subject: [PATCH 19/79] Document MetaMaskState and TransactionMeta --- app/scripts/background.js | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/app/scripts/background.js b/app/scripts/background.js index 244173dcf..6e008f501 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -65,9 +65,78 @@ initialize().catch(log.error) // setup metamask mesh testing container setupMetamaskMeshMetrics() +/** + * An object representing a transaction, in whatever state it is in. + * @typedef TransactionMeta + * + * @property {number} id - An internally unique tx identifier. + * @property {number} time - Time the tx was first suggested, in unix epoch time (ms). + * @property {string} status - The current transaction status (unapproved, signed, submitted, dropped, failed, rejected), as defined in `tx-state-manager.js`. + * @property {string} metamaskNetworkId - The transaction's network ID, used for EIP-155 compliance. + * @property {boolean} loadingDefaults - TODO: Document + * @property {Object} txParams - The tx params as passed to the network provider. + * @property {Object[]} history - A history of mutations to this TransactionMeta object. + * @property {boolean} gasPriceSpecified - True if the suggesting dapp specified a gas price, prevents auto-estimation. + * @property {boolean} gasLimitSpecified - True if the suggesting dapp specified a gas limit, prevents auto-estimation. + * @property {string} estimatedGas - A hex string represented the estimated gas limit required to complete the transaction. + * @property {string} origin - A string representing the interface that suggested the transaction. + * @property {Object} nonceDetails - A metadata object containing information used to derive the suggested nonce, useful for debugging nonce issues. + * @property {string} rawTx - A hex string of the final signed transaction, ready to submit to the network. + * @property {string} hash - A hex string of the transaction hash, used to identify the transaction on the network. + * @property {number} submittedTime - The time the transaction was submitted to the network, in Unix epoch time (ms). + */ + /* * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. * @typedef MetaMaskState + * @property {boolean} isInitialized - Whether the first vault has been created. + * @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection. + * @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed. + * @property {boolean} isMascara - True if the current context is the extensionless MetaMascara project. + * @property {boolean} isPopup - Returns true if the current view is an externally-triggered notification. + * @property {string} rpcTarget - DEPRECATED - The URL of the current RPC provider. + * @property {Object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys. + * @property {Object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions. + * @property {boolean} noActiveNotices - False if there are notices the user should confirm before using the application. + * @property {Array} frequentRpcList - A list of frequently used RPCs, including custom user-provided ones. + * @property {Array} addressBook - A list of previously sent to addresses. + * @property {address} selectedTokenAddress - Used to indicate if a token is globally selected. Should be deprecated in favor of UI-centric token selection. + * @property {Object} tokenExchangeRates - Info about current token prices. + * @property {Array} tokens - Tokens held by the current user, including their balances. + * @property {Object} send - TODO: Document + * @property {Object} coinOptions - TODO: Document + * @property {boolean} useBlockie - Indicates preferred user identicon format. True for blockie, false for Jazzicon. + * @property {Object} featureFlags - An object for optional feature flags. + * @property {string} networkEndpointType - TODO: Document + * @property {boolean} isRevealingSeedWords - True if seed words are currently being recovered, and should be shown to user. + * @property {boolean} welcomeScreen - True if welcome screen should be shown. + * @property {string} currentLocale - A locale string matching the user's preferred display language. + * @property {Object} provider - The current selected network provider. + * @property {string} provider.rpcTarget - The address for the RPC API, if using an RPC API. + * @property {string} provider.type - An identifier for the type of network selected, allows MetaMask to use custom provider strategies for known networks. + * @property {string} network - A stringified number of the current network ID. + * @property {Object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values. + * @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string. + * @property {TransactionMeta[]} selectedAddressTxList - An array of transactions associated with the currently selected account. + * @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs. + * * @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedPersonalMsgCount - The number of messages in unapprovedPersonalMsgs. + * @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedTypedMsgCount - The number of messages in unapprovedTypedMsgs. + * @property {string[]} keyringTypes - An array of unique keyring identifying strings, representing available strategies for creating accounts. + * @property {Keyring[]} keyrings - An array of keyring descriptions, summarizing the accounts that are available for use, and what keyrings they belong to. + * @property {Object} computedBalances - Maps accounts to their balances, accounting for balance changes from pending transactions. + * @property {string} currentAccountTab - A view identifying string for displaying the current displayed view, allows user to have a preferred tab in the old UI (between tokens and history). + * @property {string} selectedAddress - A lower case hex string of the currently selected address. + * @property {string} currentCurrency - A string identifying the user's preferred display currency, for use in showing conversion rates. + * @property {number} conversionRate - A number representing the current exchange rate from the user's preferred currency to Ether. + * @property {number} conversionDate - A unix epoch date (ms) for the time the current conversion rate was last retrieved. + * @property {Object} infuraNetworkStatus - An object of infura network status checks. + * @property {Block[]} recentBlocks - An array of recent blocks, used to calculate an effective but cheap gas price. + * @property {Array} shapeShiftTxList - An array of objects describing shapeshift exchange attempts. + * @property {Array} lostAccounts - TODO: Remove this feature. A leftover from the version-3 migration where our seed-phrase library changed to fix a bug where some accounts were mis-generated, but we recovered the old accounts as "lost" instead of losing them. + * @property {boolean} forgottenPassword - Returns true if the user has initiated the password recovery screen, is recovering from seed phrase. */ /* From 17afe922d4b23aa2005e1ffe8cea3d67b9843ff7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Apr 2018 10:40:53 -0700 Subject: [PATCH 20/79] Lint background docs --- app/scripts/background.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 6e008f501..63b062076 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,4 +1,4 @@ -/* +/** * @file The entry point for the web extension singleton process. */ @@ -86,7 +86,7 @@ setupMetamaskMeshMetrics() * @property {number} submittedTime - The time the transaction was submitted to the network, in Unix epoch time (ms). */ -/* +/** * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. * @typedef MetaMaskState * @property {boolean} isInitialized - Whether the first vault has been created. @@ -139,13 +139,13 @@ setupMetamaskMeshMetrics() * @property {boolean} forgottenPassword - Returns true if the user has initiated the password recovery screen, is recovering from seed phrase. */ -/* +/** * @typedef VersionedData * @property {MetaMaskState} data - The data emitted from MetaMask controller, or used to initialize it. * @property {Number} version - The latest migration version that has been run. */ -/* +/** * Initializes the MetaMask controller, and sets up all platform configuration. * @returns {Promise} Setup complete. */ @@ -160,7 +160,7 @@ async function initialize () { // State and Persistence // -/* +/** * Loads any stored data, prioritizing the latest storage strategy. * Migrates that data schema in case it was last loaded on an older version. * @returns {Promise} Last data emitted from previous instance of MetaMask. @@ -227,7 +227,7 @@ async function loadStateFromPersistence () { return versionedData.data } -/* +/** * Initializes the MetaMask Controller with any initial state and default language. * Configures platform-specific error reporting strategy. * Streams emitted state updates to platform-specific storage strategy. @@ -275,7 +275,7 @@ function setupController (initState, initLangCode) { } ) - /* + /** * Assigns the given state to the versioned object (with metadata), and returns that. * @param {Object} state - The state object as emitted by the MetaMaskController. * @returns {VersionedData} The state object wrapped in an object that includes a metadata key. @@ -316,14 +316,14 @@ function setupController (initState, initLangCode) { return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen } - /* + /** * A runtime.Port object, as provided by the browser: - * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port + * @link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port * @typedef Port * @type Object */ - /* + /** * Connects a Port to the MetaMask controller via a multiplexed duplex stream. * This method identifies trusted (MetaMask) interfaces, and connects them differently from untrusted (web pages). * @param {Port} remotePort - The port provided by a new context. @@ -381,7 +381,7 @@ function setupController (initState, initLangCode) { controller.messageManager.on('updateBadge', updateBadge) controller.personalMessageManager.on('updateBadge', updateBadge) - /* + /** * Updates the Web Extension's "badge" number, on the little fox in the toolbar. * The number reflects the current number of pending transactions or message signatures needing user approval. */ @@ -406,7 +406,7 @@ function setupController (initState, initLangCode) { // Etc... // -/* +/** * Opens the browser popup for user confirmation */ function triggerUi () { From 357582cc529556edce7ed8068157a3d7f7f3027c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Apr 2018 10:41:41 -0700 Subject: [PATCH 21/79] Add ui note to background docs --- app/scripts/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 63b062076..36e942d80 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -87,7 +87,7 @@ setupMetamaskMeshMetrics() */ /** - * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. + * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. Available in UI on React state as state.metamask. * @typedef MetaMaskState * @property {boolean} isInitialized - Whether the first vault has been created. * @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection. From 327ec03d6f5219bb42efabeb02536bb6f6781f76 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Apr 2018 10:45:06 -0700 Subject: [PATCH 22/79] Remove wrong chars --- app/scripts/background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 36e942d80..38b871bb5 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -120,9 +120,9 @@ setupMetamaskMeshMetrics() * @property {TransactionMeta[]} selectedAddressTxList - An array of transactions associated with the currently selected account. * @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. * @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs. - * * @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. * @property {number} unapprovedPersonalMsgCount - The number of messages in unapprovedPersonalMsgs. - * @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. * @property {number} unapprovedTypedMsgCount - The number of messages in unapprovedTypedMsgs. * @property {string[]} keyringTypes - An array of unique keyring identifying strings, representing available strategies for creating accounts. * @property {Keyring[]} keyrings - An array of keyring descriptions, summarizing the accounts that are available for use, and what keyrings they belong to. From 3ec22dc7e1676f5679bc33d8b5f314c890e28b8a Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 20 Apr 2018 15:48:44 -0230 Subject: [PATCH 23/79] Notification manager method docs. --- app/scripts/lib/notification-manager.js | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js index ce4825d5b..5dfb42078 100644 --- a/app/scripts/lib/notification-manager.js +++ b/app/scripts/lib/notification-manager.js @@ -12,6 +12,11 @@ class NotificationManager { * */ + /** + * Either brings an existing MetaMask notification window into focus, or creates a new notification window. New + * notification windows are given a 'popup' type. + * + */ showPopup () { this._getPopup((err, popup) => { if (err) throw err @@ -32,6 +37,10 @@ class NotificationManager { }) } + /** + * Closes a MetaMask notification if it window exists. + * + */ closePopup () { // closes notification popup this._getPopup((err, popup) => { @@ -41,10 +50,14 @@ class NotificationManager { }) } - // - // Private - // - + /** + * Checks all open MetaMask windows, and returns the first one it finds that is a notification window (i.e. has the + * type 'popup') + * + * @private + * @param {Function} cb A node style callback that to whcih the found notification window will be passed. + * + */ _getPopup (cb) { this._getWindows((err, windows) => { if (err) throw err @@ -52,6 +65,13 @@ class NotificationManager { }) } + /** + * Returns all open MetaMask windows. + * + * @private + * @param {Function} cb A node style callback that to which the windows will be passed. + * + */ _getWindows (cb) { // Ignore in test environment if (!extension.windows) { @@ -63,6 +83,13 @@ class NotificationManager { }) } + /** + * Given an array of windows, returns the first that has a 'popup' type, or null if no such window exists. + * + * @private + * @param {array} windows An array of objects containing data about the open MetaMask extension windows. + * + */ _getPopupIn (windows) { return windows ? windows.find((win) => { // Returns notification popup From 69920045e9d2392b01041532a3dfe7f33493a594 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 20 Apr 2018 15:48:58 -0230 Subject: [PATCH 24/79] Minor fixes in a number of docs. --- app/scripts/controllers/balance.js | 4 ++-- app/scripts/lib/account-tracker.js | 6 +++--- app/scripts/lib/extractEthjsErrorMessage.js | 2 +- app/scripts/lib/message-manager.js | 8 ++++---- app/scripts/lib/personal-message-manager.js | 7 +++---- app/scripts/lib/typed-message-manager.js | 8 ++++---- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 55811185a..86619fce1 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -11,7 +11,7 @@ class BalanceController { * @param {Object} opts Initialize various properties of the class. * @property {string} address A base 16 hex string. The account address which has the balance managed by this * BalanceController. - * @property {AccountTracker} accountTracker Contains and updates the users accounts; is the source of the account + * @property {AccountTracker} accountTracker Stores and updates the users accounts * for which this BalanceController manages balance. * @property {TransactionController} txController Stores, tracks and manages transactions. Here used to create a listener for * transaction updates. @@ -58,7 +58,7 @@ class BalanceController { /** * Sets up listeners and subscriptions which should trigger an update of ethBalance. These updates include: - * - when a transaction changes to a submitted, confirmed or failed state + * - when a transaction changes state to 'submitted', 'confirmed' or 'failed' * - when the current account changes (i.e. a new account is selected) * - when there is a block update * diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 1b707b2bb..0f7b3d865 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -57,7 +57,7 @@ class AccountTracker extends EventEmitter { * AccountTracker. * * Once this AccountTracker's accounts are up to date with those referenced by the passed addresses, each - * of these accounts are given an updated balance via Ethquery. + * of these accounts are given an updated balance via EthQuery. * * @param {array} address The array of hex addresses for accounts with which this AccountTracker's accounts should be * in sync @@ -119,7 +119,7 @@ class AccountTracker extends EventEmitter { * * @private * @param {object} block Data about the block that contains the data to update to. - * @fires 'block' The updated state, if all account updates are successfull + * @fires 'block' The updated state, if all account updates are successful * */ _updateForBlock (block) { @@ -139,7 +139,7 @@ class AccountTracker extends EventEmitter { /** * Calls this._updateAccount for each account in this.store * - * @param {Function} cb A callback to pass to this._updateAccount, called after each account is succesfully updated + * @param {Function} cb A callback to pass to this._updateAccount, called after each account is successfully updated * */ _updateAccounts (cb = noop) { diff --git a/app/scripts/lib/extractEthjsErrorMessage.js b/app/scripts/lib/extractEthjsErrorMessage.js index 3f0fb0232..0f100756f 100644 --- a/app/scripts/lib/extractEthjsErrorMessage.js +++ b/app/scripts/lib/extractEthjsErrorMessage.js @@ -5,7 +5,7 @@ module.exports = extractEthjsErrorMessage /** - * Extracts the important part of an ethjs-rpc error message. If the passed error is not anis isEthjsRpcError, the error + * Extracts the important part of an ethjs-rpc error message. If the passed error is not an isEthjsRpcError, the error * is returned unchanged. * * @param {string} errorMessage The error message to parse diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 52b897720..0756dbaed 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -20,7 +20,7 @@ var msgData = { * @typedef {Object} Message * @property {number} id An id to track and identify the message object * @property {object} msgParams The parameters to pass to the eth_sign method once the signature request is approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' @@ -36,7 +36,7 @@ module.exports = class MessageManager extends EventEmitter { * * @typedef {Object} MessageManager * @param {object} opts @deprecated - * @property {object} memStore The observable store where Messages are saved with persistance. + * @property {object} memStore The observable store where Messages are saved. * @property {object} memStore.unapprovedMsgs A collection of all Messages in the 'unapproved' state * @property {number} memStore.unapprovedMsgCount The count of all Messages in this.memStore.unapprobedMsgs * @property {array} messages Holds all messages that have been created by this MessageManager @@ -126,8 +126,8 @@ module.exports = class MessageManager extends EventEmitter { * Approves a Message. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise with * any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 4f19876db..825c69f46 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -3,7 +3,6 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const createId = require('./random-id') const hexRe = /^[0-9A-Fa-f]+$/g -const log = require('loglevel') /** * Represents, and contains data about, an 'personal_sign' type signature request. These are created when a @@ -15,7 +14,7 @@ const log = require('loglevel') * @property {number} id An id to track and identify the message object * @property {object} msgParams The parameters to pass to the personal_sign method once the signature request is * approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' @@ -124,8 +123,8 @@ module.exports = class PersonalMessageManager extends EventEmitter { * Approves a PersonalMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise * with any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 367c6ecb9..10fa5c2f5 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -15,7 +15,7 @@ const log = require('loglevel') * @property {number} id An id to track and identify the message object * @property {object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is * approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @property {object} msgParams.from The address that is making the signature request. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created @@ -31,7 +31,7 @@ module.exports = class TypedMessageManager extends EventEmitter { * * @typedef {Object} TypedMessage * @param {object} opts @deprecated - * @property {object} memStore The observable store where TypedMessage are saved with persistance. + * @property {object} memStore The observable store where TypedMessage are saved. * @property {object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs * @property {array} messages Holds all messages that have been created by this TypedMessage @@ -143,8 +143,8 @@ module.exports = class TypedMessageManager extends EventEmitter { * Approves a TypedMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise * with any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask. + * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ From 477b74124d24c9497fafb0c976eba27712c69d79 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Sat, 21 Apr 2018 22:23:45 -0700 Subject: [PATCH 25/79] Revert "Fix UI getting stuck in Reveal Seed screen" This reverts commit 2c8156ebe91941309d49e8f8f1ed8e9d740bb9de. --- app/scripts/lib/config-manager.js | 22 +----- app/scripts/metamask-controller.js | 2 - .../src/app/first-time/confirm-seed-screen.js | 14 +--- mascara/src/app/first-time/seed-screen.js | 71 +++++-------------- ui/app/actions.js | 16 +---- ui/app/app.js | 14 +--- 6 files changed, 23 insertions(+), 116 deletions(-) diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 63d27c40e..34b603b96 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -102,6 +102,7 @@ ConfigManager.prototype.setShowSeedWords = function (should) { this.setData(data) } + ConfigManager.prototype.getShouldShowSeedWords = function () { var data = this.getData() return data.showSeedWords @@ -117,27 +118,6 @@ ConfigManager.prototype.getSeedWords = function () { var data = this.getData() return data.seedWords } - -/** - * Called to set the isRevealingSeedWords flag. This happens only when the user chooses to reveal - * the seed words and not during the first time flow. - * @param {boolean} reveal - Value to set the isRevealingSeedWords flag. - */ -ConfigManager.prototype.setIsRevealingSeedWords = function (reveal = false) { - const data = this.getData() - data.isRevealingSeedWords = reveal - this.setData(data) -} - -/** - * Returns the isRevealingSeedWords flag. - * @returns {boolean|undefined} - */ -ConfigManager.prototype.getIsRevealingSeedWords = function () { - const data = this.getData() - return data.isRevealingSeedWords -} - ConfigManager.prototype.setRpcTarget = function (rpcUrl) { var config = this.getConfig() config.provider = { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 782bc50ac..a12b6776e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -308,7 +308,6 @@ module.exports = class MetamaskController extends EventEmitter { lostAccounts: this.configManager.getLostAccounts(), seedWords: this.configManager.getSeedWords(), forgottenPassword: this.configManager.getPasswordForgotten(), - isRevealingSeedWords: Boolean(this.configManager.getIsRevealingSeedWords()), }, } } @@ -348,7 +347,6 @@ module.exports = class MetamaskController extends EventEmitter { clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), importAccountWithStrategy: this.importAccountWithStrategy.bind(this), - setIsRevealingSeedWords: this.configManager.setIsRevealingSeedWords.bind(this.configManager), // vault management submitPassword: nodeify(keyringController.submitPassword, keyringController), diff --git a/mascara/src/app/first-time/confirm-seed-screen.js b/mascara/src/app/first-time/confirm-seed-screen.js index 7c0431431..438f383b1 100644 --- a/mascara/src/app/first-time/confirm-seed-screen.js +++ b/mascara/src/app/first-time/confirm-seed-screen.js @@ -9,7 +9,7 @@ import Identicon from '../../../../ui/app/components/identicon' import { confirmSeedWords, showModal } from '../../../../ui/app/actions' import Breadcrumbs from './breadcrumbs' import LoadingScreen from './loading-screen' -import { DEFAULT_ROUTE, INITIALIZE_BACKUP_PHRASE_ROUTE } from '../../../../ui/app/routes' +import { DEFAULT_ROUTE } from '../../../../ui/app/routes' class ConfirmSeedScreen extends Component { static propTypes = { @@ -53,7 +53,7 @@ class ConfirmSeedScreen extends Component { } render () { - const { seedWords, history } = this.props + const { seedWords } = this.props const { selectedSeeds, shuffledSeeds } = this.state const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') @@ -66,16 +66,6 @@ class ConfirmSeedScreen extends Component {
- { - e.preventDefault() - history.push(INITIALIZE_BACKUP_PHRASE_ROUTE) - }} - href="#" - > - {`< Back`} -
diff --git a/mascara/src/app/first-time/seed-screen.js b/mascara/src/app/first-time/seed-screen.js index 9af9ca3be..d004be77b 100644 --- a/mascara/src/app/first-time/seed-screen.js +++ b/mascara/src/app/first-time/seed-screen.js @@ -8,7 +8,6 @@ import Identicon from '../../../../ui/app/components/identicon' import Breadcrumbs from './breadcrumbs' import LoadingScreen from './loading-screen' import { DEFAULT_ROUTE, INITIALIZE_CONFIRM_SEED_ROUTE } from '../../../../ui/app/routes' -import { confirmSeedWords } from '../../../../ui/app/actions' const LockIcon = props => ( clearSeedWords().then(() => history.push(DEFAULT_ROUTE))} - disabled={!isShowingSecret} - > - Done - - : - } - renderSecretScreen () { - const { isRevealingSeedWords } = this.props + const { isShowingSecret } = this.state + const { history } = this.props return (
@@ -152,8 +121,14 @@ class BackupPhraseScreen extends Component {
- { this.renderSubmitButton() } - { !isRevealingSeedWords && } + +
) @@ -175,25 +150,13 @@ class BackupPhraseScreen extends Component { } } -const mapStateToProps = ({ metamask, appState }) => { - const { selectedAddress, seedWords, isRevealingSeedWords } = metamask - const { isLoading } = appState - - return { - seedWords, - isRevealingSeedWords, - isLoading, - address: selectedAddress, - } -} - -const mapDispatchToProps = dispatch => { - return { - clearSeedWords: () => dispatch(confirmSeedWords()), - } -} - export default compose( withRouter, - connect(mapStateToProps, mapDispatchToProps), + connect( + ({ metamask: { selectedAddress, seedWords }, appState: { isLoading } }) => ({ + seedWords, + isLoading, + address: selectedAddress, + }) + ) )(BackupPhraseScreen) diff --git a/ui/app/actions.js b/ui/app/actions.js index 73335db97..f5cdd32bc 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -351,6 +351,7 @@ function confirmSeedWords () { log.debug(`background.clearSeedWordCache`) return new Promise((resolve, reject) => { background.clearSeedWordCache((err, account) => { + dispatch(actions.hideLoadingIndication()) if (err) { dispatch(actions.displayWarning(err.message)) return reject(err) @@ -361,9 +362,6 @@ function confirmSeedWords () { resolve(account) }) }) - .then(() => dispatch(setIsRevealingSeedWords(false))) - .then(() => dispatch(actions.hideLoadingIndication())) - .catch(() => dispatch(actions.hideLoadingIndication())) } } @@ -448,13 +446,11 @@ function requestRevealSeed (password) { } dispatch(actions.showNewVaultSeed(result)) + dispatch(actions.hideLoadingIndication()) resolve() }) }) }) - .then(() => dispatch(setIsRevealingSeedWords(true))) - .then(() => dispatch(actions.hideLoadingIndication())) - .catch(() => dispatch(actions.hideLoadingIndication())) } } @@ -1911,11 +1907,3 @@ function updateNetworkEndpointType (networkEndpointType) { value: networkEndpointType, } } - -function setIsRevealingSeedWords (reveal) { - return dispatch => { - log.debug(`background.setIsRevealingSeedWords`) - background.setIsRevealingSeedWords(reveal) - return forceUpdateMetamaskState(dispatch) - } -} diff --git a/ui/app/app.js b/ui/app/app.js index 0b38b1326..e462701fa 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -56,20 +56,11 @@ const { class App extends Component { componentWillMount () { - const { - currentCurrency, - setCurrentCurrencyToUSD, - isRevealingSeedWords, - clearSeedWords, - } = this.props + const { currentCurrency, setCurrentCurrencyToUSD } = this.props if (!currentCurrency) { setCurrentCurrencyToUSD() } - - if (isRevealingSeedWords) { - clearSeedWords() - } } renderRoutes () { @@ -402,8 +393,6 @@ App.propTypes = { isMouseUser: PropTypes.bool, setMouseUserState: PropTypes.func, t: PropTypes.func, - isRevealingSeedWords: PropTypes.bool, - clearSeedWords: PropTypes.func, } function mapStateToProps (state) { @@ -484,7 +473,6 @@ function mapDispatchToProps (dispatch, ownProps) { setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')), toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()), setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)), - clearSeedWords: () => dispatch(actions.confirmSeedWords()), } } From 3082d2e4ef02306d5c09fbd1032a22d2efa33324 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Sun, 22 Apr 2018 00:14:40 -0700 Subject: [PATCH 26/79] Use new design for reveal seed screen. Persist seed words only in first time flow --- app/_locales/en/messages.json | 33 ++- app/images/copy-to-clipboard.svg | 24 ++ app/images/download.svg | 41 ++- app/images/warning.svg | 22 ++ gulpfile.js | 2 +- .../src/app/first-time/confirm-seed-screen.js | 14 +- ui/app/actions.js | 44 ++- ui/app/app.js | 2 +- .../export-text-container.component.js | 45 +++ .../export-text-container.scss | 52 ++++ .../components/export-text-container/index.js | 2 + .../components/pages/keychains/reveal-seed.js | 263 ++++++++---------- ui/app/css/itcss/components/index.scss | 2 + ui/app/css/itcss/components/pages/index.scss | 2 + .../itcss/components/pages/reveal-seed.scss | 17 ++ ui/app/css/itcss/generic/index.scss | 67 +++++ ui/app/css/itcss/settings/variables.scss | 1 + .../keychains/hd/recover-seed/confirmation.js | 138 --------- 18 files changed, 463 insertions(+), 308 deletions(-) create mode 100644 app/images/copy-to-clipboard.svg create mode 100644 app/images/warning.svg create mode 100644 ui/app/components/export-text-container/export-text-container.component.js create mode 100644 ui/app/components/export-text-container/export-text-container.scss create mode 100644 ui/app/components/export-text-container/index.js create mode 100644 ui/app/css/itcss/components/pages/reveal-seed.scss delete mode 100644 ui/app/keychains/hd/recover-seed/confirmation.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3b20ab49a..a40c2635c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -98,6 +98,9 @@ "clickCopy": { "message": "Click to Copy" }, + "close": { + "message": "Close" + }, "confirm": { "message": "Confirm" }, @@ -259,6 +262,9 @@ "enterPasswordConfirm": { "message": "Enter your password to confirm" }, + "enterPasswordContinue": { + "message": "Enter password to continue" + }, "passwordNotLongEnough": { "message": "Password not long enough" }, @@ -331,6 +337,9 @@ "gasPriceRequired": { "message": "Gas Price Required" }, + "generatingTransaction": { + "message": "Generating transaction" + }, "getEther": { "message": "Get Ether" }, @@ -476,6 +485,9 @@ "metamaskDescription": { "message": "MetaMask is a secure identity vault for Ethereum." }, + "metamaskSeedWords": { + "message": "MetaMask Seed Words" + }, "min": { "message": "Minimum" }, @@ -549,6 +561,9 @@ "message": "or", "description": "choice between creating or importing a new account" }, + "password": { + "message": "Password" + }, "passwordCorrect": { "message": "Please make sure your password is correct." }, @@ -634,8 +649,17 @@ "revealSeedWords": { "message": "Reveal Seed Words" }, + "revealSeedWordsTitle": { + "message": "Seed Phrase" + }, + "revealSeedWordsDescription": { + "message": "If you ever change browsers or move computers, you will need this seed phrase to access your accounts. Save them somewhere safe and secret." + }, + "revealSeedWordsWarningTitle": { + "message": "DO NOT share this phrase with anyone!" + }, "revealSeedWordsWarning": { - "message": "Do not recover your seed words in a public place! These words can be used to steal all your accounts." + "message": "These words can be used to steal all your accounts." }, "revert": { "message": "Revert" @@ -677,6 +701,9 @@ "reprice_subtitle": { "message": "Increase your gas price to attempt to overwrite and speed up your transaction" }, + "saveAsCsvFile": { + "message": "Save as CSV File" + }, "saveAsFile": { "message": "Save as File", "description": "Account export process" @@ -909,7 +936,7 @@ "youSign": { "message": "You are signing" }, - "generatingTransaction": { - "message": "Generating transaction" + "yourPrivateSeedPhrase": { + "message": "Your private seed phrase" } } diff --git a/app/images/copy-to-clipboard.svg b/app/images/copy-to-clipboard.svg new file mode 100644 index 000000000..c67c2aa84 --- /dev/null +++ b/app/images/copy-to-clipboard.svg @@ -0,0 +1,24 @@ + + + + 374E58A5-C29E-4921-83E7-889FA06D6408 + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/images/download.svg b/app/images/download.svg index 137a1190e..b55066414 100644 --- a/app/images/download.svg +++ b/app/images/download.svg @@ -1,15 +1,26 @@ - - - - - - - - - + + + + 50559280-0739-419A-8E87-3CDD16A6996A + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/images/warning.svg b/app/images/warning.svg new file mode 100644 index 000000000..9c8d697d7 --- /dev/null +++ b/app/images/warning.svg @@ -0,0 +1,22 @@ + + + + Group 7 + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 4f0da9d60..2ccc27f3f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -247,7 +247,7 @@ gulp.task('dev:scss', createScssBuildTask({ src: 'ui/app/css/index.scss', dest: 'ui/app/css/output', devMode: true, - pattern: 'ui/app/css/**/*.scss', + pattern: 'ui/app/**/*.scss', })) function createScssBuildTask({ src, dest, devMode, pattern }) { diff --git a/mascara/src/app/first-time/confirm-seed-screen.js b/mascara/src/app/first-time/confirm-seed-screen.js index 438f383b1..7c0431431 100644 --- a/mascara/src/app/first-time/confirm-seed-screen.js +++ b/mascara/src/app/first-time/confirm-seed-screen.js @@ -9,7 +9,7 @@ import Identicon from '../../../../ui/app/components/identicon' import { confirmSeedWords, showModal } from '../../../../ui/app/actions' import Breadcrumbs from './breadcrumbs' import LoadingScreen from './loading-screen' -import { DEFAULT_ROUTE } from '../../../../ui/app/routes' +import { DEFAULT_ROUTE, INITIALIZE_BACKUP_PHRASE_ROUTE } from '../../../../ui/app/routes' class ConfirmSeedScreen extends Component { static propTypes = { @@ -53,7 +53,7 @@ class ConfirmSeedScreen extends Component { } render () { - const { seedWords } = this.props + const { seedWords, history } = this.props const { selectedSeeds, shuffledSeeds } = this.state const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') @@ -66,6 +66,16 @@ class ConfirmSeedScreen extends Component {
+ { + e.preventDefault() + history.push(INITIALIZE_BACKUP_PHRASE_ROUTE) + }} + href="#" + > + {`< Back`} +
diff --git a/ui/app/actions.js b/ui/app/actions.js index f5cdd32bc..62875c629 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -83,7 +83,7 @@ var actions = { REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION', revealSeedConfirmation: revealSeedConfirmation, requestRevealSeed: requestRevealSeed, - + requestRevealSeedWords, // unlock screen UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', UNLOCK_FAILED: 'UNLOCK_FAILED', @@ -427,6 +427,30 @@ function revealSeedConfirmation () { } } +function verifyPassword (password) { + return new Promise((resolve, reject) => { + background.submitPassword(password, error => { + if (error) { + return reject(error) + } + + resolve(true) + }) + }) +} + +function verifySeedPhrase () { + return new Promise((resolve, reject) => { + background.verifySeedPhrase((error, seedWords) => { + if (error) { + return reject(error) + } + + resolve(seedWords) + }) + }) +} + function requestRevealSeed (password) { return dispatch => { dispatch(actions.showLoadingIndication()) @@ -454,6 +478,24 @@ function requestRevealSeed (password) { } } +function requestRevealSeedWords (password) { + return async dispatch => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.submitPassword`) + + try { + await verifyPassword(password) + const seedWords = await verifySeedPhrase() + dispatch(actions.hideLoadingIndication()) + return seedWords + } catch (error) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(error.message)) + throw new Error(error.message) + } + } +} + function resetAccount () { return (dispatch) => { background.resetAccount((err, account) => { diff --git a/ui/app/app.js b/ui/app/app.js index e462701fa..5af63dc9c 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -24,7 +24,7 @@ const Initialized = require('./components/pages/initialized') const Settings = require('./components/pages/settings') const UnlockPage = require('./components/pages/unlock') const RestoreVaultPage = require('./components/pages/keychains/restore-vault') -const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation') +const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed') const AddTokenPage = require('./components/pages/add-token') const CreateAccountPage = require('./components/pages/create-account') const NoticeScreen = require('./components/pages/notice') diff --git a/ui/app/components/export-text-container/export-text-container.component.js b/ui/app/components/export-text-container/export-text-container.component.js new file mode 100644 index 000000000..c2546fa9b --- /dev/null +++ b/ui/app/components/export-text-container/export-text-container.component.js @@ -0,0 +1,45 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const copyToClipboard = require('copy-to-clipboard') +const { exportAsFile } = require('../../util') + +class ExportTextContainer extends Component { + render () { + const { text = '', filename = '' } = this.props + const { t } = this.context + + return ( + h('.export-text-container', [ + h('.export-text-container__text-container', [ + h('.export-text-container__text', text), + ]), + h('.export-text-container__buttons-container', [ + h('.export-text-container__button.export-text-container__button--copy', { + onClick: () => copyToClipboard(text), + }, [ + h('img', { src: 'images/copy-to-clipboard.svg' }), + h('.export-text-container__button-text', t('copyToClipboard')), + ]), + h('.export-text-container__button', { + onClick: () => exportAsFile(filename, text), + }, [ + h('img', { src: 'images/download.svg' }), + h('.export-text-container__button-text', t('saveAsCsvFile')), + ]), + ]), + ]) + ) + } +} + +ExportTextContainer.propTypes = { + text: PropTypes.string, + filename: PropTypes.string, +} + +ExportTextContainer.contextTypes = { + t: PropTypes.func, +} + +module.exports = ExportTextContainer diff --git a/ui/app/components/export-text-container/export-text-container.scss b/ui/app/components/export-text-container/export-text-container.scss new file mode 100644 index 000000000..a42de8233 --- /dev/null +++ b/ui/app/components/export-text-container/export-text-container.scss @@ -0,0 +1,52 @@ +.export-text-container { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + border: 1px solid $alto; + border-radius: 4px; + font-weight: 400; + + &__text-container { + width: 100%; + display: flex; + justify-content: center; + padding: 20px; + border-radius: 4px; + background: $alabaster; + } + + &__text { + resize: none; + border: none; + background: $alabaster; + font-size: 20px; + text-align: center; + } + + &__buttons-container { + display: flex; + flex-direction: row; + border-top: 1px solid $alto; + width: 100%; + } + + &__button { + padding: 10px; + flex: 1; + display: flex; + justify-content: center; + align-items: center; + font-size: 14px; + cursor: pointer; + color: $curious-blue; + + &--copy { + border-right: 1px solid $alto; + } + } + + &__button-text { + padding-left: 10px; + } +} diff --git a/ui/app/components/export-text-container/index.js b/ui/app/components/export-text-container/index.js new file mode 100644 index 000000000..b2864a717 --- /dev/null +++ b/ui/app/components/export-text-container/index.js @@ -0,0 +1,2 @@ +const ExportTextContainer = require('./export-text-container.component') +module.exports = ExportTextContainer diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js index 247f3c8e2..685c81074 100644 --- a/ui/app/components/pages/keychains/reveal-seed.js +++ b/ui/app/components/pages/keychains/reveal-seed.js @@ -2,11 +2,27 @@ const { Component } = require('react') const { connect } = require('react-redux') const PropTypes = require('prop-types') const h = require('react-hyperscript') -const { exportAsFile } = require('../../../util') -const { requestRevealSeed, confirmSeedWords } = require('../../../actions') +const classnames = require('classnames') + +const { requestRevealSeedWords } = require('../../../actions') const { DEFAULT_ROUTE } = require('../../../routes') +const ExportTextContainer = require('../../export-text-container') + +const PASSWORD_PROMPT_SCREEN = 'PASSWORD_PROMPT_SCREEN' +const REVEAL_SEED_SCREEN = 'REVEAL_SEED_SCREEN' class RevealSeedPage extends Component { + constructor (props) { + super(props) + + this.state = { + screen: PASSWORD_PROMPT_SCREEN, + password: '', + seedWords: null, + error: null, + } + } + componentDidMount () { const passwordBox = document.getElementById('password-box') if (passwordBox) { @@ -14,182 +30,135 @@ class RevealSeedPage extends Component { } } - checkConfirmation (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.revealSeedWords() - } + handleSubmit (event) { + event.preventDefault() + this.setState({ seedWords: null, error: null }) + this.props.requestRevealSeedWords(this.state.password) + .then(seedWords => this.setState({ seedWords, screen: REVEAL_SEED_SCREEN })) + .catch(error => this.setState({ error: error.message })) } - revealSeedWords () { - const password = document.getElementById('password-box').value - this.props.requestRevealSeed(password) - } - - renderSeed () { - const { seedWords, confirmSeedWords, history } = this.props - + renderWarning () { return ( - h('.initialize-screen.flex-column.flex-center.flex-grow', [ - - h('h3.flex-center.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - marginTop: 36, - marginBottom: 8, - width: '100%', - fontSize: '20px', - padding: 6, - }, - }, [ - 'Vault Created', - ]), - - h('div', { - style: { - fontSize: '1em', - marginTop: '10px', - textAlign: 'center', - }, - }, [ - h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'), - ]), - - h('textarea.twelve-word-phrase', { - readOnly: true, - value: seedWords, + h('.page-container__warning-container', [ + h('img.page-container__warning-icon', { + src: 'images/warning.svg', }), - - h('button.primary', { - onClick: () => confirmSeedWords().then(() => history.push(DEFAULT_ROUTE)), - style: { - margin: '24px', - fontSize: '0.9em', - marginBottom: '10px', - }, - }, 'I\'ve copied it somewhere safe'), - - h('button.primary', { - onClick: () => exportAsFile(`MetaMask Seed Words`, seedWords), - style: { - margin: '10px', - fontSize: '0.9em', - }, - }, 'Save Seed Words As File'), + h('.page-container__warning-message', [ + h('.page-container__warning-title', [this.context.t('revealSeedWordsWarningTitle')]), + h('div', [this.context.t('revealSeedWordsWarning')]), + ]), ]) ) } - renderConfirmation () { - const { history, warning, inProgress } = this.props + renderContent () { + return this.state.screen === PASSWORD_PROMPT_SCREEN + ? this.renderPasswordPromptContent() + : this.renderRevealSeedContent() + } + + renderPasswordPromptContent () { + const { t } = this.context return ( - h('.initialize-screen.flex-column.flex-center.flex-grow', { - style: { maxWidth: '420px' }, + h('form', { + onSubmit: event => this.handleSubmit(event), }, [ - - h('h3.flex-center.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - marginBottom: 24, - width: '100%', - fontSize: '20px', - padding: 6, - }, - }, [ - 'Reveal Seed Words', - ]), - - h('.div', { - style: { - display: 'flex', - flexDirection: 'column', - padding: '20px', - justifyContent: 'center', - }, - }, [ - - h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), - - // confirmation - h('input.large-input.letter-spacey', { + h('label.input-label', { + htmlFor: 'password-box', + }, t('enterPasswordContinue')), + h('.input-group', [ + h('input.form-control', { type: 'password', + placeholder: t('password'), id: 'password-box', - placeholder: 'Enter your password to confirm', - onKeyPress: this.checkConfirmation.bind(this), - style: { - width: 260, - marginTop: '12px', - }, + value: this.state.password, + onChange: event => this.setState({ password: event.target.value }), + className: classnames({ 'form-control--error': this.state.error }), }), - - h('.flex-row.flex-start', { - style: { - marginTop: 30, - width: '50%', - }, - }, [ - // cancel - h('button.primary', { - onClick: () => history.push(DEFAULT_ROUTE), - }, 'CANCEL'), - - // submit - h('button.primary', { - style: { marginLeft: '10px' }, - onClick: this.revealSeedWords.bind(this), - }, 'OK'), - - ]), - - warning && ( - h('span.error', { - style: { - margin: '20px', - }, - }, warning.split('-')) - ), - - inProgress && ( - h('span.in-progress-notification', 'Generating Seed...') - ), ]), + this.state.error && h('.reveal-seed__error', this.state.error), + ]) + ) + } + + renderRevealSeedContent () { + const { t } = this.context + + return ( + h('div', [ + h('label.reveal-seed__label', t('yourPrivateSeedPhrase')), + h(ExportTextContainer, { + text: this.state.seedWords, + filename: t('metamaskSeedWords'), + }), + ]) + ) + } + + renderFooter () { + return this.state.screen === PASSWORD_PROMPT_SCREEN + ? this.renderPasswordPromptFooter() + : this.renderRevealSeedFooter() + } + + renderPasswordPromptFooter () { + return ( + h('.page-container__footer', [ + h('button.btn-secondary--lg.page-container__footer-button', { + onClick: () => this.props.history.push(DEFAULT_ROUTE), + }, this.context.t('cancel')), + h('button.btn-primary--lg.page-container__footer-button', { + onClick: event => this.handleSubmit(event), + disabled: this.state.password === '', + }, this.context.t('next')), + ]) + ) + } + + renderRevealSeedFooter () { + return ( + h('.page-container__footer', [ + h('button.btn-secondary--lg.page-container__footer-button', { + onClick: () => this.props.history.push(DEFAULT_ROUTE), + }, this.context.t('close')), ]) ) } render () { - return this.props.seedWords - ? this.renderSeed() - : this.renderConfirmation() + return ( + h('.page-container', [ + h('.page-container__header', [ + h('.page-container__title', this.context.t('revealSeedWordsTitle')), + h('.page-container__subtitle', this.context.t('revealSeedWordsDescription')), + ]), + h('.page-container__content', [ + this.renderWarning(), + h('.reveal-seed__content', [ + this.renderContent(), + ]), + ]), + this.renderFooter(), + ]) + ) } } RevealSeedPage.propTypes = { - requestRevealSeed: PropTypes.func, - confirmSeedWords: PropTypes.func, - seedWords: PropTypes.string, - inProgress: PropTypes.bool, + requestRevealSeedWords: PropTypes.func, history: PropTypes.object, - warning: PropTypes.string, } -const mapStateToProps = state => { - const { appState: { warning }, metamask: { seedWords } } = state - - return { - warning, - seedWords, - } +RevealSeedPage.contextTypes = { + t: PropTypes.func, } const mapDispatchToProps = dispatch => { return { - requestRevealSeed: password => dispatch(requestRevealSeed(password)), - confirmSeedWords: () => dispatch(confirmSeedWords()), + requestRevealSeedWords: password => dispatch(requestRevealSeedWords(password)), } } -module.exports = connect(mapStateToProps, mapDispatchToProps)(RevealSeedPage) +module.exports = connect(null, mapDispatchToProps)(RevealSeedPage) diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 959eb9d15..1c544e162 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -61,3 +61,5 @@ @import './welcome-screen.scss'; @import './sender-to-recipient.scss'; + +@import '../../../components/export-text-container/export-text-container.scss'; diff --git a/ui/app/css/itcss/components/pages/index.scss b/ui/app/css/itcss/components/pages/index.scss index 82446fd7a..d0b59da53 100644 --- a/ui/app/css/itcss/components/pages/index.scss +++ b/ui/app/css/itcss/components/pages/index.scss @@ -1 +1,3 @@ @import './unlock.scss'; + +@import './reveal-seed.scss'; diff --git a/ui/app/css/itcss/components/pages/reveal-seed.scss b/ui/app/css/itcss/components/pages/reveal-seed.scss new file mode 100644 index 000000000..b8f13af4a --- /dev/null +++ b/ui/app/css/itcss/components/pages/reveal-seed.scss @@ -0,0 +1,17 @@ +.reveal-seed { + &__content { + padding: 20px; + } + + &__label { + padding-bottom: 10px; + font-weight: 400; + display: inline-block; + } + + &__error { + color: $crimson; + font-size: 14px; + padding-top: 5px; + } +} diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss index 92321394b..7a64810c4 100644 --- a/ui/app/css/itcss/generic/index.scss +++ b/ui/app/css/itcss/generic/index.scss @@ -207,6 +207,27 @@ input.large-input { &__content { height: 100%; overflow-y: auto; + min-height: 250px; + max-height: 400px; + } + + &__warning-container { + background: $linen; + padding: 20px; + display: flex; + align-items: start; + } + + &__warning-message { + padding-left: 15px; + } + + &__warning-title { + font-weight: 500; + } + + &__warning-icon { + padding-top: 5px; } } @@ -237,3 +258,49 @@ input.large-input { border-radius: 0; } } + +@media screen and (min-width: 576px) { + .page-container { + height: 600px; + flex: 0 0 auto; + } +} + +.input-label { + padding-bottom: 10px; + font-weight: 400; + display: inline-block; +} + +input.form-control { + padding-left: 10px; + font-size: 14px; + height: 40px; + border: 1px solid $alto; + border-radius: 3px; + width: 100%; + + &::-webkit-input-placeholder { + font-weight: 100; + color: $dusty-gray; + } + + &::-moz-placeholder { + font-weight: 100; + color: $dusty-gray; + } + + &:-ms-input-placeholder { + font-weight: 100; + color: $dusty-gray; + } + + &:-moz-placeholder { + font-weight: 100; + color: $dusty-gray; + } + + &--error { + border: 1px solid $monzo; + } +} diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss index 51548306f..814d7a382 100644 --- a/ui/app/css/itcss/settings/variables.scss +++ b/ui/app/css/itcss/settings/variables.scss @@ -54,6 +54,7 @@ $saffron: #f6c343; $dodger-blue: #3099f2; $zumthor: #edf7ff; $ecstasy: #f7861c; +$linen: #fdf4f4; /* Z-Indicies diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js deleted file mode 100644 index eb588415f..000000000 --- a/ui/app/keychains/hd/recover-seed/confirmation.js +++ /dev/null @@ -1,138 +0,0 @@ -const inherits = require('util').inherits -const Component = require('react').Component -const PropTypes = require('prop-types') -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const actions = require('../../../actions') -const { withRouter } = require('react-router-dom') -const { compose } = require('recompose') -const { - DEFAULT_ROUTE, - INITIALIZE_BACKUP_PHRASE_ROUTE, -} = require('../../../routes') - -RevealSeedConfirmation.contextTypes = { - t: PropTypes.func, -} - -module.exports = compose( - withRouter, - connect(mapStateToProps) -)(RevealSeedConfirmation) - - -inherits(RevealSeedConfirmation, Component) -function RevealSeedConfirmation () { - Component.call(this) -} - -function mapStateToProps (state) { - return { - warning: state.appState.warning, - } -} - -RevealSeedConfirmation.prototype.render = function () { - const props = this.props - - return ( - - h('.initialize-screen.flex-column.flex-center.flex-grow', { - style: { maxWidth: '420px' }, - }, [ - - h('h3.flex-center.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - marginBottom: 24, - width: '100%', - fontSize: '20px', - padding: 6, - }, - }, [ - 'Reveal Seed Words', - ]), - - h('.div', { - style: { - display: 'flex', - flexDirection: 'column', - padding: '20px', - justifyContent: 'center', - }, - }, [ - - h('h4', this.context.t('revealSeedWordsWarning')), - - // confirmation - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box', - placeholder: this.context.t('enterPasswordConfirm'), - onKeyPress: this.checkConfirmation.bind(this), - style: { - width: 260, - marginTop: '12px', - }, - }), - - h('.flex-row.flex-start', { - style: { - marginTop: 30, - width: '50%', - }, - }, [ - // cancel - h('button.primary', { - onClick: this.goHome.bind(this), - }, 'CANCEL'), - - // submit - h('button.primary', { - style: { marginLeft: '10px' }, - onClick: this.revealSeedWords.bind(this), - }, 'OK'), - - ]), - - (props.warning) && ( - h('span.error', { - style: { - margin: '20px', - }, - }, props.warning.split('-')) - ), - - props.inProgress && ( - h('span.in-progress-notification', this.context.t('generatingSeed')) - ), - ]), - ]) - ) -} - -RevealSeedConfirmation.prototype.componentDidMount = function () { - document.getElementById('password-box').focus() -} - -RevealSeedConfirmation.prototype.goHome = function () { - this.props.dispatch(actions.showConfigPage(false)) - this.props.dispatch(actions.confirmSeedWords()) - .then(() => this.props.history.push(DEFAULT_ROUTE)) -} - -// create vault - -RevealSeedConfirmation.prototype.checkConfirmation = function (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.revealSeedWords() - } -} - -RevealSeedConfirmation.prototype.revealSeedWords = function () { - var password = document.getElementById('password-box').value - this.props.dispatch(actions.requestRevealSeed(password)) - .then(() => this.props.history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)) -} From 0c26df965330a7ad63d4b41bdacb37b1d1b9a673 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 23 Apr 2018 14:11:02 -0230 Subject: [PATCH 27/79] Fix nits. --- app/scripts/controllers/preferences.js | 2 +- app/scripts/lib/message-manager.js | 20 ++++++++--------- app/scripts/lib/personal-message-manager.js | 20 ++++++++--------- app/scripts/lib/typed-message-manager.js | 24 ++++++++++----------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index bdedde2fb..1d3308d36 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -7,7 +7,7 @@ class PreferencesController { /** * * @typedef {Object} PreferencesController - * @param {object} opts Overrides the defaults for the initial state of this.store + * @param {object} opts Overrides the defaults for the initial state of this.store * @property {object} store The stored object containing a users preferences, stored in local storage * @property {array} store.frequentRpcList A list of custom rpcs to provide the user * @property {string} store.currentAccountTab Indicates the selected tab in the ui diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 0756dbaed..a1c6d6ac8 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -19,8 +19,8 @@ var msgData = { * * @typedef {Object} Message * @property {number} id An id to track and identify the message object - * @property {object} msgParams The parameters to pass to the eth_sign method once the signature request is approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @property {Object} msgParams The parameters to pass to the eth_sign method once the signature request is approved. + * @property {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' @@ -35,9 +35,9 @@ module.exports = class MessageManager extends EventEmitter { * Controller in charge of managing - storing, adding, removing, updating - Messages. * * @typedef {Object} MessageManager - * @param {object} opts @deprecated - * @property {object} memStore The observable store where Messages are saved. - * @property {object} memStore.unapprovedMsgs A collection of all Messages in the 'unapproved' state + * @param {Object} opts @deprecated + * @property {Object} memStore The observable store where Messages are saved. + * @property {Object} memStore.unapprovedMsgs A collection of all Messages in the 'unapproved' state * @property {number} memStore.unapprovedMsgCount The count of all Messages in this.memStore.unapprobedMsgs * @property {array} messages Holds all messages that have been created by this MessageManager * @@ -64,7 +64,7 @@ module.exports = class MessageManager extends EventEmitter { /** * A getter for the 'unapproved' Messages in this.messages * - * @returns {object} An index of Message ids to Messages, for all 'unapproved' Messages in this.messages + * @returns {Object} An index of Message ids to Messages, for all 'unapproved' Messages in this.messages * */ getUnapprovedMsgs () { @@ -76,7 +76,7 @@ module.exports = class MessageManager extends EventEmitter { * Creates a new Message with an 'unapproved' status using the passed msgParams. this.addMsg is called to add the * new Message to this.messages, and to save the unapproved Messages from that list to this.memStore. * - * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @param {Object} msgParams The params for the eth_sign call to be made after the message is approved. * @returns {number} The id of the newly created message. * */ @@ -126,8 +126,8 @@ module.exports = class MessageManager extends EventEmitter { * Approves a Message. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise with * any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ @@ -164,7 +164,7 @@ module.exports = class MessageManager extends EventEmitter { /** * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams * - * @param {object} msgParams The msgParams to modify + * @param {Object} msgParams The msgParams to modify * @returns {Promise} Promises the msgParams with the metamaskId property removed * */ diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 825c69f46..896a65df8 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -12,9 +12,9 @@ const hexRe = /^[0-9A-Fa-f]+$/g * * @typedef {Object} PersonalMessage * @property {number} id An id to track and identify the message object - * @property {object} msgParams The parameters to pass to the personal_sign method once the signature request is + * @property {Object} msgParams The parameters to pass to the personal_sign method once the signature request is * approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @property {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' @@ -28,9 +28,9 @@ module.exports = class PersonalMessageManager extends EventEmitter { * Controller in charge of managing - storing, adding, removing, updating - PersonalMessage. * * @typedef {Object} PersonalMessageManager - * @param {object} opts @deprecated - * @property {object} memStore The observable store where PersonalMessage are saved with persistance. - * @property {object} memStore.unapprovedPersonalMsgs A collection of all PersonalMessages in the 'unapproved' state + * @param {Object} opts @deprecated + * @property {Object} memStore The observable store where PersonalMessage are saved with persistance. + * @property {Object} memStore.unapprovedPersonalMsgs A collection of all PersonalMessages in the 'unapproved' state * @property {number} memStore.unapprovedPersonalMsgCount The count of all PersonalMessages in this.memStore.unapprobedMsgs * @property {array} messages Holds all messages that have been created by this PersonalMessageManager * @@ -57,7 +57,7 @@ module.exports = class PersonalMessageManager extends EventEmitter { /** * A getter for the 'unapproved' PersonalMessages in this.messages * - * @returns {object} An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in + * @returns {Object} An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in * this.messages * */ @@ -71,7 +71,7 @@ module.exports = class PersonalMessageManager extends EventEmitter { * the new PersonalMessage to this.messages, and to save the unapproved PersonalMessages from that list to * this.memStore. * - * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @param {Object} msgParams The params for the eth_sign call to be made after the message is approved. * @returns {number} The id of the newly created PersonalMessage. * */ @@ -123,8 +123,8 @@ module.exports = class PersonalMessageManager extends EventEmitter { * Approves a PersonalMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise * with any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ @@ -161,7 +161,7 @@ module.exports = class PersonalMessageManager extends EventEmitter { /** * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams * - * @param {object} msgParams The msgParams to modify + * @param {Object} msgParams The msgParams to modify * @returns {Promise} Promises the msgParams with the metamaskId property removed * */ diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 10fa5c2f5..e9be93afd 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -13,10 +13,10 @@ const log = require('loglevel') * * @typedef {Object} TypedMessage * @property {number} id An id to track and identify the message object - * @property {object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is + * @property {Object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is * approved. - * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. - * @property {object} msgParams.from The address that is making the signature request. + * @property {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @property {Object} msgParams.from The address that is making the signature request. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' @@ -30,9 +30,9 @@ module.exports = class TypedMessageManager extends EventEmitter { * Controller in charge of managing - storing, adding, removing, updating - TypedMessage. * * @typedef {Object} TypedMessage - * @param {object} opts @deprecated - * @property {object} memStore The observable store where TypedMessage are saved. - * @property {object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state + * @param {Object} opts @deprecated + * @property {Object} memStore The observable store where TypedMessage are saved. + * @property {Object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs * @property {array} messages Holds all messages that have been created by this TypedMessage * @@ -59,7 +59,7 @@ module.exports = class TypedMessageManager extends EventEmitter { /** * A getter for the 'unapproved' TypedMessages in this.messages * - * @returns {object} An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in + * @returns {Object} An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in * this.messages * */ @@ -73,7 +73,7 @@ module.exports = class TypedMessageManager extends EventEmitter { * the new TypedMessage to this.messages, and to save the unapproved TypedMessages from that list to * this.memStore. Before any of this is done, msgParams are validated * - * @param {object} msgParams The params for the eth_sign call to be made after the message is approved. + * @param {Object} msgParams The params for the eth_sign call to be made after the message is approved. * @returns {number} The id of the newly created TypedMessage. * */ @@ -101,7 +101,7 @@ module.exports = class TypedMessageManager extends EventEmitter { /** * Helper method for this.addUnapprovedMessage. Validates that the passed params have the required properties. * - * @param {object} params The params to validate + * @param {Object} params The params to validate * */ validateParams (params) { @@ -143,8 +143,8 @@ module.exports = class TypedMessageManager extends EventEmitter { * Approves a TypedMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise * with any the message params modified for proper signing. * - * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. - * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. + * @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask. + * @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask. * @returns {Promise} Promises the msgParams object with metamaskId removed. * */ @@ -181,7 +181,7 @@ module.exports = class TypedMessageManager extends EventEmitter { /** * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams * - * @param {object} msgParams The msgParams to modify + * @param {Object} msgParams The msgParams to modify * @returns {Promise} Promises the msgParams with the metamaskId property removed * */ From 621e9334bc7009f7ccd7c1536df00ff6f8240bd3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 23 Apr 2018 09:43:18 -0700 Subject: [PATCH 28/79] Cleaned up some typos and JSDocs in Transactions Nonce tracker is not fully documented yet. Have not yet touched: - tx-state-manager - tx-state-history-helper - util - tx-gas-utils - pending-tx-tracker --- .../controllers/transactions/README.md | 10 +++--- app/scripts/controllers/transactions/index.js | 33 ++++++++++--------- .../controllers/transactions/nonce-tracker.js | 33 ++++++++++--------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/app/scripts/controllers/transactions/README.md b/app/scripts/controllers/transactions/README.md index ea38b5ae6..b414762dc 100644 --- a/app/scripts/controllers/transactions/README.md +++ b/app/scripts/controllers/transactions/README.md @@ -1,7 +1,7 @@ # Transaction Controller Transaction Controller is an aggregate of sub-controllers and trackers -composing them in a way to be exposed to the metamask controller +exposed to the MetaMask controller. - txStateManager responsible for the state of a transaction and @@ -14,11 +14,11 @@ composing them in a way to be exposed to the metamask controller - nonceTracker calculating nonces -## flow digram of processing a transaction +## Flow diagram of processing a transaction ![transaction-flow](../../../../docs/transaction-flow.png) -## txMeta's && txParams +## txMeta's & txParams A txMeta is the "meta" object it has all the random bits of info we need about a transaction on it. txParams are sacred every thing on txParams gets signed so it must be a valid key and be hex prefixed except for the network number. Extra stuff must go on the txMeta! @@ -59,8 +59,8 @@ txMeta = { "value": "0x3b9aca00" }, ...], // I've removed most of history for this - "gasPriceSpecified": false, //weather or not the user/dapp has specified gasPrice - "gasLimitSpecified": false, //weather or not the user/dapp has specified gas + "gasPriceSpecified": false, //whether or not the user/dapp has specified gasPrice + "gasLimitSpecified": false, //whether or not the user/dapp has specified gas "estimatedGas": "5208", "origin": "MetaMask", //debug "nonceDetails": { diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index fc6340bd6..321438598 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -25,17 +25,18 @@ const log = require('loglevel') calculating nonces -@param {object} opts - - @property {object} opts.initState initial transaction list default is an empty array + @class + @param {Object} opts + @property {Object} opts.initState initial transaction list default is an empty array @property {Object} opts.networkStore an observable store for network number - @property {Object} opts.blockTracker + @param {Object} opts.blockTracker - An instance of eth-blocktracker @property {Object} opts.provider + @param {Object} opts.provider - A network provider. @property {Object} opts.signTransaction function the signs an ethereumjs-tx @property {function} opts.getGasPrice optional gas price calculator @property {function} opts.signTransaction ethTx signer that returns a rawTx @property {number} opts.txHistoryLimit number *optional* for limiting how many transactions are in state @property {Object} opts.preferencesStore -@class */ class TransactionController extends EventEmitter { @@ -103,21 +104,21 @@ class TransactionController extends EventEmitter { this.emit(`${txMeta.id}:unapproved`, txMeta) } -/** + /** wipes the transactions for a given account - @param address {string} - hex string of the from address for txs being removed -*/ + @param {string} address - hex string of the from address for txs being removed + */ wipeTransactions (address) { this.txStateManager.wipeTransactions(address) } -/** -add a new unapproved transaction to the pipeline -@returns {promise} -@param txParams {object} - txParams for the transaction -@param opts {object} - with the key origin to put the origin on the txMeta + /** + add a new unapproved transaction to the pipeline -*/ + @returns {promise} + @param txParams {Object} - txParams for the transaction + @param opts {Object} - with the key origin to put the origin on the txMeta + */ async newUnapprovedTransaction (txParams, opts = {}) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) const initialTxMeta = await this.addUnapprovedTransaction(txParams) @@ -171,7 +172,7 @@ add a new unapproved transaction to the pipeline } /** adds the tx gas defaults: gas && gasPrice - @param txMeta {object} - the txMeta object + @param txMeta {Object} - the txMeta object @returns {promise} resolves with txMeta */ async addTxGasDefaults (txMeta) { @@ -211,7 +212,7 @@ add a new unapproved transaction to the pipeline /** updates the txMeta in the txStateManager - @param txMeta {object} - the updated txMeta + @param txMeta {Object} - the updated txMeta */ async updateTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction') @@ -219,7 +220,7 @@ add a new unapproved transaction to the pipeline /** updates and approves the transaction - @param txMeta {object} + @param txMeta {Object} */ async updateAndApproveTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js index e0f4d0fe3..e2c5dadef 100644 --- a/app/scripts/controllers/transactions/nonce-tracker.js +++ b/app/scripts/controllers/transactions/nonce-tracker.js @@ -2,12 +2,12 @@ const EthQuery = require('ethjs-query') const assert = require('assert') const Mutex = require('await-semaphore').Mutex /** - @param opts {object} - + @param opts {Object} - @property {Object} opts.provider a ethereum provider - @property {function} opts.getPendingTransactions a function that returns an array of txMeta - whos status is `submitted` - @property {function} opts.getConfirmedTransactions a function that returns an array of txMeta - whos status is `confirmed` + @property {Function} opts.getPendingTransactions a function that returns an array of txMeta + whosee status is `submitted` + @property {Function} opts.getConfirmedTransactions a function that returns an array of txMeta + whose status is `confirmed` @class */ class NonceTracker { @@ -21,7 +21,7 @@ class NonceTracker { } /** - @returns {object} with the key releaseLock (the gloabl mutex) + @returns {Promise} with the key releaseLock (the gloabl mutex) */ async getGlobalLock () { const globalMutex = this._lookupMutex('global') @@ -31,17 +31,18 @@ class NonceTracker { } /** - this will return an object with the `nextNonce` `nonceDetails` which is an - object with: - highestLocallyConfirmed (nonce), - highestSuggested (either the network nonce or the highestLocallyConfirmed nonce), - nextNetworkNonce (the nonce suggested by the network), - and the releaseLock -
note: releaseLock must be called after adding signed tx to pending transactions - (or discarding)
+ * @typedef NonceDetails + * @property {number} highestLocallyConfirmed - A hex string of the highest nonce on a confirmed transaction. + * @property {number} nextNetworkNonce - The next nonce suggested by the eth_getTransactionCount method. + * @property {number} highetSuggested - The maximum between the other two, the number returned. + */ - @param address {string} the hex string for the address whos nonce we are calculating - @returns {object} + /** + this will return an object with the `nextNonce` `nonceDetails` of type NonceDetails, and the releaseLock + Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding). + + @param address {string} the hex string for the address whose nonce we are calculating + @returns {Promise} */ async getNonceLock (address) { // await global mutex free From 9e41f58af2f236389f11bd1d401da73c0d709b2d Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Thu, 12 Apr 2018 14:17:36 -0700 Subject: [PATCH 29/79] Clean up network config --- app/scripts/config.js | 79 ------------------- app/scripts/controllers/network/enums.js | 56 +++++++++++++ app/scripts/controllers/network/index.js | 2 + .../controllers/{ => network}/network.js | 27 ++++--- app/scripts/controllers/network/util.js | 65 +++++++++++++++ app/scripts/first-time-state.js | 3 +- app/scripts/lib/config-manager.js | 23 +++--- old-ui/app/app.js | 2 +- old-ui/app/components/buy-button-subview.js | 4 +- test/unit/network-contoller-test.js | 42 +++++++++- ui/app/components/buy-button-subview.js | 4 +- ui/app/components/modals/buy-options-modal.js | 4 +- .../components/modals/deposit-ether-modal.js | 4 +- ui/app/components/pages/settings/settings.js | 2 +- ui/app/components/pages/unlock.js | 2 +- ui/app/first-time/init-menu.js | 2 +- ui/app/reducers/metamask.js | 2 +- ui/app/select-app.js | 2 +- ui/app/unlock.js | 2 +- ui/index.js | 6 +- 20 files changed, 212 insertions(+), 121 deletions(-) delete mode 100644 app/scripts/config.js create mode 100644 app/scripts/controllers/network/enums.js create mode 100644 app/scripts/controllers/network/index.js rename app/scripts/controllers/{ => network}/network.js (91%) create mode 100644 app/scripts/controllers/network/util.js diff --git a/app/scripts/config.js b/app/scripts/config.js deleted file mode 100644 index e6f70ca2b..000000000 --- a/app/scripts/config.js +++ /dev/null @@ -1,79 +0,0 @@ -const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask' -const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask' -const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask' -const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' -const LOCALHOST_RPC_URL = 'http://localhost:8545' - -const MAINET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2' -const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2' -const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2' -const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2' - -const DEFAULT_RPC = 'rinkeby' -const OLD_UI_NETWORK_TYPE = 'network' -const BETA_UI_NETWORK_TYPE = 'networkBeta' - -global.METAMASK_DEBUG = process.env.METAMASK_DEBUG - -/** - * @typedef {Object} UrlConfig - * @property {string} localhost URL of local RPC provider - * @property {string} mainnet URL of mainnet RPC provider - * @property {string} ropsten URL of Ropsten testnet RPC provider - * @property {string} kovan URL of Kovan testnet RPC provider - * @property {string} rinkeby URL of Rinkeby testnet RPC provider - */ - -/** - * @typedef {Object} NameConfig - * @property {string} 3 URL of local RPC provider - * @property {string} 4 URL of mainnet RPC provider - * @property {string} 42 URL of Ropsten testnet RPC provider - */ - -/** - * @typedef {Object} EnumConfig - * @property {string} DEFAULT_RPC Default network provider URL - * @property {string} OLD_UI_NETWORK_TYPE Network associated with old UI - * @property {string} BETA_UI_NETWORK_TYPE Network associated with new UI - */ - -/** - * @typedef {Object} Config - * @property {UrlConfig} network Network configuration parameters - * @property {UrlConfig} networkBeta Beta UI network configuration parameters - * @property {NameConfig} networkNames Network name configuration parameters - * @property {EnumConfig} enums Application-wide string constants - */ - -/** - * @type {Config} - **/ -const config = { - network: { - localhost: LOCALHOST_RPC_URL, - mainnet: MAINET_RPC_URL, - ropsten: ROPSTEN_RPC_URL, - kovan: KOVAN_RPC_URL, - rinkeby: RINKEBY_RPC_URL, - }, - networkBeta: { - localhost: LOCALHOST_RPC_URL, - mainnet: MAINET_RPC_URL_BETA, - ropsten: ROPSTEN_RPC_URL_BETA, - kovan: KOVAN_RPC_URL_BETA, - rinkeby: RINKEBY_RPC_URL_BETA, - }, - networkNames: { - 3: 'Ropsten', - 4: 'Rinkeby', - 42: 'Kovan', - }, - enums: { - DEFAULT_RPC, - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, - }, -} - -module.exports = config diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js new file mode 100644 index 000000000..4f29e301b --- /dev/null +++ b/app/scripts/controllers/network/enums.js @@ -0,0 +1,56 @@ +const ROPSTEN = 'ropsten' +const RINKEBY = 'rinkeby' +const KOVAN = 'kovan' +const MAINNET = 'mainnet' +const LOCALHOST = 'localhost' + +const ROPSTEN_CODE = 3 +const RINKEYBY_CODE = 4 +const KOVAN_CODE = 42 + +const ROPSTEN_DISPLAY_NAME = 'Ropsten' +const RINKEBY_DISPLAY_NAME = 'Rinkeby' +const KOVAN_DISPLAY_NAME = 'Kovan' +const MAINNET_DISPLAY_NAME = 'Main Ethereum Network' + +const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask' +const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask' +const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask' +const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' +const LOCALHOST_RPC_URL = 'http://localhost:8545' + +const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2' +const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2' +const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2' +const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2' + +const DEFAULT_NETWORK = 'rinkeby' +const OLD_UI_NETWORK_TYPE = 'network' +const BETA_UI_NETWORK_TYPE = 'networkBeta' + +module.exports = { + ROPSTEN, + RINKEBY, + KOVAN, + MAINNET, + LOCALHOST, + ROPSTEN_CODE, + RINKEYBY_CODE, + KOVAN_CODE, + ROPSTEN_DISPLAY_NAME, + RINKEBY_DISPLAY_NAME, + KOVAN_DISPLAY_NAME, + MAINNET_DISPLAY_NAME, + MAINNET_RPC_URL, + ROPSTEN_RPC_URL, + KOVAN_RPC_URL, + RINKEBY_RPC_URL, + LOCALHOST_RPC_URL, + MAINNET_RPC_URL_BETA, + ROPSTEN_RPC_URL_BETA, + KOVAN_RPC_URL_BETA, + RINKEBY_RPC_URL_BETA, + DEFAULT_NETWORK, + OLD_UI_NETWORK_TYPE, + BETA_UI_NETWORK_TYPE, +} diff --git a/app/scripts/controllers/network/index.js b/app/scripts/controllers/network/index.js new file mode 100644 index 000000000..fb095bf33 --- /dev/null +++ b/app/scripts/controllers/network/index.js @@ -0,0 +1,2 @@ +const NetworkController = require('./network') +module.exports = NetworkController diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network/network.js similarity index 91% rename from app/scripts/controllers/network.js rename to app/scripts/controllers/network/network.js index 45574e673..6fd983bb2 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network/network.js @@ -7,11 +7,18 @@ const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') const EthQuery = require('eth-query') -const createEventEmitterProxy = require('../lib/events-proxy.js') -const networkConfig = require('../config.js') +const createEventEmitterProxy = require('../../lib/events-proxy.js') const log = require('loglevel') -const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums -const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] +const { + ROPSTEN, + RINKEBY, + KOVAN, + MAINNET, + OLD_UI_NETWORK_TYPE, + DEFAULT_NETWORK, +} = require('./enums') +const { getNetworkEndpoints } = require('./util') +const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET] module.exports = class NetworkController extends EventEmitter { @@ -19,8 +26,8 @@ module.exports = class NetworkController extends EventEmitter { super() this._networkEndpointVersion = OLD_UI_NETWORK_TYPE - this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE) - this._defaultRpc = this._networkEndpoints[DEFAULT_RPC] + this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE) + this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider) this.networkStore = new ObservableStore('loading') @@ -37,17 +44,13 @@ module.exports = class NetworkController extends EventEmitter { } this._networkEndpointVersion = version - this._networkEndpoints = this.getNetworkEndpoints(version) - this._defaultRpc = this._networkEndpoints[DEFAULT_RPC] + this._networkEndpoints = getNetworkEndpoints(version) + this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] const { type } = this.getProviderConfig() return this.setProviderType(type, true) } - getNetworkEndpoints (version = OLD_UI_NETWORK_TYPE) { - return networkConfig[version] - } - initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const { type, rpcTarget } = this.providerStore.getState() diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js new file mode 100644 index 000000000..4f38ccda4 --- /dev/null +++ b/app/scripts/controllers/network/util.js @@ -0,0 +1,65 @@ +const { + ROPSTEN, + RINKEBY, + KOVAN, + MAINNET, + LOCALHOST, + ROPSTEN_CODE, + RINKEYBY_CODE, + KOVAN_CODE, + ROPSTEN_DISPLAY_NAME, + RINKEBY_DISPLAY_NAME, + KOVAN_DISPLAY_NAME, + MAINNET_DISPLAY_NAME, + MAINNET_RPC_URL, + ROPSTEN_RPC_URL, + KOVAN_RPC_URL, + RINKEBY_RPC_URL, + LOCALHOST_RPC_URL, + MAINNET_RPC_URL_BETA, + ROPSTEN_RPC_URL_BETA, + KOVAN_RPC_URL_BETA, + RINKEBY_RPC_URL_BETA, + OLD_UI_NETWORK_TYPE, + BETA_UI_NETWORK_TYPE, +} = require('./enums') + +const networkToNameMap = { + [ROPSTEN]: ROPSTEN_DISPLAY_NAME, + [RINKEBY]: RINKEBY_DISPLAY_NAME, + [KOVAN]: KOVAN_DISPLAY_NAME, + [MAINNET]: MAINNET_DISPLAY_NAME, + [ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME, + [RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME, + [KOVAN_CODE]: KOVAN_DISPLAY_NAME, +} + +const networkEndpointsMap = { + [OLD_UI_NETWORK_TYPE]: { + [LOCALHOST]: LOCALHOST_RPC_URL, + [MAINNET]: MAINNET_RPC_URL, + [ROPSTEN]: ROPSTEN_RPC_URL, + [KOVAN]: KOVAN_RPC_URL, + [RINKEBY]: RINKEBY_RPC_URL, + }, + [BETA_UI_NETWORK_TYPE]: { + [LOCALHOST]: LOCALHOST_RPC_URL, + [MAINNET]: MAINNET_RPC_URL_BETA, + [ROPSTEN]: ROPSTEN_RPC_URL_BETA, + [KOVAN]: KOVAN_RPC_URL_BETA, + [RINKEBY]: RINKEBY_RPC_URL_BETA, + }, +} + +const getNetworkDisplayName = key => networkToNameMap[key] + +const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => { + return { + ...networkEndpointsMap[networkType], + } +} + +module.exports = { + getNetworkDisplayName, + getNetworkEndpoints, +} diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js index 144534f43..c49d89288 100644 --- a/app/scripts/first-time-state.js +++ b/app/scripts/first-time-state.js @@ -1,6 +1,7 @@ // test and development environment variables const env = process.env.METAMASK_ENV const METAMASK_DEBUG = process.env.METAMASK_DEBUG +const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums') /** * @typedef {Object} FirstTimeState @@ -15,7 +16,7 @@ const initialState = { config: {}, NetworkController: { provider: { - type: (METAMASK_DEBUG || env === 'test') ? 'rinkeby' : 'mainnet', + type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET, }, }, } diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 63d27c40e..c10ff2f4e 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,12 +1,11 @@ const ethUtil = require('ethereumjs-util') const normalize = require('eth-sig-util').normalize -const MetamaskConfig = require('../config.js') - - -const MAINNET_RPC = MetamaskConfig.network.mainnet -const ROPSTEN_RPC = MetamaskConfig.network.ropsten -const KOVAN_RPC = MetamaskConfig.network.kovan -const RINKEBY_RPC = MetamaskConfig.network.rinkeby +const { + MAINNET_RPC_URL, + ROPSTEN_RPC_URL, + KOVAN_RPC_URL, + RINKEBY_RPC_URL, +} = require('../controllers/network/enums') /* The config-manager is a convenience object * wrapping a pojo-migrator. @@ -174,19 +173,19 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { switch (provider.type) { case 'mainnet': - return MAINNET_RPC + return MAINNET_RPC_URL case 'ropsten': - return ROPSTEN_RPC + return ROPSTEN_RPC_URL case 'kovan': - return KOVAN_RPC + return KOVAN_RPC_URL case 'rinkeby': - return RINKEBY_RPC + return RINKEBY_RPC_URL default: - return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC + return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL } } diff --git a/old-ui/app/app.js b/old-ui/app/app.js index fc0d634aa..3aa845b3a 100644 --- a/old-ui/app/app.js +++ b/old-ui/app/app.js @@ -35,7 +35,7 @@ const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete') const HDRestoreVaultScreen = require('./keychains/hd/restore-vault') const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation') const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns -const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums +const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums') module.exports = connect(mapStateToProps)(App) diff --git a/old-ui/app/components/buy-button-subview.js b/old-ui/app/components/buy-button-subview.js index 56d173839..8bb73ae3e 100644 --- a/old-ui/app/components/buy-button-subview.js +++ b/old-ui/app/components/buy-button-subview.js @@ -8,7 +8,7 @@ const ShapeshiftForm = require('./shapeshift-form') const Loading = require('./loading') const AccountPanel = require('./account-panel') const RadioList = require('./custom-radio-list') -const networkNames = require('../../../app/scripts/config.js').networkNames +const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util') module.exports = connect(mapStateToProps)(BuyButtonSubview) @@ -142,7 +142,7 @@ BuyButtonSubview.prototype.primarySubview = function () { case '3': case '4': case '42': - const networkName = networkNames[network] + const networkName = getNetworkDisplayName(network) const label = `${networkName} Test Faucet` return ( h('div.flex-column', { diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index dd0b52365..2b905718b 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -1,6 +1,10 @@ const assert = require('assert') const nock = require('nock') const NetworkController = require('../../app/scripts/controllers/network') +const { + getNetworkDisplayName, + getNetworkEndpoints, +} = require('../../app/scripts/controllers/network/util') const { createTestProviderTools } = require('../stub/provider') const providerResultStub = {} @@ -79,4 +83,40 @@ describe('# Network Controller', function () { }) }) }) -}) \ No newline at end of file +}) + +describe('# Network utils', () => { + it('getNetworkDisplayName should return the correct network name', () => { + const tests = [ + { + input: 3, + expected: 'Ropsten', + }, { + input: 4, + expected: 'Rinkeby', + }, { + input: 42, + expected: 'Kovan', + }, { + input: 'ropsten', + expected: 'Ropsten', + }, { + input: 'rinkeby', + expected: 'Rinkeby', + }, { + input: 'kovan', + expected: 'Kovan', + }, { + input: 'mainnet', + expected: 'Main Ethereum Network', + }, + ] + + tests.forEach(({ input, expected }) => assert.equal(getNetworkDisplayName(input), expected)) + }) + + it('getNetworkEndpoints should return the correct endpoints', () => { + assert.equal(getNetworkEndpoints('networkBeta').ropsten, 'https://ropsten.infura.io/metamask2') + assert.equal(getNetworkEndpoints('network').rinkeby, 'https://rinkeby.infura.io/metamask') + }) +}) diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js index 9ac565cf4..fda7c3e17 100644 --- a/ui/app/components/buy-button-subview.js +++ b/ui/app/components/buy-button-subview.js @@ -9,7 +9,7 @@ const ShapeshiftForm = require('./shapeshift-form') const Loading = require('./loading') const AccountPanel = require('./account-panel') const RadioList = require('./custom-radio-list') -const networkNames = require('../../../app/scripts/config.js').networkNames +const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util') BuyButtonSubview.contextTypes = { t: PropTypes.func, @@ -148,7 +148,7 @@ BuyButtonSubview.prototype.primarySubview = function () { case '3': case '4': case '42': - const networkName = networkNames[network] + const networkName = getNetworkDisplayName(network) const label = `${networkName} ${this.context.t('testFaucet')}` return ( h('div.flex-column', { diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js index d871e7516..c70510b5f 100644 --- a/ui/app/components/modals/buy-options-modal.js +++ b/ui/app/components/modals/buy-options-modal.js @@ -4,7 +4,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') -const networkNames = require('../../../../app/scripts/config.js').networkNames +const { getNetworkDisplayName } = require('../../../../app/scripts/controllers/network/util') function mapStateToProps (state) { return { @@ -52,7 +52,7 @@ BuyOptions.prototype.renderModalContentOption = function (title, header, onClick BuyOptions.prototype.render = function () { const { network, toCoinbase, address, toFaucet } = this.props const isTestNetwork = ['3', '4', '42'].find(n => n === network) - const networkName = networkNames[network] + const networkName = getNetworkDisplayName(network) return h('div', {}, [ h('div.buy-modal-content.transfers-subview', { diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js index 0dc611f50..ad5f9b695 100644 --- a/ui/app/components/modals/deposit-ether-modal.js +++ b/ui/app/components/modals/deposit-ether-modal.js @@ -4,7 +4,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') -const networkNames = require('../../../../app/scripts/config.js').networkNames +const { getNetworkDisplayName } = require('../../../../app/scripts/controllers/network/util') const ShapeshiftForm = require('../shapeshift-form') let DIRECT_DEPOSIT_ROW_TITLE @@ -122,7 +122,7 @@ DepositEtherModal.prototype.render = function () { const { buyingWithShapeshift } = this.state const isTestNetwork = ['3', '4', '42'].find(n => n === network) - const networkName = networkNames[network] + const networkName = getNetworkDisplayName(network) return h('div.page-container.page-container--full-width.page-container--full-height', {}, [ diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js index 05a7379fb..bdefe56f8 100644 --- a/ui/app/components/pages/settings/settings.js +++ b/ui/app/components/pages/settings/settings.js @@ -12,7 +12,7 @@ const SimpleDropdown = require('../../dropdowns/simple-dropdown') const ToggleButton = require('react-toggle-button') const { REVEAL_SEED_ROUTE } = require('../../../routes') const locales = require('../../../../../app/_locales/index.json') -const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/config').enums +const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/controllers/network/enums') const getInfuraCurrencyOptions = () => { const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { diff --git a/ui/app/components/pages/unlock.js b/ui/app/components/pages/unlock.js index 567b72518..30144b978 100644 --- a/ui/app/components/pages/unlock.js +++ b/ui/app/components/pages/unlock.js @@ -16,7 +16,7 @@ const { getEnvironmentType } = require('../../../../app/scripts/lib/util') const getCaretCoordinates = require('textarea-caret') const EventEmitter = require('events').EventEmitter const Mascot = require('../mascot') -const { OLD_UI_NETWORK_TYPE } = require('../../../../app/scripts/config').enums +const { OLD_UI_NETWORK_TYPE } = require('../../../../app/scripts/controllers/network/enums') const { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } = require('../../routes') class UnlockScreen extends Component { diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index 3df040922..6cb548bb9 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -10,7 +10,7 @@ const getCaretCoordinates = require('textarea-caret') const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes') const { getEnvironmentType } = require('../../../app/scripts/lib/util') const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums') -const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums +const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums') class InitializeMenuScreen extends Component { constructor (props) { diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 5f965fbe0..bb35cf990 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -3,7 +3,7 @@ const actions = require('../actions') const MetamascaraPlatform = require('../../../app/scripts/platforms/window') const { getEnvironmentType } = require('../../../app/scripts/lib/util') const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums') -const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums +const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums') module.exports = reduceMetamask diff --git a/ui/app/select-app.js b/ui/app/select-app.js index d1565e2fb..808f9ba56 100644 --- a/ui/app/select-app.js +++ b/ui/app/select-app.js @@ -7,7 +7,7 @@ const App = require('./app') const OldApp = require('../../old-ui/app/app') const { autoAddToBetaUI } = require('./selectors') const { setFeatureFlag, setNetworkEndpoints } = require('./actions') -const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums +const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums') const I18nProvider = require('./i18n-provider') function mapStateToProps (state) { diff --git a/ui/app/unlock.js b/ui/app/unlock.js index 1325656af..099e5f9c6 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -6,7 +6,7 @@ const connect = require('react-redux').connect const actions = require('./actions') const getCaretCoordinates = require('textarea-caret') const EventEmitter = require('events').EventEmitter -const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums +const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums') const { getEnvironmentType } = require('../../app/scripts/lib/util') const { ENVIRONMENT_TYPE_POPUP } = require('../../app/scripts/lib/enums') diff --git a/ui/index.js b/ui/index.js index 7b6faac76..075faf66d 100644 --- a/ui/index.js +++ b/ui/index.js @@ -5,7 +5,11 @@ const actions = require('./app/actions') const configureStore = require('./app/store') const txHelper = require('./lib/tx-helper') const { fetchLocale } = require('./i18n-helper') -const { OLD_UI_NETWORK_TYPE, BETA_UI_NETWORK_TYPE } = require('../app/scripts/config').enums +const { + OLD_UI_NETWORK_TYPE, + BETA_UI_NETWORK_TYPE, +} = require('../app/scripts/controllers/network/enums') + const log = require('loglevel') module.exports = launchMetamaskUi From c120fbdfb9c5fb714af6279e362e07a9fe64f292 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 23 Apr 2018 10:20:40 -0700 Subject: [PATCH 30/79] deps - bump sentry for more detailed fetch failure --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e01eba370..8ff344cdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19752,9 +19752,9 @@ } }, "raven-js": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.0.tgz", - "integrity": "sha512-+/ygcWib8PXAE7Xq53j1tYxCgkzFyp9z05LYAKp2PA9KwO4Ek74q1tkGwZyPWI/FoXOgas6jNtQ7O3tdPif6uA==" + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.2.tgz", + "integrity": "sha512-Dy/FHDxuo5pXywVf8Nrs5utB2juMATpkxWGqHjVbpFD3m8CaWYLvkB5SEXjWFUZ5GvUsrBVVQ+Dfcp0x6Z7SOg==" }, "raw-body": { "version": "2.3.2", diff --git a/package.json b/package.json index 2c5b05547..914b29707 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "pumpify": "^1.3.4", "qrcode-npm": "0.0.3", "ramda": "^0.24.1", - "raven-js": "^3.24.0", + "raven-js": "^3.24.2", "react": "^15.6.2", "react-addons-css-transition-group": "^15.6.0", "react-dom": "^15.6.2", From 5abb0256a9de6f45cf47da5baa1a4f3c45c473c9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 23 Apr 2018 14:47:11 -0700 Subject: [PATCH 31/79] Linted docs --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ff974506c..edde38819 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -722,7 +722,7 @@ module.exports = class MetamaskController extends EventEmitter { * Triggers signing, and the callback function from newUnsignedPersonalMessage. * * @param {Object} msgParams - The params of the message to sign & return to the Dapp. - * @returns {Promise/} - A full state update. + * @returns {Promise} - A full state update. */ signPersonalMessage (msgParams) { log.info('MetaMaskController - signPersonalMessage') From a833f02f33380ff636c4f01937bb7140ea544aa9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 23 Apr 2018 16:39:08 -0700 Subject: [PATCH 32/79] Use the metamask balanc3 dev price api Fixes #4056 --- app/scripts/controllers/token-rates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index 28409ea10..21384f262 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -39,7 +39,7 @@ class TokenRatesController { */ async fetchExchangeRate (address) { try { - const response = await fetch(`https://exchanges.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) + const response = await fetch(`https://metamask.dev.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) const json = await response.json() return json && json.length ? json[0].averagePrice : 0 } catch (error) { } From 016d492414b1229ec9bc023782a8cd571909afca Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 24 Apr 2018 12:20:38 -0230 Subject: [PATCH 33/79] Remove unnecessary doc comment. --- app/scripts/lib/typed-message-manager.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index e9be93afd..c58921610 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -9,8 +9,6 @@ const log = require('loglevel') * Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a * signature for an eth_signTypedData call is requested. * - * @see {@link } - * * @typedef {Object} TypedMessage * @property {number} id An id to track and identify the message object * @property {Object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is From 020824f3c79c8c7a17f827b47ff9e54338d2b34a Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 24 Apr 2018 12:36:26 -0230 Subject: [PATCH 34/79] Remove accidentally added code in message-manager. --- app/scripts/lib/message-manager.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index a1c6d6ac8..901367f04 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -3,14 +3,6 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const createId = require('./random-id') -var msgData = { - id: msgId, - msgParams: msgParams, - time: time, - status: 'unapproved', - type: 'eth_sign', -} - /** * Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for * an eth_sign call is requested. From 0fbd389a509a2447d833192bbe854c586890d512 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 24 Apr 2018 13:18:34 -0230 Subject: [PATCH 35/79] lint fix --- app/scripts/lib/personal-message-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 896a65df8..e96ced1f2 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -3,6 +3,7 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const createId = require('./random-id') const hexRe = /^[0-9A-Fa-f]+$/g +const log = require('loglevel') /** * Represents, and contains data about, an 'personal_sign' type signature request. These are created when a From 3500689ed0a7e19cd36283df10482171572d0d1c Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 24 Apr 2018 09:53:16 -0700 Subject: [PATCH 36/79] i18n-helper - abort on i18n failure --- ui/i18n-helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/i18n-helper.js b/ui/i18n-helper.js index 3eee55ae9..fddce522b 100644 --- a/ui/i18n-helper.js +++ b/ui/i18n-helper.js @@ -11,8 +11,9 @@ const getMessage = (locale, key, substitutions) => { const { current, en } = locale const entry = current[key] || en[key] if (!entry) { - log.error(`Translator - Unable to find value for "${key}"`) // throw new Error(`Translator - Unable to find value for "${key}"`) + log.error(`Translator - Unable to find value for "${key}"`) + return '' } let phrase = entry.message // perform substitutions From 5c7f00cae0d7941d7b4f44edc94c625ae86acb23 Mon Sep 17 00:00:00 2001 From: Dan Finlay <542863+danfinlay@users.noreply.github.com> Date: Tue, 24 Apr 2018 14:11:20 -0700 Subject: [PATCH 37/79] Clarify privacy notice We got an alarmed user in support because the word "account" does not clarify what is revealed, and could imply private key. --- notices/archive/notice_2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notices/archive/notice_2.md b/notices/archive/notice_2.md index 4cea97b4f..62f368c50 100644 --- a/notices/archive/notice_2.md +++ b/notices/archive/notice_2.md @@ -1,6 +1,6 @@ MetaMask is beta software. -When you log in to MetaMask, your current account is visible to every new site you visit. +When you log in to MetaMask, your current account's address is visible to every new site you visit. This can be used to look up your account balances of Ether and other tokens. For your privacy, for now, please sign out of MetaMask when you're done using a site. From 7ec775d614b3717a424e6a5576aef90a74e4344d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 24 Apr 2018 14:12:57 -0700 Subject: [PATCH 38/79] Use latest balanc3 api for prices --- app/scripts/controllers/token-rates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index 21384f262..abeec4cc0 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -39,7 +39,7 @@ class TokenRatesController { */ async fetchExchangeRate (address) { try { - const response = await fetch(`https://metamask.dev.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) + const response = await fetch(`https://metamask.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) const json = await response.json() return json && json.length ? json[0].averagePrice : 0 } catch (error) { } From 0c5add59f72a1700d5f2294d988fc319e652fdef Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 24 Apr 2018 14:55:43 -0700 Subject: [PATCH 39/79] Make minified builds more determinstic May fix #3999, but will need to see if Mozilla can reproduce the build with this updated repo. Switches our `uglifyify` dependency from the production one (under-maintained) to one that I've merged a critical patch into. I'm open to discussion of how else we might approach this problem here. Maybe we should use a different minification module entirely, remove minification, or maybe refactor our build system! --- package-lock.json | 1734 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 1661 insertions(+), 75 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ff344cdf..4b6fb2296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -637,6 +637,11 @@ "sprintf-js": "1.0.3" } }, + "argsarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz", + "integrity": "sha1-bnIHtOzbObCviDA/pa4ivajfYcs=" + }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -908,8 +913,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "async-reduce": { "version": "0.0.1", @@ -2505,8 +2509,7 @@ "browser-stdout": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" }, "browser-unpack": { "version": "1.2.0", @@ -2805,7 +2808,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", - "dev": true, "requires": { "base64-js": "1.2.1", "ieee754": "1.1.8" @@ -2814,20 +2816,29 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" }, + "buffer-from": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", + "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==" + }, "buffer-more-ints": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=", "dev": true }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -2869,6 +2880,23 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, + "bytewise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", + "integrity": "sha1-HRPL/3F65xWAlKqIGzXQgbOHJT4=", + "requires": { + "bytewise-core": "1.2.3", + "typewise": "1.0.3" + } + }, + "bytewise-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", + "integrity": "sha1-P7QQx+kVWOsasiqCg0V3qmvWHUI=", + "requires": { + "typewise-core": "1.2.0" + } + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -2921,6 +2949,25 @@ "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", "dev": true }, + "cachedown": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cachedown/-/cachedown-1.0.0.tgz", + "integrity": "sha1-1D8DbkUQaWsxJG19sx6/D3rDLRU=", + "requires": { + "abstract-leveldown": "2.6.3", + "lru-cache": "3.2.0" + }, + "dependencies": { + "lru-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", + "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", + "requires": { + "pseudomap": "1.0.2" + } + } + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -3767,6 +3814,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "4.1.1", + "vary": "1.1.2" + } + }, "cosmiconfig": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", @@ -4130,6 +4186,11 @@ "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" }, + "d64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz", + "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA=" + }, "dargs": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", @@ -4228,15 +4289,116 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "requires": { + "decompress-tar": "4.1.1", + "decompress-tarbz2": "4.1.1", + "decompress-targz": "4.1.1", + "decompress-unzip": "4.0.1", + "graceful-fs": "4.1.11", + "make-dir": "1.2.0", + "pify": "2.3.0", + "strip-dirs": "2.1.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, "requires": { "mimic-response": "1.0.0" } }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "5.2.0", + "is-stream": "1.1.0", + "tar-stream": "1.5.5" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "4.1.1", + "file-type": "6.2.0", + "is-stream": "1.1.0", + "seek-bzip": "1.0.5", + "unbzip2-stream": "1.2.5" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "4.1.1", + "file-type": "5.2.0", + "is-stream": "1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "3.9.0", + "get-stream": "2.3.1", + "pify": "2.3.0", + "yauzl": "2.9.1" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "4.1.1", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + } + } + }, "deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", @@ -4832,8 +4994,7 @@ "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "duplexify": { "version": "3.5.1", @@ -5804,6 +5965,20 @@ } } }, + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0", + "keccakjs": "0.2.1", + "nano-json-stream-parser": "0.1.2", + "servify": "0.1.12", + "ws": "3.3.3", + "xhr-request-promise": "0.1.2" + } + }, "eth-phishing-detect": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/eth-phishing-detect/-/eth-phishing-detect-1.1.12.tgz", @@ -5983,6 +6158,28 @@ } } }, + "ethjs-contract": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.1.9.tgz", + "integrity": "sha1-HCdmiWpW1H7B1tZhgpxJzDilUgo=", + "requires": { + "ethjs-abi": "0.2.0", + "ethjs-filter": "0.1.5", + "ethjs-util": "0.1.3", + "js-sha3": "0.5.5" + }, + "dependencies": { + "ethjs-util": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz", + "integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + } + } + }, "ethjs-format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.2.tgz", @@ -6238,6 +6435,29 @@ "number-to-bn": "1.7.0" } }, + "ethjs-contract": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.1.9.tgz", + "integrity": "sha1-HCdmiWpW1H7B1tZhgpxJzDilUgo=", + "requires": { + "ethjs-abi": "0.2.0", + "ethjs-filter": "0.1.5", + "ethjs-util": "0.1.3", + "js-sha3": "0.5.5" + }, + "dependencies": { + "ethjs-abi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.0.tgz", + "integrity": "sha1-0+LCIQEVIPxJm3FoIDbBT8wvWyU=", + "requires": { + "bn.js": "4.11.6", + "js-sha3": "0.5.5", + "number-to-bn": "1.7.0" + } + } + } + }, "ethjs-util": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz", @@ -6277,16 +6497,24 @@ } }, "ethjs-contract": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.1.9.tgz", - "integrity": "sha1-HCdmiWpW1H7B1tZhgpxJzDilUgo=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.2.0.tgz", + "integrity": "sha512-jkQrDvoeaFI4oxatkwRDZrCVCszGZ02WJgiWX2RhQA9ifNw+oSUNBj5DJ7/sc0iiR3w4fJG0fPjLwC6HkYYNzg==", "requires": { "ethjs-abi": "0.2.0", - "ethjs-filter": "0.1.5", + "ethjs-filter": "0.1.7", "ethjs-util": "0.1.3", "js-sha3": "0.5.5" }, "dependencies": { + "ethjs-filter": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/ethjs-filter/-/ethjs-filter-0.1.7.tgz", + "integrity": "sha512-oQb9pjLfg1jeBtn1Pt6kONj7X4rFNX/n55IakxFWJiCAU7n9IVGpp//FU7rO7WTkYLiQLj+7L3fAJmdSWNnSWg==", + "requires": { + "ganache-core": "2.1.0" + } + }, "ethjs-util": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz", @@ -6319,6 +6547,17 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" }, + "ethjs-contract": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.1.9.tgz", + "integrity": "sha1-HCdmiWpW1H7B1tZhgpxJzDilUgo=", + "requires": { + "ethjs-abi": "0.2.0", + "ethjs-filter": "0.1.5", + "ethjs-util": "0.1.3", + "js-sha3": "0.5.5" + } + }, "ethjs-format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.2.tgz", @@ -6354,6 +6593,11 @@ "is-hex-prefixed": "1.0.0", "strip-hex-prefix": "1.0.0" } + }, + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" } } }, @@ -6841,7 +7085,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, "requires": { "pend": "1.2.0" } @@ -6881,6 +7124,11 @@ "flat": "1.0.0" } }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -7529,7 +7777,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-2.0.3.tgz", "integrity": "sha1-9k5PhUvPaJqovdy6JokW2z20aFQ=", - "dev": true, "requires": { "any-promise": "1.3.0", "fs-extra": "2.1.2", @@ -7540,14 +7787,12 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, "fs-extra": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", - "dev": true, "requires": { "graceful-fs": "4.1.11", "jsonfile": "2.4.0" @@ -8567,6 +8812,354 @@ } } }, + "ganache-core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ganache-core/-/ganache-core-2.1.0.tgz", + "integrity": "sha512-zUO61d23XHBCYOUjEQjIuENcfa6EQNBHKuG2oXWdTdGyu5Gbm4fXX9klZ2jjl9ZnBD2T4BaeBXycsLmL0S/d2w==", + "requires": { + "abstract-leveldown": "3.0.0", + "async": "2.6.0", + "bip39": "2.4.0", + "bn.js": "4.11.6", + "cachedown": "1.0.0", + "chai": "3.5.0", + "clone": "2.1.1", + "ethereumjs-account": "2.0.4", + "ethereumjs-block": "1.2.2", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "5.1.5", + "ethereumjs-vm": "2.3.3", + "ethereumjs-wallet": "0.6.0", + "fake-merkle-patricia-tree": "1.0.1", + "heap": "0.2.6", + "js-scrypt": "0.2.0", + "level-sublevel": "6.6.1", + "levelup": "1.3.9", + "localstorage-down": "0.6.7", + "lodash": "4.17.10", + "merkle-patricia-tree": "2.3.0", + "mocha": "3.3.0", + "pify": "3.0.0", + "prepend-file": "1.3.1", + "seedrandom": "2.4.3", + "shebang-loader": "0.0.1", + "solc": "0.4.18", + "temp": "0.8.3", + "tmp": "0.0.31", + "web3": "1.0.0-beta.34", + "web3-provider-engine": "13.8.0", + "websocket": "1.0.25", + "yargs": "7.1.0" + }, + "dependencies": { + "abstract-leveldown": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz", + "integrity": "sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ==", + "requires": { + "xtend": "4.0.1" + } + }, + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "requires": { + "assertion-error": "1.0.2", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + } + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", + "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=", + "requires": { + "ms": "0.7.2" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + } + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" + }, + "ethereum-common": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.16.tgz", + "integrity": "sha1-mh4Wnq00q3XgifUMpRK/0PvRJlU=" + }, + "ethereumjs-block": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.2.2.tgz", + "integrity": "sha1-LsdTSlkCG47JuDww5JaQxuuu3aE=", + "requires": { + "async": "1.5.2", + "ethereum-common": "0.0.16", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "4.5.0", + "merkle-patricia-tree": "2.3.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "ethereumjs-util": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz", + "integrity": "sha1-PpQosxfuvaPXJg2FT93alUsfG8Y=", + "requires": { + "bn.js": "4.11.6", + "create-hash": "1.1.3", + "keccakjs": "0.2.1", + "rlp": "2.0.0", + "secp256k1": "3.4.0" + } + } + } + }, + "ethereumjs-util": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", + "integrity": "sha512-xPaSEATYJpMTCGowIt0oMZwFP4R1bxd6QsWgkcDvFL0JtXsr39p32WEcD14RscCjfP41YXZPCVWA4yAg0nrJmw==", + "requires": { + "bn.js": "4.11.6", + "create-hash": "1.1.3", + "ethjs-util": "0.1.4", + "keccak": "1.4.0", + "rlp": "2.0.0", + "safe-buffer": "5.1.1", + "secp256k1": "3.4.0" + } + }, + "ethereumjs-vm": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.3.3.tgz", + "integrity": "sha512-yIWJqTEcrF9vJTCvNMxacRkAx6zIZTOW0SmSA+hSFiU1x8JyVZDi9o5udwsRVECT5RkPgQzm62kpL6Pf4qemsw==", + "requires": { + "async": "2.6.0", + "async-eventemitter": "0.2.4", + "ethereum-common": "0.2.0", + "ethereumjs-account": "2.0.4", + "ethereumjs-block": "1.7.1", + "ethereumjs-util": "5.1.5", + "fake-merkle-patricia-tree": "1.0.1", + "functional-red-black-tree": "1.0.1", + "merkle-patricia-tree": "2.3.0", + "rustbn.js": "0.1.1", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" + }, + "ethereumjs-block": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", + "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", + "requires": { + "async": "2.6.0", + "ethereum-common": "0.2.0", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "5.1.5", + "merkle-patricia-tree": "2.3.0" + } + } + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "mocha": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.3.0.tgz", + "integrity": "sha1-0pt0KNP1LILi5l3x7LcGThqrv7U=", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.0", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + } + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" + }, + "solc": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.18.tgz", + "integrity": "sha512-Kq+O3PNF9Pfq7fB+lDYAuoqRdghLmZyfngsg0h1Hj38NKAeVHeGPOGeZasn5KqdPeCzbMFvaGyTySxzGv6aXCg==", + "requires": { + "fs-extra": "0.30.0", + "memorystream": "0.3.1", + "require-from-string": "1.2.1", + "semver": "5.4.1", + "yargs": "4.8.1" + }, + "dependencies": { + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "lodash.assign": "4.2.0", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "window-size": "0.2.0", + "y18n": "3.2.1", + "yargs-parser": "2.4.1" + } + } + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "1.0.0" + } + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=" + }, + "web3": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.0.0-beta.34.tgz", + "integrity": "sha1-NH5WG3hAmMtVYzFfSQR5odkfKrE=", + "requires": { + "web3-bzz": "1.0.0-beta.34", + "web3-core": "1.0.0-beta.34", + "web3-eth": "1.0.0-beta.34", + "web3-eth-personal": "1.0.0-beta.34", + "web3-net": "1.0.0-beta.34", + "web3-shh": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + } + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "3.0.0" + } + } + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "3.0.0", + "lodash.assign": "4.2.0" + } + } + } + }, "gather-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gather-stream/-/gather-stream-1.0.0.tgz", @@ -8609,6 +9202,11 @@ "is-property": "1.0.2" } }, + "generic-pool": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.0.4.tgz", + "integrity": "sha1-+XGN7agvoSXtXEPjQcmiFadm2aM=" + }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -8634,8 +9232,7 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-uri": { "version": "2.0.1", @@ -8985,8 +9582,7 @@ "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "grouped-queue": { "version": "0.3.3", @@ -10617,11 +11213,15 @@ "sparkles": "1.0.0" } }, + "has-localstorage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-localstorage/-/has-localstorage-1.0.1.tgz", + "integrity": "sha1-/mJAbEdn+9bXhNrGkFkoEIuClxs=" + }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" }, "has-symbols": { "version": "1.0.0", @@ -10632,7 +11232,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "dev": true, "requires": { "has-symbol-support-x": "1.4.2" } @@ -10744,6 +11343,11 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "heap": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", + "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=" + }, "hipchat-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz", @@ -10995,6 +11599,11 @@ } } }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, "http-parser-js": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", @@ -11077,6 +11686,15 @@ "resolved": "https://registry.npmjs.org/human-standard-token-abi/-/human-standard-token-abi-1.0.2.tgz", "integrity": "sha1-IH14Rnlu5buF/dM252nLOARbKuA=" }, + "humble-localstorage": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/humble-localstorage/-/humble-localstorage-1.4.2.tgz", + "integrity": "sha1-0Fqw1SbE7b3b98amDfb/WAUoNGk=", + "requires": { + "has-localstorage": "1.0.1", + "localstorage-memory": "1.0.2" + } + }, "i": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", @@ -11663,6 +12281,11 @@ "xtend": "4.0.1" } }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -11690,8 +12313,7 @@ "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" }, "is-observable": { "version": "0.2.0", @@ -11817,8 +12439,7 @@ "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" }, "is-scoped": { "version": "1.0.0", @@ -11956,7 +12577,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, "requires": { "has-to-string-tag-x": "1.4.1", "is-object": "1.0.1" @@ -11994,6 +12614,14 @@ "integrity": "sha1-fPLLaYGWaEeQNQ0MTKB/Su2ewX4=", "dev": true }, + "js-scrypt": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/js-scrypt/-/js-scrypt-0.2.0.tgz", + "integrity": "sha1-emK3AbRhbnCtDN5URiequ5nX/jk=", + "requires": { + "generic-pool": "2.0.4" + } + }, "js-sha3": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.3.1.tgz", @@ -12483,8 +13111,7 @@ "json3": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" }, "json5": { "version": "0.5.1", @@ -13192,6 +13819,118 @@ } } }, + "level-post": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/level-post/-/level-post-1.0.7.tgz", + "integrity": "sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew==", + "requires": { + "ltgt": "2.2.0" + } + }, + "level-sublevel": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/level-sublevel/-/level-sublevel-6.6.1.tgz", + "integrity": "sha1-+ad/dSGrcKj46S7VbyGjx4hqRIU=", + "requires": { + "bytewise": "1.1.0", + "levelup": "0.19.1", + "ltgt": "2.1.3", + "pull-level": "2.0.4", + "pull-stream": "3.6.7", + "typewiselite": "1.0.0", + "xtend": "4.0.1" + }, + "dependencies": { + "abstract-leveldown": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", + "integrity": "sha1-KeGOYy5g5OIh1YECR4UqY9ey5BA=", + "requires": { + "xtend": "3.0.0" + }, + "dependencies": { + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=" + } + } + }, + "bl": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.8.2.tgz", + "integrity": "sha1-yba8oI0bwuoA/Ir7Txpf0eHGbk4=", + "requires": { + "readable-stream": "1.0.34" + } + }, + "deferred-leveldown": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz", + "integrity": "sha1-LO8fER4cV4cNi7uK8mUOWHzS9bQ=", + "requires": { + "abstract-leveldown": "0.12.4" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "levelup": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-0.19.1.tgz", + "integrity": "sha1-86anIFJyxLXzXkEv8ASgOgrt9Qs=", + "requires": { + "bl": "0.8.2", + "deferred-leveldown": "0.2.0", + "errno": "0.1.6", + "prr": "0.0.0", + "readable-stream": "1.0.34", + "semver": "5.1.1", + "xtend": "3.0.0" + }, + "dependencies": { + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=" + } + } + }, + "ltgt": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.1.3.tgz", + "integrity": "sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ=" + }, + "prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "semver": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.1.tgz", + "integrity": "sha1-oykqNz5vPgeY2gsgZBuanFvEfhk=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "level-ws": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", @@ -13504,6 +14243,40 @@ "resolved": "https://registry.npmjs.org/locale-currency/-/locale-currency-0.0.1.tgz", "integrity": "sha1-yeFaIv9XW0tLuUekv5KsI2vR/ps=" }, + "localstorage-down": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/localstorage-down/-/localstorage-down-0.6.7.tgz", + "integrity": "sha1-0Hmak7MebF+lGI7AYkLrHM6dbRU=", + "requires": { + "abstract-leveldown": "0.12.3", + "argsarray": "0.0.1", + "buffer-from": "0.1.2", + "d64": "1.0.0", + "humble-localstorage": "1.4.2", + "inherits": "2.0.3", + "tiny-queue": "0.2.0" + }, + "dependencies": { + "abstract-leveldown": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.3.tgz", + "integrity": "sha1-EWsexcdxDvei1XBnaLvbREC+EHA=", + "requires": { + "xtend": "3.0.0" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=" + } + } + }, + "localstorage-memory": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/localstorage-memory/-/localstorage-memory-1.0.2.tgz", + "integrity": "sha1-zUqPIQ5V3VGckp9LTMgoKbWPmlE=" + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -13536,7 +14309,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, "requires": { "lodash._basecopy": "3.0.1", "lodash.keys": "3.1.2" @@ -13547,6 +14319,11 @@ "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + }, "lodash._baseflatten": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/lodash._baseflatten/-/lodash._baseflatten-3.1.4.tgz", @@ -13636,6 +14413,16 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -14066,6 +14853,11 @@ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, + "looper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-2.0.0.tgz", + "integrity": "sha1-Zs0Md0rz1P7axTeU90LbVtqPCew=" + }, "loose-envify": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", @@ -14086,8 +14878,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, "lru-cache": { "version": "4.1.1", @@ -14193,7 +14984,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", - "dev": true, "requires": { "pify": "3.0.0" } @@ -14927,8 +15717,7 @@ "mimic-response": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", - "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=", - "dev": true + "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" }, "min-document": { "version": "2.19.0", @@ -15099,6 +15888,14 @@ } } }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "0.5.1" + } + }, "mocha": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.0.tgz", @@ -15161,6 +15958,11 @@ "integrity": "sha1-cjqTEOfXN9e3fHpmghI3QlsDLUg=", "dev": true }, + "mock-fs": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.4.2.tgz", + "integrity": "sha512-dF+yxZSojSiI8AXGoxj5qdFWpucndc54Ug+TwlpHFaV7j22MGG+OML2+FVa6xAZtjb/OFFQhOC37Jegx2GbEwA==" + }, "module-deps": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.0.tgz", @@ -15206,6 +16008,11 @@ } } }, + "mout": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.11.1.tgz", + "integrity": "sha1-ujYR318OWx/7/QEWa48C0fX6K5k=" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -15263,7 +16070,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "requires": { "any-promise": "1.3.0", "object-assign": "4.1.1", @@ -15273,8 +16079,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" } } }, @@ -15283,6 +16088,11 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -18063,6 +18873,14 @@ "has": "1.0.1" } }, + "oboe": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.3.tgz", + "integrity": "sha1-K0hl29Rr6BIlcT9Om/5Lz09oCk8=", + "requires": { + "http-https": "1.0.0" + } + }, "obs-store": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/obs-store/-/obs-store-3.0.0.tgz", @@ -18281,8 +19099,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { "version": "1.1.0", @@ -18639,8 +19456,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "percentile": { "version": "1.2.0", @@ -19238,6 +20054,24 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, + "prepend-file": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prepend-file/-/prepend-file-1.3.1.tgz", + "integrity": "sha1-g7FuC0rBkB/OiNvZRaIvTMgd9Xk=", + "requires": { + "tmp": "0.0.31" + }, + "dependencies": { + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "requires": { + "os-tmpdir": "1.0.2" + } + } + } + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -19433,6 +20267,52 @@ "randombytes": "2.0.5" } }, + "pull-cat": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/pull-cat/-/pull-cat-1.1.11.tgz", + "integrity": "sha1-tkLdElXaN2pwa220+pYvX9t0wxs=" + }, + "pull-level": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pull-level/-/pull-level-2.0.4.tgz", + "integrity": "sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg==", + "requires": { + "level-post": "1.0.7", + "pull-cat": "1.1.11", + "pull-live": "1.0.1", + "pull-pushable": "2.2.0", + "pull-stream": "3.6.7", + "pull-window": "2.1.4", + "stream-to-pull-stream": "1.7.2" + } + }, + "pull-live": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pull-live/-/pull-live-1.0.1.tgz", + "integrity": "sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU=", + "requires": { + "pull-cat": "1.1.11", + "pull-stream": "3.6.7" + } + }, + "pull-pushable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pull-pushable/-/pull-pushable-2.2.0.tgz", + "integrity": "sha1-Xy867UethpGfAbEqLpnW8b13ZYE=" + }, + "pull-stream": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.7.tgz", + "integrity": "sha512-XdE2/o1I2lK7A+sbbA/HjYnd5Xk7wL5CwAKzqHIgcBsluDb0LiKHNTl1K0it3/RKPshQljLf4kl1aJ12YsCCGQ==" + }, + "pull-window": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/pull-window/-/pull-window-2.1.4.tgz", + "integrity": "sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA=", + "requires": { + "looper": "2.0.0" + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -19493,7 +20373,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, "requires": { "decode-uri-component": "0.2.0", "object-assign": "4.1.1", @@ -19738,6 +20617,11 @@ "safe-buffer": "5.1.1" } }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -20914,6 +21798,29 @@ "safe-buffer": "5.1.1" } }, + "seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=" + }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } + } + } + }, "selenium-webdriver": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", @@ -20986,6 +21893,18 @@ "send": "0.16.1" } }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "1.18.2", + "cors": "2.8.4", + "express": "4.16.2", + "request": "2.83.0", + "xhr": "2.4.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -21076,6 +21995,11 @@ "shebang-regex": "1.0.0" } }, + "shebang-loader": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", + "integrity": "sha1-pAAEldRMzu++xjQ157FphWn6Uuw=" + }, "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", @@ -21248,6 +22172,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "3.3.0", + "once": "1.4.0", + "simple-concat": "1.0.0" + } + }, "sinon": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-5.0.0.tgz", @@ -22229,6 +23168,22 @@ "any-observable": "0.2.0" } }, + "stream-to-pull-stream": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.2.tgz", + "integrity": "sha1-dXYJrhzr0zx0MtSvvjH/eGULnd4=", + "requires": { + "looper": "3.0.0", + "pull-stream": "3.6.7" + }, + "dependencies": { + "looper": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz", + "integrity": "sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k=" + } + } + }, "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", @@ -22255,8 +23210,7 @@ "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, "string-length": { "version": "1.0.1", @@ -22348,6 +23302,14 @@ "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", "dev": true }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "4.0.1" + } + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -22972,6 +23934,84 @@ } } }, + "swarm-js": { + "version": "0.1.37", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.37.tgz", + "integrity": "sha512-G8gi5fcXP/2upwiuOShJ258sIufBVztekgobr3cVgYXObZwJ5AXLqZn52AI+/ffft29pJexF9WNdUxjlkVehoQ==", + "requires": { + "bluebird": "3.5.1", + "buffer": "5.1.0", + "decompress": "4.2.0", + "eth-lib": "0.1.27", + "fs-extra": "2.1.2", + "fs-promise": "2.0.3", + "got": "7.1.0", + "mime-types": "2.1.17", + "mkdirp-promise": "5.0.1", + "mock-fs": "4.4.2", + "setimmediate": "1.0.5", + "tar.gz": "1.0.7", + "xhr-request-promise": "0.1.2" + }, + "dependencies": { + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-plain-obj": "1.1.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.1", + "p-cancelable": "0.3.0", + "p-timeout": "1.2.1", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "1.0.4" + } + } + } + }, "symbol-observable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz", @@ -23129,11 +24169,40 @@ "inherits": "2.0.3" } }, + "tar-stream": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", + "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", + "requires": { + "bl": "1.2.1", + "end-of-stream": "1.4.0", + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, + "tar.gz": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tar.gz/-/tar.gz-1.0.7.tgz", + "integrity": "sha512-uhGatJvds/3diZrETqMj4RxBR779LKlIE74SsMcn5JProZsfs9j0QBwWO1RW+IWNJxS2x8Zzra1+AW6OQHWphg==", + "requires": { + "bluebird": "2.11.0", + "commander": "2.11.0", + "fstream": "1.0.11", + "mout": "0.11.1", + "tar": "2.2.1" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + } + } + }, "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", - "dev": true, "requires": { "os-tmpdir": "1.0.2", "rimraf": "2.2.8" @@ -23142,8 +24211,7 @@ "rimraf": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" } } }, @@ -23362,7 +24430,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", - "dev": true, "requires": { "any-promise": "1.3.0" }, @@ -23370,8 +24437,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" } } }, @@ -23379,7 +24445,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, "requires": { "thenify": "3.3.0" } @@ -23430,8 +24495,7 @@ "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, "timers-browserify": { "version": "1.4.2", @@ -23467,6 +24531,11 @@ "dev": true, "optional": true }, + "tiny-queue": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.0.tgz", + "integrity": "sha1-xJ/LXIdVW+G0pd9+uHEB1beLydw=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -23797,25 +24866,51 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "1.0.0" + } + }, + "typewise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", + "integrity": "sha1-EGeTZUCvl5N8xdz5kiSG6fooRlE=", + "requires": { + "typewise-core": "1.2.0" + } + }, + "typewise-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", + "integrity": "sha1-l+uRgFx/VdL5QXSPpQ0xXZke8ZU=" + }, + "typewiselite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typewiselite/-/typewiselite-1.0.0.tgz", + "integrity": "sha1-yIgvobsQksBgBal/NO9chQjjZk4=" + }, "ua-parser-js": { "version": "0.7.17", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" }, "uglify-es": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.4.tgz", - "integrity": "sha512-vDOyDaf7LcABZI5oJt8bin5FD8kYONux5jd8FY6SsV2SfD+MMXaPeGUotysbycSxdu170y5IQ8FvlKzU/TUryw==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", "dev": true, "requires": { - "commander": "2.12.2", + "commander": "2.13.0", "source-map": "0.6.1" }, "dependencies": { "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, "source-map": { @@ -23833,16 +24928,14 @@ "optional": true }, "uglifyify": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/uglifyify/-/uglifyify-4.0.5.tgz", - "integrity": "sha512-vVvJjL5rxAbvUs5m0zfxLCwy5fu6+6HBRY06cpWfOGTtKgAONWy7HDovTr28Y7kwXFNAAt+OMbRGP6ulqWqakA==", + "version": "github:danfinlay/uglifyify#8662585e39125a96a5379d71cb4a606829790f87", "dev": true, "requires": { "convert-source-map": "1.1.3", "extend": "1.3.0", "minimatch": "3.0.4", "through": "2.3.8", - "uglify-es": "3.3.4" + "uglify-es": "3.3.9" }, "dependencies": { "extend": { @@ -23864,6 +24957,32 @@ "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=" }, + "unbzip2-stream": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", + "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", + "requires": { + "buffer": "3.6.0", + "through": "2.3.8" + }, + "dependencies": { + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + }, + "buffer": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", + "requires": { + "base64-js": "0.0.8", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + } + } + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -24093,11 +25212,15 @@ "prepend-http": "2.0.0" } }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, "url-to-options": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, "use": { "version": "2.0.2", @@ -24745,6 +25868,332 @@ } } }, + "web3-bzz": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.0.0-beta.34.tgz", + "integrity": "sha1-Bo03d3q2Xlxg+OyLmlDP5FJ3kpw=", + "requires": { + "got": "7.1.0", + "swarm-js": "0.1.37", + "underscore": "1.8.3" + }, + "dependencies": { + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-plain-obj": "1.1.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.1", + "p-cancelable": "0.3.0", + "p-timeout": "1.2.1", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "1.0.4" + } + } + } + }, + "web3-core": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.0.0-beta.34.tgz", + "integrity": "sha1-EhvoVV6fsA0sXQXd0zgdDJ5GmH4=", + "requires": { + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-core-requestmanager": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + } + }, + "web3-core-helpers": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz", + "integrity": "sha1-sWjaANPhnhVrwVriAyA91N/uLQM=", + "requires": { + "underscore": "1.8.3", + "web3-eth-iban": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-method": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.0.0-beta.34.tgz", + "integrity": "sha1-7BY8iixJD6AqfsFVWfpzB/x8xt0=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-promievent": "1.0.0-beta.34", + "web3-core-subscriptions": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-promievent": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.34.tgz", + "integrity": "sha1-pPT6Z4S7KT6CxglgrltWqUzQPtw=", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "1.1.1" + }, + "dependencies": { + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "eventemitter3": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA=" + } + } + }, + "web3-core-requestmanager": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.34.tgz", + "integrity": "sha1-Afj2zyrmtvC3DDi64e90G1urIVw=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34", + "web3-providers-http": "1.0.0-beta.34", + "web3-providers-ipc": "1.0.0-beta.34", + "web3-providers-ws": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-subscriptions": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.34.tgz", + "integrity": "sha1-n+0UQDPyIcPPIQYDAv/a9e8t4t4=", + "requires": { + "eventemitter3": "1.1.1", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34" + }, + "dependencies": { + "eventemitter3": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.0.0-beta.34.tgz", + "integrity": "sha1-dAhgAIUMb+b1Ne9Jg31tS7YRMmg=", + "requires": { + "underscore": "1.8.3", + "web3-core": "1.0.0-beta.34", + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-core-subscriptions": "1.0.0-beta.34", + "web3-eth-abi": "1.0.0-beta.34", + "web3-eth-accounts": "1.0.0-beta.34", + "web3-eth-contract": "1.0.0-beta.34", + "web3-eth-iban": "1.0.0-beta.34", + "web3-eth-personal": "1.0.0-beta.34", + "web3-net": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-abi": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz", + "integrity": "sha1-A0Uz46ovfln/MXk+rqaFwO1a9no=", + "requires": { + "bn.js": "4.11.6", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-accounts": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz", + "integrity": "sha1-4JFC7uzHl6w0WbdemyOUbTaV8zM=", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scrypt.js": "0.2.0", + "underscore": "1.8.3", + "uuid": "2.0.1", + "web3-core": "1.0.0-beta.34", + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0", + "xhr-request-promise": "0.1.2" + } + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } + } + }, + "web3-eth-contract": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz", + "integrity": "sha1-nbs4+udkOoCEJ6IBgEcOx0FckeY=", + "requires": { + "underscore": "1.8.3", + "web3-core": "1.0.0-beta.34", + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-core-promievent": "1.0.0-beta.34", + "web3-core-subscriptions": "1.0.0-beta.34", + "web3-eth-abi": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-iban": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz", + "integrity": "sha1-mvRYYFhnzPdOqXmq8yazi6alugw=", + "requires": { + "bn.js": "4.11.6", + "web3-utils": "1.0.0-beta.34" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "web3-eth-personal": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.34.tgz", + "integrity": "sha1-mvuhZzQuveVCC81YlcP2w0OI8gU=", + "requires": { + "web3-core": "1.0.0-beta.34", + "web3-core-helpers": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-net": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + } + }, + "web3-net": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.0.0-beta.34.tgz", + "integrity": "sha1-QnzqL0MYgUScjjjVIykPFz+f9j0=", + "requires": { + "web3-core": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-utils": "1.0.0-beta.34" + } + }, "web3-provider-engine": { "version": "13.8.0", "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz", @@ -24787,6 +26236,76 @@ } } }, + "web3-providers-http": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz", + "integrity": "sha1-5WG1K7tDdmKCAH1AKFv+NVDCfno=", + "requires": { + "web3-core-helpers": "1.0.0-beta.34", + "xhr2": "0.1.4" + }, + "dependencies": { + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + } + } + }, + "web3-providers-ipc": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.34.tgz", + "integrity": "sha1-obd/GjBtc2SanAOQUuQMtxMo0Ao=", + "requires": { + "oboe": "2.1.3", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-providers-ws": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.34.tgz", + "integrity": "sha1-fecPG4Py3jZHZ3IVa+z+9uNRbrM=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.34", + "websocket": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c", + "requires": { + "debug": "2.6.9", + "nan": "2.8.0", + "typedarray-to-buffer": "3.1.5", + "yaeti": "0.0.6" + } + } + } + }, + "web3-shh": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.0.0-beta.34.tgz", + "integrity": "sha1-l1Bh1x6uxCzO5Xb3vY9w8DhEr+A=", + "requires": { + "web3-core": "1.0.0-beta.34", + "web3-core-method": "1.0.0-beta.34", + "web3-core-subscriptions": "1.0.0-beta.34", + "web3-net": "1.0.0-beta.34" + } + }, "web3-stream-provider": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/web3-stream-provider/-/web3-stream-provider-3.0.1.tgz", @@ -24795,6 +26314,37 @@ "readable-stream": "2.3.3" } }, + "web3-utils": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.34.tgz", + "integrity": "sha1-lBH8OarvOcpOBhafdiKX2f8CCXA=", + "requires": { + "bn.js": "4.11.6", + "eth-lib": "0.1.27", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.8.3", + "utf8": "2.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=" + } + } + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -25332,6 +26882,17 @@ } } }, + "websocket": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.25.tgz", + "integrity": "sha512-M58njvi6ZxVb5k7kpnHh2BvNKuBWiwIYvsToErBzWhvBZYwlEiLcyLrG41T1jRcrY9ettqPYEqduLI7ul54CVQ==", + "requires": { + "debug": "2.6.9", + "nan": "2.8.0", + "typedarray-to-buffer": "3.1.5", + "yaeti": "0.0.6" + } + }, "websocket-driver": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", @@ -25489,7 +27050,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, "requires": { "async-limiter": "1.0.0", "safe-buffer": "5.1.1", @@ -25499,8 +27059,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" } } }, @@ -25531,6 +27090,28 @@ "xtend": "4.0.1" } }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "0.0.5", + "object-assign": "4.1.1", + "query-string": "5.1.1", + "simple-get": "2.8.1", + "timed-out": "4.0.1", + "url-set-query": "1.0.0", + "xhr": "2.4.1" + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "requires": { + "xhr-request": "1.1.0" + } + }, "xhr2": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.3.tgz", @@ -25598,6 +27179,11 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/package.json b/package.json index 88f73a609..e9ffb627d 100644 --- a/package.json +++ b/package.json @@ -272,7 +272,7 @@ "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", - "uglifyify": "^4.0.5", + "uglifyify": "github:danfinlay/uglifyify#keep-flags", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "watchify": "^3.9.0" From 91161739a5a0d45eb0467dc4943969fb64994fe6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 25 Apr 2018 10:46:01 -0700 Subject: [PATCH 40/79] Move uglifyify to MetaMask org --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9ffb627d..f47b232b7 100644 --- a/package.json +++ b/package.json @@ -272,7 +272,7 @@ "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", - "uglifyify": "github:danfinlay/uglifyify#keep-flags", + "uglifyify": "github:MetaMask/uglifyify#keep-flags", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "watchify": "^3.9.0" From b2fbc6399a94fee5e6fee17f2a84c0d28ef1e042 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 25 Apr 2018 10:57:44 -0700 Subject: [PATCH 41/79] Return key instead of empty string on i18n fail --- ui/i18n-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/i18n-helper.js b/ui/i18n-helper.js index fddce522b..7753b3bda 100644 --- a/ui/i18n-helper.js +++ b/ui/i18n-helper.js @@ -13,7 +13,7 @@ const getMessage = (locale, key, substitutions) => { if (!entry) { // throw new Error(`Translator - Unable to find value for "${key}"`) log.error(`Translator - Unable to find value for "${key}"`) - return '' + return key } let phrase = entry.message // perform substitutions From 1eb80cc46d1d375e36907992dc713c2d05ac5f7f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 25 Apr 2018 11:05:47 -0700 Subject: [PATCH 42/79] Add develop guide to contributing.md Explain our new master-develop git branch strategy, so developers know which branch to PR against. --- CONTRIBUTING.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d2e5207b..8517eed70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,12 +12,14 @@ For any new programmatic functionality, we like unit tests when possible, so if ### PR Format -We use [waffle](https://waffle.io/) for project management, and it will automatically keep us organized if you do one simple thing: - If this PR closes the issue, add the line `Fixes #$ISSUE_NUMBER`. Ex. For closing issue 418, include the line `Fixes #418`. If it doesn't close the issue but addresses it partially, just include a reference to the issue number, like `#418`. +Submit your PR against the `develop` branch. This is where we merge new features so they get some time to receive extra testing before being pushed to `master` for production. + +If your PR is a hot-fix that needs to be published urgently, you may submit a PR against the `master` branch, but this PR will receive tighter scrutiny before merging. + ## Before Merging Make sure you get a `:thumbsup`, `:+1`, or `LGTM` from another collaborator before merging. From 8ffce8b59dc95ffa1af72293f40c21f78fd103bb Mon Sep 17 00:00:00 2001 From: frankiebee Date: Wed, 25 Apr 2018 11:13:51 -0700 Subject: [PATCH 43/79] transactions - more docs and clean ups --- app/scripts/controllers/transactions/index.js | 70 ++++++++++++------- .../lib/tx-state-history-helper.js | 2 +- .../controllers/transactions/nonce-tracker.js | 25 +++++-- .../transactions/pending-tx-tracker.js | 14 ++-- .../controllers/transactions/tx-gas-utils.js | 33 +++++++-- .../transactions/tx-state-manager.js | 27 +++---- 6 files changed, 116 insertions(+), 55 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 321438598..541f1db73 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -26,17 +26,16 @@ const log = require('loglevel') @class - @param {Object} opts - @property {Object} opts.initState initial transaction list default is an empty array - @property {Object} opts.networkStore an observable store for network number - @param {Object} opts.blockTracker - An instance of eth-blocktracker - @property {Object} opts.provider - @param {Object} opts.provider - A network provider. - @property {Object} opts.signTransaction function the signs an ethereumjs-tx - @property {function} opts.getGasPrice optional gas price calculator - @property {function} opts.signTransaction ethTx signer that returns a rawTx - @property {number} opts.txHistoryLimit number *optional* for limiting how many transactions are in state - @property {Object} opts.preferencesStore + @param {object} - opts + @param {object} opts.initState - initial transaction list default is an empty array + @param {Object} opts.networkStore - an observable store for network number + @param {Object} opts.blockTracker - An instance of eth-blocktracker + @param {Object} opts.provider - A network provider. + @param {Function} opts.signTransaction - function the signs an ethereumjs-tx + @param {Function} [opts.getGasPrice] - optional gas price calculator + @param {Function} opts.signTransaction - ethTx signer that returns a rawTx + @param {Number} [opts.txHistoryLimit] - number *optional* for limiting how many transactions are in state + @param {Object} opts.preferencesStore */ class TransactionController extends EventEmitter { @@ -105,7 +104,7 @@ class TransactionController extends EventEmitter { } /** - wipes the transactions for a given account + Wipes the transactions for a given account @param {string} address - hex string of the from address for txs being removed */ wipeTransactions (address) { @@ -115,9 +114,9 @@ class TransactionController extends EventEmitter { /** add a new unapproved transaction to the pipeline - @returns {promise} - @param txParams {Object} - txParams for the transaction - @param opts {Object} - with the key origin to put the origin on the txMeta + @returns {Promise} the hash of the transaction after being submitted to the network + @param txParams {object} - txParams for the transaction + @param opts {object} - with the key origin to put the origin on the txMeta */ async newUnapprovedTransaction (txParams, opts = {}) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) @@ -142,7 +141,7 @@ class TransactionController extends EventEmitter { } /** - validates and generates a txMeta with defaults and puts it in txStateManager + Validates and generates a txMeta with defaults and puts it in txStateManager store @returns {txMeta} @@ -173,7 +172,7 @@ class TransactionController extends EventEmitter { /** adds the tx gas defaults: gas && gasPrice @param txMeta {Object} - the txMeta object - @returns {promise} resolves with txMeta + @returns {Promise} resolves with txMeta */ async addTxGasDefaults (txMeta) { const txParams = txMeta.txParams @@ -190,7 +189,7 @@ class TransactionController extends EventEmitter { } /** - creates a new txMeta with the same txParams as the original + Creates a new txMeta with the same txParams as the original to allow the user to resign the transaction with a higher gas values @param originalTxId {number} - the id of the txMeta that you want to attempt to retry @@ -290,6 +289,7 @@ class TransactionController extends EventEmitter { publishes the raw tx and sets the txMeta to submitted @param txId {number} - the tx's Id @param rawTx {string} - the hex string of the serialized signed transaction + @returns {Promise} */ async publishTransaction (txId, rawTx) { const txMeta = this.txStateManager.getTx(txId) @@ -301,15 +301,16 @@ class TransactionController extends EventEmitter { } /** - convenience method for the ui thats sets the transaction to rejected + Convenience method for the ui thats sets the transaction to rejected @param txId {number} - the tx's Id + @returns {Promise} */ async cancelTransaction (txId) { this.txStateManager.setTxStatusRejected(txId) } /** - sets the txHas on the txMeta + Sets the txHas on the txMeta @param txId {number} - the tx's Id @param txHash {string} - the hash for the txMeta */ @@ -325,20 +326,29 @@ class TransactionController extends EventEmitter { // /** maps methods for convenience*/ _mapMethods () { - /** Returns the state in transaction controller */ + /** @returns the state in transaction controller */ this.getState = () => this.memStore.getState() - /** Returns the network number stored in networkStore */ + /** @returns the network number stored in networkStore */ this.getNetwork = () => this.networkStore.getState() - /** Returns the user selected address */ + /** @returns the user selected address */ this.getSelectedAddress = () => this.preferencesStore.getState().selectedAddress /** Returns an array of transactions whos status is unapproved */ this.getUnapprovedTxCount = () => Object.keys(this.txStateManager.getUnapprovedTxList()).length - /** Returns a number that represents how many transactions have the status submitted*/ + /** + @returns a number that represents how many transactions have the status submitted + @param account {String} - hex prefixed account + */ this.getPendingTxCount = (account) => this.txStateManager.getPendingTransactions(account).length /** see txStateManager */ this.getFilteredTxList = (opts) => this.txStateManager.getFilteredTxList(opts) } + /** + If transaction controller was rebooted with transactions that are uncompleted + in steps of the transaction signing or user confirmation process it will either + transition txMetas to a failed state or try to redo those tasks. + */ + _onBootCleanUp () { this.txStateManager.getFilteredTxList({ status: 'unapproved', @@ -364,13 +374,13 @@ class TransactionController extends EventEmitter { /** is called in constructor applies the listeners for pendingTxTracker txStateManager and blockTracker -
*/ _setupListners () { this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.pendingTxTracker.on('tx:warning', (txMeta) => { this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning') }) + this.pendingTxTracker.on('tx:confirmed', (txId) => this.txStateManager.setTxStatusConfirmed(txId)) this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId)) this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { @@ -393,8 +403,13 @@ class TransactionController extends EventEmitter { } + /** + Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions + in the list have the same nonce + + @param txId {Number} - the txId of the transaction that has been confirmed in a block + */ _markNonceDuplicatesDropped (txId) { - this.txStateManager.setTxStatusConfirmed(txId) // get the confirmed transactions nonce and from address const txMeta = this.txStateManager.getTx(txId) const { nonce, from } = txMeta.txParams @@ -409,6 +424,9 @@ class TransactionController extends EventEmitter { }) } + /** + Updates the memStore in transaction controller + */ _updateMemstore () { const unapprovedTxs = this.txStateManager.getUnapprovedTxList() const selectedAddressTxList = this.txStateManager.getFilteredTxList({ diff --git a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js index 7a57e3cb5..59a4b562c 100644 --- a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js +++ b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js @@ -52,7 +52,7 @@ function replayHistory (_shortHistory) { } /** - @param txMeta {object} + @param txMeta {Object} @returns {object} a clone object of the txMeta with out history */ function snapshotFromTxMeta (txMeta) { diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js index e2c5dadef..f8cdc5523 100644 --- a/app/scripts/controllers/transactions/nonce-tracker.js +++ b/app/scripts/controllers/transactions/nonce-tracker.js @@ -2,11 +2,11 @@ const EthQuery = require('ethjs-query') const assert = require('assert') const Mutex = require('await-semaphore').Mutex /** - @param opts {Object} - - @property {Object} opts.provider a ethereum provider - @property {Function} opts.getPendingTransactions a function that returns an array of txMeta + @param opts {Object} + @param {Object} opts.provider a ethereum provider + @param {Function} opts.getPendingTransactions a function that returns an array of txMeta whosee status is `submitted` - @property {Function} opts.getConfirmedTransactions a function that returns an array of txMeta + @param {Function} opts.getConfirmedTransactions a function that returns an array of txMeta whose status is `confirmed` @class */ @@ -42,7 +42,7 @@ class NonceTracker { Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding). @param address {string} the hex string for the address whose nonce we are calculating - @returns {Promise} + @returns {Promise} */ async getNonceLock (address) { // await global mutex free @@ -146,6 +146,17 @@ class NonceTracker { return highestNonce } + /** + @typedef {object} highestContinuousFrom + @property {string} - name the name for how the nonce was calculated based on the data used + @property {number} - nonce the next suggested nonce + @property {object} - details the provided starting nonce that was used (for debugging) + */ + /** + @param txList {array} - list of txMeta's + @param startPoint {number} - the highest known locally confirmed nonce + @returns {highestContinuousFrom} + */ _getHighestContinuousFrom (txList, startPoint) { const nonces = txList.map((txMeta) => { const nonce = txMeta.txParams.nonce @@ -163,6 +174,10 @@ class NonceTracker { // this is a hotfix for the fact that the blockTracker will // change when the network changes + + /** + @returns {Object} the current blockTracker + */ _getBlockTracker () { return this.provider._blockTracker } diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index f7c9993b3..6e2fcb40b 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -9,10 +9,10 @@ const EthQuery = require('ethjs-query') As well as continues broadcast while in the pending state
@param config {object} - non optional configuration object consists of: - @property {Object} config.provider - @property {Object} config.nonceTracker see nonce tracker - @property {function} config.getPendingTransactions a function for getting an array of transactions, - @property {function} config.publishTransaction a async function for publishing raw transactions, + @param {Object} config.provider - A network provider. + @param {Object} config.nonceTracker see nonce tracker + @param {function} config.getPendingTransactions a function for getting an array of transactions, + @param {function} config.publishTransaction a async function for publishing raw transactions, @class @@ -117,7 +117,7 @@ class PendingTransactionTracker extends EventEmitter { /** resubmits the individual txMeta used in resubmitPendingTxs - @param txMeta {object} - txMeta object + @param txMeta {Object} - txMeta object @param latestBlockNumber {string} - hex string for the latest block number @emits tx:retry @returns txHash {string} @@ -147,7 +147,7 @@ class PendingTransactionTracker extends EventEmitter { } /** Ask the network for the transaction to see if it has been include in a block - @param txMeta {object} - the txMeta object + @param txMeta {Object} - the txMeta object @emits tx:failed @emits tx:confirmed @emits tx:warning @@ -208,7 +208,7 @@ class PendingTransactionTracker extends EventEmitter { /** checks to see if a confirmed txMeta has the same nonce - @param txMeta {object} - txMeta object + @param txMeta {Object} - txMeta object @returns {boolean} */ async _checkIfNonceIsTaken (txMeta) { diff --git a/app/scripts/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js index 31a5bfcf4..36b5cdbc9 100644 --- a/app/scripts/controllers/transactions/tx-gas-utils.js +++ b/app/scripts/controllers/transactions/tx-gas-utils.js @@ -7,19 +7,23 @@ const { const { addHexPrefix } = require('ethereumjs-util') const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. -/* -tx-utils are utility methods for Transaction manager +/** +tx-gas-utils are gas utility methods for Transaction manager its passed ethquery and used to do things like calculate gas of a tx. -@param provider {object} +@param {Object} provider - A network provider. */ -module.exports = class TxGasUtil { +class TxGasUtil { constructor (provider) { this.query = new EthQuery(provider) } + /** + @param txMeta {Object} - the txMeta object + @returns {object} the txMeta object with the gas written to the txParams + */ async analyzeGasUsage (txMeta) { const block = await this.query.getBlockByNumber('latest', true) let estimatedGasHex @@ -39,6 +43,12 @@ module.exports = class TxGasUtil { return txMeta } + /** + Estimates the tx's gas usage + @param txMeta {Object} - the txMeta object + @param blockGasLimitHex {string} - hex string of the block's gas limit + @returns {string} the estimated gas limit as a hex string + */ async estimateTxGas (txMeta, blockGasLimitHex) { const txParams = txMeta.txParams @@ -71,6 +81,12 @@ module.exports = class TxGasUtil { return await this.query.estimateGas(txParams) } + /** + Writes the gas on the txParams in the txMeta + @param txMeta {Object} - the txMeta object to write to + @param blockGasLimitHex {string} - the block gas limit hex + @param estimatedGasHex {string} - the estimated gas hex + */ setTxGas (txMeta, blockGasLimitHex, estimatedGasHex) { txMeta.estimatedGas = addHexPrefix(estimatedGasHex) const txParams = txMeta.txParams @@ -88,6 +104,13 @@ module.exports = class TxGasUtil { return } + /** + Adds a gas buffer with out exceeding the block gas limit + + @param initialGasLimitHex {string} - the initial gas limit to add the buffer too + @param blockGasLimitHex {string} - the block gas limit + @returns {string} the buffered gas limit as a hex string + */ addGasBuffer (initialGasLimitHex, blockGasLimitHex) { const initialGasLimitBn = hexToBn(initialGasLimitHex) const blockGasLimitBn = hexToBn(blockGasLimitHex) @@ -102,3 +125,5 @@ module.exports = class TxGasUtil { return bnToHex(upperGasLimitBn) } } + +module.exports = TxGasUtil \ No newline at end of file diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 328024925..53428c333 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -20,11 +20,11 @@ const { getFinalStates } = require('./lib/util')
- `'confirmed'` the tx has been included in a block.
- `'failed'` the tx failed for some reason, included on tx data.
- `'dropped'` the tx nonce was already used - @param opts {object} - - @property {object} opts.initState with the key transaction {array} - @property {number} opts.txHistoryLimit limit for how many finished + @param opts {object} + @param {object} [opts.initState={ transactions: [] }] initial transactions list with the key transaction {array} + @param {number} [opts.txHistoryLimit] limit for how many finished transactions can hang around in state - @property {function} opts.getNetwork return network number + @param {function} opts.getNetwork return network number @class */ class TransactionStateManager extends EventEmitter { @@ -81,8 +81,9 @@ class TransactionStateManager extends EventEmitter { } /** - @param address {string} - hex prefixed address to sort the txMetas for [optional] - @returns {array} the tx list whos status is submitted + @param [address] {string} - hex prefixed address to sort the txMetas for [optional] + @returns {array} the tx list whos status is submitted if no address is provide + returns all txMetas who's status is submitted for the current network */ getPendingTransactions (address) { const opts = { status: 'submitted' } @@ -91,8 +92,9 @@ class TransactionStateManager extends EventEmitter { } /** - @param address {string} - hex prefixed address to sort the txMetas for [optional] - @returns {array} the tx list whos status is confirmed + @param [address] {string} - hex prefixed address to sort the txMetas for [optional] + @returns {array} the tx list whos status is confirmed if no address is provide + returns all txMetas who's status is confirmed for the current network */ getConfirmedTransactions (address) { const opts = { status: 'confirmed' } @@ -106,7 +108,7 @@ class TransactionStateManager extends EventEmitter { is in its final state it will allso add the key `history` to the txMeta with the snap shot of the original object - @param txMeta {object} + @param txMeta {Object} @returns {object} the txMeta */ addTx (txMeta) { @@ -155,8 +157,8 @@ class TransactionStateManager extends EventEmitter { /** updates the txMeta in the list and adds a history entry - @param txMeta {object} - the txMeta to update - @param note {string} - a not about the update for history + @param txMeta {Object} - the txMeta to update + @param [note] {string} - a not about the update for history */ updateTx (txMeta, note) { // validate txParams @@ -225,6 +227,7 @@ class TransactionStateManager extends EventEmitter { status: 'signed',
err: undefined,
}
+ @param [initialList=this.getTxList()] @returns a {array} of txMeta with all options matching */ @@ -253,7 +256,7 @@ class TransactionStateManager extends EventEmitter { @param key {string} - the key to check @param value - the value your looking for - @param txList {array} - [optional] the list to search. default is the txList + @param [txList=this.getTxList()] {array} - the list to search. default is the txList from txStateManager#getTxList @returns {array} a list of txMetas who matches the search params */ From 1c63b11e3f338d81a19385274edfa52b9e7e6983 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 25 Apr 2018 14:02:41 -0700 Subject: [PATCH 44/79] rpc - bump provider-engine + eth-json-rpc-filters --- app/scripts/controllers/network/network.js | 2 +- package-lock.json | 212 +++++++++++++++++---- package.json | 4 +- 3 files changed, 176 insertions(+), 42 deletions(-) diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 6fd983bb2..2f5b81cd2 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -1,7 +1,7 @@ const assert = require('assert') const EventEmitter = require('events') const createMetamaskProvider = require('web3-provider-engine/zero.js') -const SubproviderFromProvider = require('web3-provider-engine/subproviders/web3.js') +const SubproviderFromProvider = require('web3-provider-engine/subproviders/provider.js') const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') diff --git a/package-lock.json b/package-lock.json index 4b6fb2296..5b5b82450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2084,6 +2084,14 @@ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", "dev": true }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2.3" + } + }, "bail": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", @@ -3904,6 +3912,22 @@ "is-windows": "1.0.2" } }, + "cross-fetch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.1.0.tgz", + "integrity": "sha512-FTIt2WK44RiafWQ62xIvd+oBoVd392abh1lF872trLlA74JCR1s4oTHlixwoIKy44ehn8WbQ0Ds2P16sw7ZQxg==", + "requires": { + "node-fetch": "2.1.1", + "whatwg-fetch": "2.0.3" + }, + "dependencies": { + "node-fetch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.1.tgz", + "integrity": "sha1-NpynC4L1DIZJYQSmx3bSdPTkotQ=" + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -5832,37 +5856,13 @@ } }, "eth-json-rpc-filters": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-1.2.5.tgz", - "integrity": "sha512-MUcUzwDy0cPso1BEnY5T5DtlI8r8gA7lNqn+BOFzDX1Ll6C8mCPz8pzSKrTkbKV9KTfgQYJ/M4vJbR83TqhROg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-1.2.6.tgz", + "integrity": "sha512-6G9t43s3lxJckeSfNduc3Ww/40BGm1Cf8MU1nL8rrumZbEg44ZSexWUowB00D4kJ9qSOH+CbzdI+m3oVMi4xFw==", "requires": { "await-semaphore": "0.1.3", - "eth-json-rpc-middleware": "1.5.0", - "json-rpc-engine": "3.4.0", + "json-rpc-engine": "3.6.1", "lodash.flatmap": "4.5.0" - }, - "dependencies": { - "babelify": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", - "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", - "requires": { - "babel-core": "6.26.0", - "object-assign": "4.1.1" - } - }, - "json-rpc-engine": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-3.4.0.tgz", - "integrity": "sha1-ihZHp/LMcBj0gC9B7III0oH3i/w=", - "requires": { - "async": "2.6.0", - "babel-preset-env": "1.6.1", - "babelify": "7.3.0", - "json-rpc-error": "2.0.0", - "promise-to-callback": "1.0.0" - } - } } }, "eth-json-rpc-infura": { @@ -6242,6 +6242,50 @@ "through2": "2.0.3", "treeify": "1.1.0", "web3-provider-engine": "13.8.0" + }, + "dependencies": { + "web3-provider-engine": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz", + "integrity": "sha512-fZXhX5VWwWpoFfrfocslyg6P7cN3YWPG/ASaevNfeO80R+nzgoPUBXcWQekSGSsNDkeRTis4aMmpmofYf1TNtQ==", + "requires": { + "async": "2.6.0", + "clone": "2.1.1", + "eth-block-tracker": "2.3.0", + "eth-sig-util": "1.4.2", + "ethereumjs-block": "1.7.0", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "5.1.5", + "ethereumjs-vm": "2.3.2", + "fetch-ponyfill": "4.1.0", + "json-rpc-error": "2.0.0", + "json-stable-stringify": "1.0.1", + "promise-to-callback": "1.0.0", + "readable-stream": "2.3.3", + "request": "2.83.0", + "semaphore": "1.1.0", + "solc": "0.4.23", + "tape": "4.8.0", + "xhr": "2.4.1", + "xtend": "4.0.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", + "integrity": "sha512-xPaSEATYJpMTCGowIt0oMZwFP4R1bxd6QsWgkcDvFL0JtXsr39p32WEcD14RscCjfP41YXZPCVWA4yAg0nrJmw==", + "requires": { + "bn.js": "4.11.8", + "create-hash": "1.1.3", + "ethjs-util": "0.1.4", + "keccak": "1.4.0", + "rlp": "2.0.0", + "safe-buffer": "5.1.1", + "secp256k1": "3.4.0" + } + } + } + } } }, "ethereum-common": { @@ -9119,6 +9163,32 @@ "web3-utils": "1.0.0-beta.34" } }, + "web3-provider-engine": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz", + "integrity": "sha512-fZXhX5VWwWpoFfrfocslyg6P7cN3YWPG/ASaevNfeO80R+nzgoPUBXcWQekSGSsNDkeRTis4aMmpmofYf1TNtQ==", + "requires": { + "async": "2.6.0", + "clone": "2.1.1", + "eth-block-tracker": "2.3.0", + "eth-sig-util": "1.4.2", + "ethereumjs-block": "1.2.2", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "5.1.5", + "ethereumjs-vm": "2.3.3", + "fetch-ponyfill": "4.1.0", + "json-rpc-error": "2.0.0", + "json-stable-stringify": "1.0.1", + "promise-to-callback": "1.0.0", + "readable-stream": "2.3.3", + "request": "2.83.0", + "semaphore": "1.1.0", + "solc": "0.4.18", + "tape": "4.8.0", + "xhr": "2.4.1", + "xtend": "4.0.1" + } + }, "yargs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", @@ -20049,6 +20119,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -22585,9 +22660,9 @@ } }, "solc": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.21.tgz", - "integrity": "sha512-8lJmimVjOG9AJOQRWS2ph4rSctPMsPGZ4H360HLs5iI+euUlt7iAvUxSLeFZZzwk0kas4Qta7HmlMXNU3yYwhw==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.23.tgz", + "integrity": "sha512-AT7anLHY6uIRg2It6N0UlCHeZ7YeecIkUhnlirrCgCPCUevtnoN48BxvgigN/4jJTRljv5oFhAJtI6gvHzT5DQ==", "requires": { "fs-extra": "0.30.0", "memorystream": "0.3.1", @@ -24928,7 +25003,7 @@ "optional": true }, "uglifyify": { - "version": "github:danfinlay/uglifyify#8662585e39125a96a5379d71cb4a606829790f87", + "version": "github:MetaMask/uglifyify#8662585e39125a96a5379d71cb4a606829790f87", "dev": true, "requires": { "convert-source-map": "1.1.3", @@ -26195,31 +26270,59 @@ } }, "web3-provider-engine": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz", - "integrity": "sha512-fZXhX5VWwWpoFfrfocslyg6P7cN3YWPG/ASaevNfeO80R+nzgoPUBXcWQekSGSsNDkeRTis4aMmpmofYf1TNtQ==", + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-14.0.5.tgz", + "integrity": "sha512-1W/ue7VOwOMnmKgMY3HCpbixi6qhfl4r1dK8W597AwJLbrQ+twJKwWlFAedDpJjCc9MwRCCB3pyexW4HJVSiBg==", "requires": { "async": "2.6.0", + "backoff": "2.5.0", "clone": "2.1.1", - "eth-block-tracker": "2.3.0", + "cross-fetch": "2.1.0", + "eth-block-tracker": "3.0.0", + "eth-json-rpc-infura": "3.1.0", "eth-sig-util": "1.4.2", "ethereumjs-block": "1.7.0", "ethereumjs-tx": "1.3.3", "ethereumjs-util": "5.1.5", - "ethereumjs-vm": "2.3.2", - "fetch-ponyfill": "4.1.0", + "ethereumjs-vm": "2.3.4", "json-rpc-error": "2.0.0", "json-stable-stringify": "1.0.1", "promise-to-callback": "1.0.0", "readable-stream": "2.3.3", "request": "2.83.0", "semaphore": "1.1.0", - "solc": "0.4.21", "tape": "4.8.0", + "ws": "5.1.1", "xhr": "2.4.1", "xtend": "4.0.1" }, "dependencies": { + "eth-block-tracker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-3.0.0.tgz", + "integrity": "sha512-Lhhu/+1GOeekMRDRhUcM7VSJRmX279DByrwzEbmG0JL1tcT3xRo6GLNXnidyJ7ahHJm+0JFhw/RqtTeIxagQwA==", + "requires": { + "eth-query": "2.1.2", + "ethereumjs-tx": "1.3.3", + "ethereumjs-util": "5.1.5", + "ethjs-util": "0.1.4", + "json-rpc-engine": "3.6.1", + "pify": "2.3.0", + "tape": "4.8.0" + } + }, + "eth-json-rpc-infura": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-3.1.0.tgz", + "integrity": "sha512-uMYkEP6fga8CyNo8TMoA/7cxi6bL3V8pTvjKQikOi9iYl6/AO5xlfgniyAMElSiq2mmXz3lYa/9VYDMzt/J5aA==", + "requires": { + "cross-fetch": "2.1.0", + "eth-json-rpc-middleware": "1.5.0", + "json-rpc-engine": "3.6.1", + "json-rpc-error": "2.0.0", + "tape": "4.8.0" + } + }, "ethereumjs-util": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", @@ -26233,6 +26336,37 @@ "safe-buffer": "5.1.1", "secp256k1": "3.4.0" } + }, + "ethereumjs-vm": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.3.4.tgz", + "integrity": "sha512-Y4SlzNDqxrCO58jhp98HdnZVdjOqB+HC0hoU+N/DEp1aU+hFkRX/nru5F7/HkQRPIlA6aJlQp/xIA6xZs1kspw==", + "requires": { + "async": "2.6.0", + "async-eventemitter": "0.2.4", + "ethereum-common": "0.2.0", + "ethereumjs-account": "2.0.4", + "ethereumjs-block": "1.7.0", + "ethereumjs-util": "5.1.5", + "fake-merkle-patricia-tree": "1.0.1", + "functional-red-black-tree": "1.0.1", + "merkle-patricia-tree": "2.3.0", + "rustbn.js": "0.1.1", + "safe-buffer": "5.1.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "ws": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.1.1.tgz", + "integrity": "sha512-bOusvpCb09TOBLbpMKszd45WKC2KPtxiyiHanv+H2DE3Az+1db5a/L7sVJZVDPUC1Br8f0SKRr1KjLpD1U/IAw==", + "requires": { + "async-limiter": "1.0.0" + } } } }, diff --git a/package.json b/package.json index f47b232b7..73892bc28 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "eth-block-tracker": "^2.3.0", "eth-contract-metadata": "^1.1.5", "eth-hd-keyring": "^1.2.1", - "eth-json-rpc-filters": "^1.2.5", + "eth-json-rpc-filters": "^1.2.6", "eth-json-rpc-infura": "^3.0.0", "eth-keyring-controller": "^2.2.0", "eth-phishing-detect": "^1.1.4", @@ -185,7 +185,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.8.0", + "web3-provider-engine": "^14.0.5", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, From 03517f79655a8991ecb2b4f19994369fade3b8b5 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 25 Apr 2018 14:17:20 -0700 Subject: [PATCH 45/79] Updated notices json file. --- notices/notices.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notices/notices.json b/notices/notices.json index ddadea1b0..68201581d 100644 --- a/notices/notices.json +++ b/notices/notices.json @@ -1 +1 @@ -[{"read":false,"date":"Thu Feb 09 2017","title":"Terms of Use","body":"# Terms of Use #\n\n**THIS AGREEMENT IS SUBJECT TO BINDING ARBITRATION AND A WAIVER OF CLASS ACTION RIGHTS AS DETAILED IN SECTION 13. PLEASE READ THE AGREEMENT CAREFULLY.**\n\n_Our Terms of Use have been updated as of September 5, 2016_\n\n## 1. Acceptance of Terms ##\n\nMetaMask provides a platform for managing Ethereum (or \"ETH\") accounts, and allowing ordinary websites to interact with the Ethereum blockchain, while keeping the user in control over what transactions they approve, through our website located at[ ](http://metamask.io)[https://metamask.io/](https://metamask.io/) and browser plugin (the \"Site\") — which includes text, images, audio, code and other materials (collectively, the “Content”) and all of the features, and services provided. The Site, and any other features, tools, materials, or other services offered from time to time by MetaMask are referred to here as the “Service.” Please read these Terms of Use (the “Terms” or “Terms of Use”) carefully before using the Service. By using or otherwise accessing the Services, or clicking to accept or agree to these Terms where that option is made available, you (1) accept and agree to these Terms (2) consent to the collection, use, disclosure and other handling of information as described in our Privacy Policy and (3) any additional terms, rules and conditions of participation issued by MetaMask from time to time. If you do not agree to the Terms, then you may not access or use the Content or Services.\n\n## 2. Modification of Terms of Use ##\n\nExcept for Section 13, providing for binding arbitration and waiver of class action rights, MetaMask reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. The most current version of these Terms will be posted on our Site. You shall be responsible for reviewing and becoming familiar with any such modifications. Use of the Services by you after any modification to the Terms constitutes your acceptance of the Terms of Use as modified.\n\n\n\n## 3. Eligibility ##\n\nYou hereby represent and warrant that you are fully able and competent to enter into the terms, conditions, obligations, affirmations, representations and warranties set forth in these Terms and to abide by and comply with these Terms.\n\nMetaMask is a global platform and by accessing the Content or Services, you are representing and warranting that, you are of the legal age of majority in your jurisdiction as is required to access such Services and Content and enter into arrangements as provided by the Service. You further represent that you are otherwise legally permitted to use the service in your jurisdiction including owning cryptographic tokens of value, and interacting with the Services or Content in any way. You further represent you are responsible for ensuring compliance with the laws of your jurisdiction and acknowledge that MetaMask is not liable for your compliance with such laws.\n\n## 4 Account Password and Security ##\n\nWhen setting up an account within MetaMask, you will be responsible for keeping your own account secrets, which may be a twelve-word seed phrase, an account file, or other locally stored secret information. MetaMask encrypts this information locally with a password you provide, that we never send to our servers. You agree to (a) never use the same password for MetaMask that you have ever used outside of this service; (b) keep your secret information and password confidential and do not share them with anyone else; (c) immediately notify MetaMask of any unauthorized use of your account or breach of security. MetaMask cannot and will not be liable for any loss or damage arising from your failure to comply with this section.\n\n## 5. Representations, Warranties, and Risks ##\n\n### 5.1. Warranty Disclaimer ###\n\nYou expressly understand and agree that your use of the Service is at your sole risk. The Service (including the Service and the Content) are provided on an \"AS IS\" and \"as available\" basis, without warranties of any kind, either express or implied, including, without limitation, implied warranties of merchantability, fitness for a particular purpose or non-infringement. You acknowledge that MetaMask has no control over, and no duty to take any action regarding: which users gain access to or use the Service; what effects the Content may have on you; how you may interpret or use the Content; or what actions you may take as a result of having been exposed to the Content. You release MetaMask from all liability for you having acquired or not acquired Content through the Service. MetaMask makes no representations concerning any Content contained in or accessed through the Service, and MetaMask will not be responsible or liable for the accuracy, copyright compliance, legality or decency of material contained in or accessed through the Service.\n\n### 5.2 Sophistication and Risk of Cryptographic Systems ###\n\nBy utilizing the Service or interacting with the Content or platform in any way, you represent that you understand the inherent risks associated with cryptographic systems; and warrant that you have an understanding of the usage and intricacies of native cryptographic tokens, like Ether (ETH) and Bitcoin (BTC), smart contract based tokens such as those that follow the Ethereum Token Standard (https://github.com/ethereum/EIPs/issues/20), and blockchain-based software systems.\n\n### 5.3 Risk of Regulatory Actions in One or More Jurisdictions ###\n\nMetaMask and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of MetaMask to continue to develop, or which could impede or limit your ability to access or use the Service or Ethereum blockchain.\n\n### 5.4 Risk of Weaknesses or Exploits in the Field of Cryptography ###\n\nYou acknowledge and understand that Cryptography is a progressing field. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and Services of Content, which could result in the theft or loss of your cryptographic tokens or property. To the extent possible, MetaMask intends to update the protocol underlying Services to account for any advances in cryptography and to incorporate additional security measures, but does not guarantee or otherwise represent full security of the system. By using the Service or accessing Content, you acknowledge these inherent risks.\n\n### 5.5 Volatility of Crypto Currencies ###\n\nYou understand that Ethereum and other blockchain technologies and associated currencies or tokens are highly volatile due to many factors including but not limited to adoption, speculation, technology and security risks. You also acknowledge that the cost of transacting on such technologies is variable and may increase at any time causing impact to any activities taking place on the Ethereum blockchain. You acknowledge these risks and represent that MetaMask cannot be held liable for such fluctuations or increased costs.\n\n### 5.6 Application Security ###\n\nYou acknowledge that Ethereum applications are code subject to flaws and acknowledge that you are solely responsible for evaluating any code provided by the Services or Content and the trustworthiness of any third-party websites, products, smart-contracts, or Content you access or use through the Service. You further expressly acknowledge and represent that Ethereum applications can be written maliciously or negligently, that MetaMask cannot be held liable for your interaction with such applications and that such applications may cause the loss of property or even identity. This warning and others later provided by MetaMask in no way evidence or represent an on-going duty to alert you to all of the potential risks of utilizing the Service or Content.\n\n## 6. Indemnity ##\n\nYou agree to release and to indemnify, defend and hold harmless MetaMask and its parents, subsidiaries, affiliates and agencies, as well as the officers, directors, employees, shareholders and representatives of any of the foregoing entities, from and against any and all losses, liabilities, expenses, damages, costs (including attorneys’ fees and court costs) claims or actions of any kind whatsoever arising or resulting from your use of the Service, your violation of these Terms of Use, and any of your acts or omissions that implicate publicity rights, defamation or invasion of privacy. MetaMask reserves the right, at its own expense, to assume exclusive defense and control of any matter otherwise subject to indemnification by you and, in such case, you agree to cooperate with MetaMask in the defense of such matter.\n\n## 7. Limitation on liability ##\n\nYOU ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE SITE AND SERVICE. YOU ACKNOWLEDGE AND AGREE THAT ANY INFORMATION YOU SEND OR RECEIVE DURING YOUR USE OF THE SITE AND SERVICE MAY NOT BE SECURE AND MAY BE INTERCEPTED OR LATER ACQUIRED BY UNAUTHORIZED PARTIES. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OF THE SITE AND SERVICE IS AT YOUR OWN RISK. RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER METAMASK NOR ITS SUPPLIERS OR LICENSORS WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF METAMASK HAD BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE OR SERVICE; THE USE OR THE INABILITY TO USE THE SITE OR SERVICE; UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA; STATEMENTS OR CONDUCT OF ANY THIRD PARTY ON THE SITE OR SERVICE; ANY ACTIONS WE TAKE OR FAIL TO TAKE AS A RESULT OF COMMUNICATIONS YOU SEND TO US; HUMAN ERRORS; TECHNICAL MALFUNCTIONS; FAILURES, INCLUDING PUBLIC UTILITY OR TELEPHONE OUTAGES; OMISSIONS, INTERRUPTIONS, LATENCY, DELETIONS OR DEFECTS OF ANY DEVICE OR NETWORK, PROVIDERS, OR SOFTWARE (INCLUDING, BUT NOT LIMITED TO, THOSE THAT DO NOT PERMIT PARTICIPATION IN THE SERVICE); ANY INJURY OR DAMAGE TO COMPUTER EQUIPMENT; INABILITY TO FULLY ACCESS THE SITE OR SERVICE OR ANY OTHER WEBSITE; THEFT, TAMPERING, DESTRUCTION, OR UNAUTHORIZED ACCESS TO, IMAGES OR OTHER CONTENT OF ANY KIND; DATA THAT IS PROCESSED LATE OR INCORRECTLY OR IS INCOMPLETE OR LOST; TYPOGRAPHICAL, PRINTING OR OTHER ERRORS, OR ANY COMBINATION THEREOF; OR ANY OTHER MATTER RELATING TO THE SITE OR SERVICE.\n\nSOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.\n\n## 8. Our Proprietary Rights ##\n\nAll title, ownership and intellectual property rights in and to the Service are owned by MetaMask or its licensors. You acknowledge and agree that the Service contains proprietary and confidential information that is protected by applicable intellectual property and other laws. Except as expressly authorized by MetaMask, you agree not to copy, modify, rent, lease, loan, sell, distribute, perform, display or create derivative works based on the Service, in whole or in part. MetaMask issues a license for MetaMask, found [here](https://github.com/MetaMask/metamask-plugin/blob/master/LICENSE). For information on other licenses utilized in the development of MetaMask, please see our attribution page at: [https://metamask.io/attributions.html](https://metamask.io/attributions.html)\n\n## 9. Links ##\n\nThe Service provides, or third parties may provide, links to other World Wide Web or accessible sites, applications or resources. Because MetaMask has no control over such sites, applications and resources, you acknowledge and agree that MetaMask is not responsible for the availability of such external sites, applications or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that MetaMask shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such site or resource.\n\n## 10. Termination and Suspension ##\n\nMetaMask may terminate or suspend all or part of the Service and your MetaMask access immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms. Upon termination of your access, your right to use the Service will immediately cease.\n\nThe following provisions of the Terms survive any termination of these Terms: INDEMNITY; WARRANTY DISCLAIMERS; LIMITATION ON LIABILITY; OUR PROPRIETARY RIGHTS; LINKS; TERMINATION; NO THIRD PARTY BENEFICIARIES; BINDING ARBITRATION AND CLASS ACTION WAIVER; GENERAL INFORMATION.\n\n## 11. No Third Party Beneficiaries ##\n\nYou agree that, except as otherwise expressly provided in these Terms, there shall be no third party beneficiaries to the Terms.\n\n## 12. Notice and Procedure For Making Claims of Copyright Infringement ##\n\nIf you believe that your copyright or the copyright of a person on whose behalf you are authorized to act has been infringed, please provide MetaMask’s Copyright Agent a written Notice containing the following information:\n\n· an electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other intellectual property interest;\n\n· a description of the copyrighted work or other intellectual property that you claim has been infringed;\n\n· a description of where the material that you claim is infringing is located on the Service;\n\n· your address, telephone number, and email address;\n\n· a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law;\n\n· a statement by you, made under penalty of perjury, that the above information in your Notice is accurate and that you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf.\n\nMetaMask’s Copyright Agent can be reached at:\n\nEmail: copyright [at] metamask [dot] io\n\nMail:\n\nAttention:\n\nMetaMask Copyright ℅ ConsenSys\n\n49 Bogart Street\n\nBrooklyn, NY 11206\n\n## 13. Binding Arbitration and Class Action Waiver ##\n\nPLEASE READ THIS SECTION CAREFULLY – IT MAY SIGNIFICANTLY AFFECT YOUR LEGAL RIGHTS, INCLUDING YOUR RIGHT TO FILE A LAWSUIT IN COURT\n\n### 13.1 Initial Dispute Resolution ###\n\nThe parties shall use their best efforts to engage directly to settle any dispute, claim, question, or disagreement and engage in good faith negotiations which shall be a condition to either party initiating a lawsuit or arbitration.\n\n### 13.2 Binding Arbitration ###\n\nIf the parties do not reach an agreed upon solution within a period of 30 days from the time informal dispute resolution under the Initial Dispute Resolution provision begins, then either party may initiate binding arbitration as the sole means to resolve claims, subject to the terms set forth below. Specifically, all claims arising out of or relating to these Terms (including their formation, performance and breach), the parties’ relationship with each other and/or your use of the Service shall be finally settled by binding arbitration administered by the American Arbitration Association in accordance with the provisions of its Commercial Arbitration Rules and the supplementary procedures for consumer related disputes of the American Arbitration Association (the \"AAA\"), excluding any rules or procedures governing or permitting class actions.\n\nThe arbitrator, and not any federal, state or local court or agency, shall have exclusive authority to resolve all disputes arising out of or relating to the interpretation, applicability, enforceability or formation of these Terms, including, but not limited to any claim that all or any part of these Terms are void or voidable, or whether a claim is subject to arbitration. The arbitrator shall be empowered to grant whatever relief would be available in a court under law or in equity. The arbitrator’s award shall be written, and binding on the parties and may be entered as a judgment in any court of competent jurisdiction.\n\nThe parties understand that, absent this mandatory provision, they would have the right to sue in court and have a jury trial. They further understand that, in some instances, the costs of arbitration could exceed the costs of litigation and the right to discovery may be more limited in arbitration than in court.\n\n### 13.3 Location ###\n\nBinding arbitration shall take place in New York. You agree to submit to the personal jurisdiction of any federal or state court in New York County, New York, in order to compel arbitration, to stay proceedings pending arbitration, or to confirm, modify, vacate or enter judgment on the award entered by the arbitrator.\n\n### 13.4 Class Action Waiver ###\n\nThe parties further agree that any arbitration shall be conducted in their individual capacities only and not as a class action or other representative action, and the parties expressly waive their right to file a class action or seek relief on a class basis. YOU AND METAMASK AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. If any court or arbitrator determines that the class action waiver set forth in this paragraph is void or unenforceable for any reason or that an arbitration can proceed on a class basis, then the arbitration provision set forth above shall be deemed null and void in its entirety and the parties shall be deemed to have not agreed to arbitrate disputes.\n\n### 13.5 Exception - Litigation of Intellectual Property and Small Claims Court Claims ###\n\nNotwithstanding the parties' decision to resolve all disputes through arbitration, either party may bring an action in state or federal court to protect its intellectual property rights (\"intellectual property rights\" means patents, copyrights, moral rights, trademarks, and trade secrets, but not privacy or publicity rights). Either party may also seek relief in a small claims court for disputes or claims within the scope of that court’s jurisdiction.\n\n### 13.6 30-Day Right to Opt Out ###\n\nYou have the right to opt-out and not be bound by the arbitration and class action waiver provisions set forth above by sending written notice of your decision to opt-out to the following address: MetaMask ℅ ConsenSys, 49 Bogart Street, Brooklyn NY 11206 and via email at legal-opt@metamask.io. The notice must be sent within 30 days of September 6, 2016 or your first use of the Service, whichever is later, otherwise you shall be bound to arbitrate disputes in accordance with the terms of those paragraphs. If you opt-out of these arbitration provisions, MetaMask also will not be bound by them.\n\n### 13.7 Changes to This Section ###\n\nMetaMask will provide 60-days’ notice of any changes to this section. Changes will become effective on the 60th day, and will apply prospectively only to any claims arising after the 60th day.\n\nFor any dispute not subject to arbitration you and MetaMask agree to submit to the personal and exclusive jurisdiction of and venue in the federal and state courts located in New York, New York. You further agree to accept service of process by mail, and hereby waive any and all jurisdictional and venue defenses otherwise available.\n\nThe Terms and the relationship between you and MetaMask shall be governed by the laws of the State of New York without regard to conflict of law provisions.\n\n## 14. General Information ##\n\n### 14.1 Entire Agreement ###\n\nThese Terms (and any additional terms, rules and conditions of participation that MetaMask may post on the Service) constitute the entire agreement between you and MetaMask with respect to the Service and supersedes any prior agreements, oral or written, between you and MetaMask. In the event of a conflict between these Terms and the additional terms, rules and conditions of participation, the latter will prevail over the Terms to the extent of the conflict.\n\n### 14.2 Waiver and Severability of Terms ###\n\nThe failure of MetaMask to exercise or enforce any right or provision of the Terms shall not constitute a waiver of such right or provision. If any provision of the Terms is found by an arbitrator or court of competent jurisdiction to be invalid, the parties nevertheless agree that the arbitrator or court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of the Terms remain in full force and effect.\n\n### 14.3 Statute of Limitations ###\n\nYou agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to the use of the Service or the Terms must be filed within one (1) year after such claim or cause of action arose or be forever barred.\n\n### 14.4 Section Titles ###\n\nThe section titles in the Terms are for convenience only and have no legal or contractual effect.\n\n### 14.5 Communications ###\n\nUsers with questions, complaints or claims with respect to the Service may contact us using the relevant contact information set forth above and at communications@metamask.io.\n\n## 15 Related Links ##\n\n**[Terms of Use](https://metamask.io/terms.html)**\n\n**[Privacy](https://metamask.io/privacy.html)**\n\n**[Attributions](https://metamask.io/attributions.html)**\n\n","id":0},{"read":false,"date":"Mon May 08 2017","title":"Privacy Notice","body":"MetaMask is beta software. \n\nWhen you log in to MetaMask, your current account is visible to every new site you visit.\n\nFor your privacy, for now, please sign out of MetaMask when you're done using a site.\n\n","id":2},{"read":false,"date":"Tue Nov 28 2017","title":"Seed Phrase Alert","firstVersion":"<=3.12.0","body":"Please take a moment to [back up your seed phrase again](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up).\n\nMetaMask has become aware of a previous issue where a very small number of users were shown the wrong seed phrase to back up. The only way to protect yourself from this issue, is to back up your seed phrase again now.\n\nYou can follow the guide at this link:\n\n[https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up)\n\nWe have fixed the known issue, but will be issuing ongoing bug bounties to help prevent this kind of problem in the future.\n\nFor more information on this issue, [see this blog post](https://medium.com/metamask/seed-phrase-issue-bounty-awarded-e1986e811021)","id":3}] +[{"read":false,"date":"Thu Feb 09 2017","title":"Terms of Use","body":"# Terms of Use #\n\n**THIS AGREEMENT IS SUBJECT TO BINDING ARBITRATION AND A WAIVER OF CLASS ACTION RIGHTS AS DETAILED IN SECTION 13. PLEASE READ THE AGREEMENT CAREFULLY.**\n\n_Our Terms of Use have been updated as of September 5, 2016_\n\n## 1. Acceptance of Terms ##\n\nMetaMask provides a platform for managing Ethereum (or \"ETH\") accounts, and allowing ordinary websites to interact with the Ethereum blockchain, while keeping the user in control over what transactions they approve, through our website located at[ ](http://metamask.io)[https://metamask.io/](https://metamask.io/) and browser plugin (the \"Site\") — which includes text, images, audio, code and other materials (collectively, the “Content”) and all of the features, and services provided. The Site, and any other features, tools, materials, or other services offered from time to time by MetaMask are referred to here as the “Service.” Please read these Terms of Use (the “Terms” or “Terms of Use”) carefully before using the Service. By using or otherwise accessing the Services, or clicking to accept or agree to these Terms where that option is made available, you (1) accept and agree to these Terms (2) consent to the collection, use, disclosure and other handling of information as described in our Privacy Policy and (3) any additional terms, rules and conditions of participation issued by MetaMask from time to time. If you do not agree to the Terms, then you may not access or use the Content or Services.\n\n## 2. Modification of Terms of Use ##\n\nExcept for Section 13, providing for binding arbitration and waiver of class action rights, MetaMask reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. The most current version of these Terms will be posted on our Site. You shall be responsible for reviewing and becoming familiar with any such modifications. Use of the Services by you after any modification to the Terms constitutes your acceptance of the Terms of Use as modified.\n\n\n\n## 3. Eligibility ##\n\nYou hereby represent and warrant that you are fully able and competent to enter into the terms, conditions, obligations, affirmations, representations and warranties set forth in these Terms and to abide by and comply with these Terms.\n\nMetaMask is a global platform and by accessing the Content or Services, you are representing and warranting that, you are of the legal age of majority in your jurisdiction as is required to access such Services and Content and enter into arrangements as provided by the Service. You further represent that you are otherwise legally permitted to use the service in your jurisdiction including owning cryptographic tokens of value, and interacting with the Services or Content in any way. You further represent you are responsible for ensuring compliance with the laws of your jurisdiction and acknowledge that MetaMask is not liable for your compliance with such laws.\n\n## 4 Account Password and Security ##\n\nWhen setting up an account within MetaMask, you will be responsible for keeping your own account secrets, which may be a twelve-word seed phrase, an account file, or other locally stored secret information. MetaMask encrypts this information locally with a password you provide, that we never send to our servers. You agree to (a) never use the same password for MetaMask that you have ever used outside of this service; (b) keep your secret information and password confidential and do not share them with anyone else; (c) immediately notify MetaMask of any unauthorized use of your account or breach of security. MetaMask cannot and will not be liable for any loss or damage arising from your failure to comply with this section.\n\n## 5. Representations, Warranties, and Risks ##\n\n### 5.1. Warranty Disclaimer ###\n\nYou expressly understand and agree that your use of the Service is at your sole risk. The Service (including the Service and the Content) are provided on an \"AS IS\" and \"as available\" basis, without warranties of any kind, either express or implied, including, without limitation, implied warranties of merchantability, fitness for a particular purpose or non-infringement. You acknowledge that MetaMask has no control over, and no duty to take any action regarding: which users gain access to or use the Service; what effects the Content may have on you; how you may interpret or use the Content; or what actions you may take as a result of having been exposed to the Content. You release MetaMask from all liability for you having acquired or not acquired Content through the Service. MetaMask makes no representations concerning any Content contained in or accessed through the Service, and MetaMask will not be responsible or liable for the accuracy, copyright compliance, legality or decency of material contained in or accessed through the Service.\n\n### 5.2 Sophistication and Risk of Cryptographic Systems ###\n\nBy utilizing the Service or interacting with the Content or platform in any way, you represent that you understand the inherent risks associated with cryptographic systems; and warrant that you have an understanding of the usage and intricacies of native cryptographic tokens, like Ether (ETH) and Bitcoin (BTC), smart contract based tokens such as those that follow the Ethereum Token Standard (https://github.com/ethereum/EIPs/issues/20), and blockchain-based software systems.\n\n### 5.3 Risk of Regulatory Actions in One or More Jurisdictions ###\n\nMetaMask and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of MetaMask to continue to develop, or which could impede or limit your ability to access or use the Service or Ethereum blockchain.\n\n### 5.4 Risk of Weaknesses or Exploits in the Field of Cryptography ###\n\nYou acknowledge and understand that Cryptography is a progressing field. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and Services of Content, which could result in the theft or loss of your cryptographic tokens or property. To the extent possible, MetaMask intends to update the protocol underlying Services to account for any advances in cryptography and to incorporate additional security measures, but does not guarantee or otherwise represent full security of the system. By using the Service or accessing Content, you acknowledge these inherent risks.\n\n### 5.5 Volatility of Crypto Currencies ###\n\nYou understand that Ethereum and other blockchain technologies and associated currencies or tokens are highly volatile due to many factors including but not limited to adoption, speculation, technology and security risks. You also acknowledge that the cost of transacting on such technologies is variable and may increase at any time causing impact to any activities taking place on the Ethereum blockchain. You acknowledge these risks and represent that MetaMask cannot be held liable for such fluctuations or increased costs.\n\n### 5.6 Application Security ###\n\nYou acknowledge that Ethereum applications are code subject to flaws and acknowledge that you are solely responsible for evaluating any code provided by the Services or Content and the trustworthiness of any third-party websites, products, smart-contracts, or Content you access or use through the Service. You further expressly acknowledge and represent that Ethereum applications can be written maliciously or negligently, that MetaMask cannot be held liable for your interaction with such applications and that such applications may cause the loss of property or even identity. This warning and others later provided by MetaMask in no way evidence or represent an on-going duty to alert you to all of the potential risks of utilizing the Service or Content.\n\n## 6. Indemnity ##\n\nYou agree to release and to indemnify, defend and hold harmless MetaMask and its parents, subsidiaries, affiliates and agencies, as well as the officers, directors, employees, shareholders and representatives of any of the foregoing entities, from and against any and all losses, liabilities, expenses, damages, costs (including attorneys’ fees and court costs) claims or actions of any kind whatsoever arising or resulting from your use of the Service, your violation of these Terms of Use, and any of your acts or omissions that implicate publicity rights, defamation or invasion of privacy. MetaMask reserves the right, at its own expense, to assume exclusive defense and control of any matter otherwise subject to indemnification by you and, in such case, you agree to cooperate with MetaMask in the defense of such matter.\n\n## 7. Limitation on liability ##\n\nYOU ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE SITE AND SERVICE. YOU ACKNOWLEDGE AND AGREE THAT ANY INFORMATION YOU SEND OR RECEIVE DURING YOUR USE OF THE SITE AND SERVICE MAY NOT BE SECURE AND MAY BE INTERCEPTED OR LATER ACQUIRED BY UNAUTHORIZED PARTIES. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OF THE SITE AND SERVICE IS AT YOUR OWN RISK. RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER METAMASK NOR ITS SUPPLIERS OR LICENSORS WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF METAMASK HAD BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE OR SERVICE; THE USE OR THE INABILITY TO USE THE SITE OR SERVICE; UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA; STATEMENTS OR CONDUCT OF ANY THIRD PARTY ON THE SITE OR SERVICE; ANY ACTIONS WE TAKE OR FAIL TO TAKE AS A RESULT OF COMMUNICATIONS YOU SEND TO US; HUMAN ERRORS; TECHNICAL MALFUNCTIONS; FAILURES, INCLUDING PUBLIC UTILITY OR TELEPHONE OUTAGES; OMISSIONS, INTERRUPTIONS, LATENCY, DELETIONS OR DEFECTS OF ANY DEVICE OR NETWORK, PROVIDERS, OR SOFTWARE (INCLUDING, BUT NOT LIMITED TO, THOSE THAT DO NOT PERMIT PARTICIPATION IN THE SERVICE); ANY INJURY OR DAMAGE TO COMPUTER EQUIPMENT; INABILITY TO FULLY ACCESS THE SITE OR SERVICE OR ANY OTHER WEBSITE; THEFT, TAMPERING, DESTRUCTION, OR UNAUTHORIZED ACCESS TO, IMAGES OR OTHER CONTENT OF ANY KIND; DATA THAT IS PROCESSED LATE OR INCORRECTLY OR IS INCOMPLETE OR LOST; TYPOGRAPHICAL, PRINTING OR OTHER ERRORS, OR ANY COMBINATION THEREOF; OR ANY OTHER MATTER RELATING TO THE SITE OR SERVICE.\n\nSOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.\n\n## 8. Our Proprietary Rights ##\n\nAll title, ownership and intellectual property rights in and to the Service are owned by MetaMask or its licensors. You acknowledge and agree that the Service contains proprietary and confidential information that is protected by applicable intellectual property and other laws. Except as expressly authorized by MetaMask, you agree not to copy, modify, rent, lease, loan, sell, distribute, perform, display or create derivative works based on the Service, in whole or in part. MetaMask issues a license for MetaMask, found [here](https://github.com/MetaMask/metamask-plugin/blob/master/LICENSE). For information on other licenses utilized in the development of MetaMask, please see our attribution page at: [https://metamask.io/attributions.html](https://metamask.io/attributions.html)\n\n## 9. Links ##\n\nThe Service provides, or third parties may provide, links to other World Wide Web or accessible sites, applications or resources. Because MetaMask has no control over such sites, applications and resources, you acknowledge and agree that MetaMask is not responsible for the availability of such external sites, applications or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that MetaMask shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such site or resource.\n\n## 10. Termination and Suspension ##\n\nMetaMask may terminate or suspend all or part of the Service and your MetaMask access immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms. Upon termination of your access, your right to use the Service will immediately cease.\n\nThe following provisions of the Terms survive any termination of these Terms: INDEMNITY; WARRANTY DISCLAIMERS; LIMITATION ON LIABILITY; OUR PROPRIETARY RIGHTS; LINKS; TERMINATION; NO THIRD PARTY BENEFICIARIES; BINDING ARBITRATION AND CLASS ACTION WAIVER; GENERAL INFORMATION.\n\n## 11. No Third Party Beneficiaries ##\n\nYou agree that, except as otherwise expressly provided in these Terms, there shall be no third party beneficiaries to the Terms.\n\n## 12. Notice and Procedure For Making Claims of Copyright Infringement ##\n\nIf you believe that your copyright or the copyright of a person on whose behalf you are authorized to act has been infringed, please provide MetaMask’s Copyright Agent a written Notice containing the following information:\n\n· an electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other intellectual property interest;\n\n· a description of the copyrighted work or other intellectual property that you claim has been infringed;\n\n· a description of where the material that you claim is infringing is located on the Service;\n\n· your address, telephone number, and email address;\n\n· a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law;\n\n· a statement by you, made under penalty of perjury, that the above information in your Notice is accurate and that you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf.\n\nMetaMask’s Copyright Agent can be reached at:\n\nEmail: copyright [at] metamask [dot] io\n\nMail:\n\nAttention:\n\nMetaMask Copyright ℅ ConsenSys\n\n49 Bogart Street\n\nBrooklyn, NY 11206\n\n## 13. Binding Arbitration and Class Action Waiver ##\n\nPLEASE READ THIS SECTION CAREFULLY – IT MAY SIGNIFICANTLY AFFECT YOUR LEGAL RIGHTS, INCLUDING YOUR RIGHT TO FILE A LAWSUIT IN COURT\n\n### 13.1 Initial Dispute Resolution ###\n\nThe parties shall use their best efforts to engage directly to settle any dispute, claim, question, or disagreement and engage in good faith negotiations which shall be a condition to either party initiating a lawsuit or arbitration.\n\n### 13.2 Binding Arbitration ###\n\nIf the parties do not reach an agreed upon solution within a period of 30 days from the time informal dispute resolution under the Initial Dispute Resolution provision begins, then either party may initiate binding arbitration as the sole means to resolve claims, subject to the terms set forth below. Specifically, all claims arising out of or relating to these Terms (including their formation, performance and breach), the parties’ relationship with each other and/or your use of the Service shall be finally settled by binding arbitration administered by the American Arbitration Association in accordance with the provisions of its Commercial Arbitration Rules and the supplementary procedures for consumer related disputes of the American Arbitration Association (the \"AAA\"), excluding any rules or procedures governing or permitting class actions.\n\nThe arbitrator, and not any federal, state or local court or agency, shall have exclusive authority to resolve all disputes arising out of or relating to the interpretation, applicability, enforceability or formation of these Terms, including, but not limited to any claim that all or any part of these Terms are void or voidable, or whether a claim is subject to arbitration. The arbitrator shall be empowered to grant whatever relief would be available in a court under law or in equity. The arbitrator’s award shall be written, and binding on the parties and may be entered as a judgment in any court of competent jurisdiction.\n\nThe parties understand that, absent this mandatory provision, they would have the right to sue in court and have a jury trial. They further understand that, in some instances, the costs of arbitration could exceed the costs of litigation and the right to discovery may be more limited in arbitration than in court.\n\n### 13.3 Location ###\n\nBinding arbitration shall take place in New York. You agree to submit to the personal jurisdiction of any federal or state court in New York County, New York, in order to compel arbitration, to stay proceedings pending arbitration, or to confirm, modify, vacate or enter judgment on the award entered by the arbitrator.\n\n### 13.4 Class Action Waiver ###\n\nThe parties further agree that any arbitration shall be conducted in their individual capacities only and not as a class action or other representative action, and the parties expressly waive their right to file a class action or seek relief on a class basis. YOU AND METAMASK AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. If any court or arbitrator determines that the class action waiver set forth in this paragraph is void or unenforceable for any reason or that an arbitration can proceed on a class basis, then the arbitration provision set forth above shall be deemed null and void in its entirety and the parties shall be deemed to have not agreed to arbitrate disputes.\n\n### 13.5 Exception - Litigation of Intellectual Property and Small Claims Court Claims ###\n\nNotwithstanding the parties' decision to resolve all disputes through arbitration, either party may bring an action in state or federal court to protect its intellectual property rights (\"intellectual property rights\" means patents, copyrights, moral rights, trademarks, and trade secrets, but not privacy or publicity rights). Either party may also seek relief in a small claims court for disputes or claims within the scope of that court’s jurisdiction.\n\n### 13.6 30-Day Right to Opt Out ###\n\nYou have the right to opt-out and not be bound by the arbitration and class action waiver provisions set forth above by sending written notice of your decision to opt-out to the following address: MetaMask ℅ ConsenSys, 49 Bogart Street, Brooklyn NY 11206 and via email at legal-opt@metamask.io. The notice must be sent within 30 days of September 6, 2016 or your first use of the Service, whichever is later, otherwise you shall be bound to arbitrate disputes in accordance with the terms of those paragraphs. If you opt-out of these arbitration provisions, MetaMask also will not be bound by them.\n\n### 13.7 Changes to This Section ###\n\nMetaMask will provide 60-days’ notice of any changes to this section. Changes will become effective on the 60th day, and will apply prospectively only to any claims arising after the 60th day.\n\nFor any dispute not subject to arbitration you and MetaMask agree to submit to the personal and exclusive jurisdiction of and venue in the federal and state courts located in New York, New York. You further agree to accept service of process by mail, and hereby waive any and all jurisdictional and venue defenses otherwise available.\n\nThe Terms and the relationship between you and MetaMask shall be governed by the laws of the State of New York without regard to conflict of law provisions.\n\n## 14. General Information ##\n\n### 14.1 Entire Agreement ###\n\nThese Terms (and any additional terms, rules and conditions of participation that MetaMask may post on the Service) constitute the entire agreement between you and MetaMask with respect to the Service and supersedes any prior agreements, oral or written, between you and MetaMask. In the event of a conflict between these Terms and the additional terms, rules and conditions of participation, the latter will prevail over the Terms to the extent of the conflict.\n\n### 14.2 Waiver and Severability of Terms ###\n\nThe failure of MetaMask to exercise or enforce any right or provision of the Terms shall not constitute a waiver of such right or provision. If any provision of the Terms is found by an arbitrator or court of competent jurisdiction to be invalid, the parties nevertheless agree that the arbitrator or court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of the Terms remain in full force and effect.\n\n### 14.3 Statute of Limitations ###\n\nYou agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to the use of the Service or the Terms must be filed within one (1) year after such claim or cause of action arose or be forever barred.\n\n### 14.4 Section Titles ###\n\nThe section titles in the Terms are for convenience only and have no legal or contractual effect.\n\n### 14.5 Communications ###\n\nUsers with questions, complaints or claims with respect to the Service may contact us using the relevant contact information set forth above and at communications@metamask.io.\n\n## 15 Related Links ##\n\n**[Terms of Use](https://metamask.io/terms.html)**\n\n**[Privacy](https://metamask.io/privacy.html)**\n\n**[Attributions](https://metamask.io/attributions.html)**\n\n","id":0},{"read":false,"date":"Mon May 08 2017","title":"Privacy Notice","body":"MetaMask is beta software. \n\nWhen you log in to MetaMask, your current account's address is visible to every new site you visit. This can be used to look up your account balances of Ether and other tokens.\n\nFor your privacy, for now, please sign out of MetaMask when you're done using a site.\n\n","id":2},{"read":false,"date":"Tue Nov 28 2017","title":"Seed Phrase Alert","firstVersion":"<=3.12.0","body":"Please take a moment to [back up your seed phrase again](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up).\n\nMetaMask has become aware of a previous issue where a very small number of users were shown the wrong seed phrase to back up. The only way to protect yourself from this issue, is to back up your seed phrase again now.\n\nYou can follow the guide at this link:\n\n[https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up)\n\nWe have fixed the known issue, but will be issuing ongoing bug bounties to help prevent this kind of problem in the future.\n\nFor more information on this issue, [see this blog post](https://medium.com/metamask/seed-phrase-issue-bounty-awarded-e1986e811021)","id":3}] From ee8b91262df3c614252e7f2a0aae464f533a06ed Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 25 Apr 2018 15:27:30 -0700 Subject: [PATCH 46/79] i18n-helper - wrap fallback in brackets to help indicate something is wrong --- ui/i18n-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/i18n-helper.js b/ui/i18n-helper.js index 7753b3bda..79aa93116 100644 --- a/ui/i18n-helper.js +++ b/ui/i18n-helper.js @@ -13,7 +13,7 @@ const getMessage = (locale, key, substitutions) => { if (!entry) { // throw new Error(`Translator - Unable to find value for "${key}"`) log.error(`Translator - Unable to find value for "${key}"`) - return key + return `[${key}]` } let phrase = entry.message // perform substitutions From b97457e9e2b74ca1bffc7655568e4f61dba1c409 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 26 Apr 2018 13:43:44 -0700 Subject: [PATCH 47/79] v4.6.0 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e2b481de..16a9523b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 4.6.0 Thu Apr 26 2018 + - Correctly format currency conversion for locally selected preferred currency. - Improved performance of 3D fox logo. - Fetch token prices based on contract address, not symbol diff --git a/app/manifest.json b/app/manifest.json index dc46f1ca4..3e5eed205 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "4.5.5", + "version": "4.6.0", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", From 8c5994abc9354158d74bf2079192c7f60818f9be Mon Sep 17 00:00:00 2001 From: bitpshr Date: Thu, 26 Apr 2018 18:31:54 -0400 Subject: [PATCH 48/79] Return 0 for token rate fetch failures --- app/scripts/controllers/token-rates.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index abeec4cc0..87d716aa6 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -1,4 +1,5 @@ const ObservableStore = require('obs-store') +const { warn } = require('loglevel') // By default, poll every 3 minutes const DEFAULT_INTERVAL = 180 * 1000 @@ -42,7 +43,10 @@ class TokenRatesController { const response = await fetch(`https://metamask.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) const json = await response.json() return json && json.length ? json[0].averagePrice : 0 - } catch (error) { } + } catch (error) { + warn(`MetaMask - TokenRatesController exchange rate fetch failed for ${address}.`, error) + return 0 + } } /** From 61b8c3efff3b12e117c80db80c237dcfce0c0f49 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Thu, 26 Apr 2018 18:41:24 -0700 Subject: [PATCH 49/79] Redirect to correct seed phrase screen --- ui/app/components/pages/home.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index 90b8e1d37..9110f8202 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -21,7 +21,7 @@ const QrView = require('../../components/qr-code') // Routes const { - REVEAL_SEED_ROUTE, + INITIALIZE_BACKUP_PHRASE_ROUTE, RESTORE_VAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE, NOTICE_ROUTE, @@ -69,7 +69,7 @@ class Home extends Component { log.debug('rendering seed words') return h(Redirect, { to: { - pathname: REVEAL_SEED_ROUTE, + pathname: INITIALIZE_BACKUP_PHRASE_ROUTE, }, }) } From 1bad3fa25782a322859df8942abf39b08777f13d Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Fri, 27 Apr 2018 10:42:02 -0400 Subject: [PATCH 50/79] Allow transactions with 0-gwei gas price (#4073) * Allow transactions with 0-gwei gas price * Add tests to verify tx with 0 gas fee * Conditionally use CurrencyInput in CurrencyDisplay --- CHANGELOG.md | 1 + old-ui/app/components/pending-tx.js | 3 +- package-lock.json | 42 ++++++++++--- test/integration/lib/send-new-ui.js | 59 +++++++++++-------- .../components/customize-gas-modal/index.js | 4 +- ui/app/components/send/currency-display.js | 15 ++--- ui/app/components/send/send-constants.js | 4 +- 7 files changed, 80 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e2b481de..7b8a7e646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Fetch token prices based on contract address, not symbol - Fix bug that prevents setting language locale in settings. - Show checksum addresses throughout the UI +- Allow transactions with a 0 gwei gas price ## 4.5.5 Fri Apr 06 2018 diff --git a/old-ui/app/components/pending-tx.js b/old-ui/app/components/pending-tx.js index cd4189fc4..c8132539c 100644 --- a/old-ui/app/components/pending-tx.js +++ b/old-ui/app/components/pending-tx.js @@ -16,8 +16,7 @@ const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') const BNInput = require('./bn-as-decimal-input') -// corresponds with 0.1 GWEI -const MIN_GAS_PRICE_BN = new BN('100000000') +const MIN_GAS_PRICE_BN = new BN('0') const MIN_GAS_LIMIT_BN = new BN('21000') module.exports = PendingTx diff --git a/package-lock.json b/package-lock.json index 5b5b82450..fd381e993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2821,6 +2821,20 @@ "ieee754": "1.1.8" } }, + "buffer-alloc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.1.0.tgz", + "integrity": "sha1-BVFNM78WVtNUDGhPZbEgLpDsowM=", + "requires": { + "buffer-alloc-unsafe": "0.1.1", + "buffer-fill": "0.1.1" + } + }, + "buffer-alloc-unsafe": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-0.1.1.tgz", + "integrity": "sha1-/+H2dVHdBVc33iUzN7/oU9+rGmo=" + }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2831,6 +2845,11 @@ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" }, + "buffer-fill": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-0.1.1.tgz", + "integrity": "sha512-YgBMBzdRLEfgxJIGu2wrvI2E03tMCFU1p7d1KhB4BOoMN0VxmTFjSyN5JtKt9z8Z9JajMHruI6SE25W96wNv7Q==" + }, "buffer-from": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", @@ -4350,7 +4369,7 @@ "requires": { "file-type": "5.2.0", "is-stream": "1.1.0", - "tar-stream": "1.5.5" + "tar-stream": "1.5.6" } }, "decompress-tarbz2": { @@ -24245,13 +24264,15 @@ } }, "tar-stream": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", - "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.6.tgz", + "integrity": "sha512-tFG9xPbc4Y7CubEwriTss87tdcBQDsw81ejJyCbT4ALNYkNsdPqCfCD6Gkg3OpRkUkq6VO7qpNfwoQAuk/aeNQ==", "requires": { "bl": "1.2.1", + "buffer-alloc": "1.1.0", "end-of-stream": "1.4.0", "readable-stream": "2.3.3", + "to-buffer": "1.1.0", "xtend": "4.0.1" } }, @@ -24639,6 +24660,11 @@ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" }, + "to-buffer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.0.tgz", + "integrity": "sha1-N1vAPtrlw1qPoLP+laHzmF2x3Po=" + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -26284,7 +26310,7 @@ "ethereumjs-block": "1.7.0", "ethereumjs-tx": "1.3.3", "ethereumjs-util": "5.1.5", - "ethereumjs-vm": "2.3.4", + "ethereumjs-vm": "2.3.5", "json-rpc-error": "2.0.0", "json-stable-stringify": "1.0.1", "promise-to-callback": "1.0.0", @@ -26338,9 +26364,9 @@ } }, "ethereumjs-vm": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.3.4.tgz", - "integrity": "sha512-Y4SlzNDqxrCO58jhp98HdnZVdjOqB+HC0hoU+N/DEp1aU+hFkRX/nru5F7/HkQRPIlA6aJlQp/xIA6xZs1kspw==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.3.5.tgz", + "integrity": "sha512-AJ7x44+xqyE5+UO3Nns19WkTdZfyqFZ+sEjIEpvme7Ipbe3iBU1uwCcHEdiu/yY9bdhr3IfSa/NfIKNeXPaRVQ==", "requires": { "async": "2.6.0", "async-eventemitter": "0.2.4", diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js index 09a074750..3da3f4f95 100644 --- a/test/integration/lib/send-new-ui.js +++ b/test/integration/lib/send-new-ui.js @@ -23,6 +23,37 @@ global.ethQuery = { global.ethereumProvider = {} +async function customizeGas (assert, price, limit, ethFee, usdFee) { + const sendGasOpenCustomizeModalButton = await queryAsync($, '.sliders-icon-container') + sendGasOpenCustomizeModalButton[0].click() + + const customizeGasModal = await queryAsync($, '.send-v2__customize-gas') + assert.ok(customizeGasModal[0], 'should render the customize gas modal') + + const customizeGasPriceInput = (await queryAsync($, '.send-v2__gas-modal-card')).first().find('input') + customizeGasPriceInput.val(price) + reactTriggerChange(customizeGasPriceInput[0]) + const customizeGasLimitInput = (await queryAsync($, '.send-v2__gas-modal-card')).last().find('input') + customizeGasLimitInput.val(limit) + reactTriggerChange(customizeGasLimitInput[0]) + + const customizeGasSaveButton = await queryAsync($, '.send-v2__customize-gas__save') + customizeGasSaveButton[0].click() + const sendGasField = await queryAsync($, '.send-v2__gas-fee-display') + + assert.equal( + (await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(), + ethFee, + 'send gas field should show customized gas total' + ) + + assert.equal( + (await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent, + usdFee, + 'send gas field should show customized gas total converted to USD' + ) +} + async function runSendFlowTest(assert, done) { console.log('*** start runSendFlowTest') const selectState = await queryAsync($, 'select') @@ -95,32 +126,8 @@ async function runSendFlowTest(assert, done) { 'send gas field should show estimated gas total converted to USD' ) - const sendGasOpenCustomizeModalButton = await queryAsync($, '.sliders-icon-container') - sendGasOpenCustomizeModalButton[0].click() - - const customizeGasModal = await queryAsync($, '.send-v2__customize-gas') - assert.ok(customizeGasModal[0], 'should render the customize gas modal') - - const customizeGasPriceInput = (await queryAsync($, '.send-v2__gas-modal-card')).first().find('input') - customizeGasPriceInput.val(50) - reactTriggerChange(customizeGasPriceInput[0]) - const customizeGasLimitInput = (await queryAsync($, '.send-v2__gas-modal-card')).last().find('input') - customizeGasLimitInput.val(60000) - reactTriggerChange(customizeGasLimitInput[0]) - - const customizeGasSaveButton = await queryAsync($, '.send-v2__customize-gas__save') - customizeGasSaveButton[0].click() - - assert.equal( - (await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(), - '0.003', - 'send gas field should show customized gas total' - ) - assert.equal( - (await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent, - '$3.60 USD', - 'send gas field should show customized gas total converted to USD' - ) + await customizeGas(assert, 0, 21000, '0', '$0.00 USD') + await customizeGas(assert, 500, 60000, '0.003', '$3.60 USD') const sendButton = await queryAsync($, 'button.btn-primary--lg.page-container__footer-button') assert.equal(sendButton[0].textContent, 'Next', 'next button rendered') diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index 4c693d1c3..1ff8eea87 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -280,8 +280,7 @@ CustomizeGasModal.prototype.render = function () { h(GasModalCard, { value: convertedGasPrice, min: forceGasMin || MIN_GAS_PRICE_GWEI, - // max: 1000, - step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10), + step: 1, onChange: value => this.convertAndSetGasPrice(value), title: this.context.t('gasPrice'), copy: this.context.t('gasPriceCalculation'), @@ -290,7 +289,6 @@ CustomizeGasModal.prototype.render = function () { h(GasModalCard, { value: convertedGasLimit, min: 1, - // max: 100000, step: 1, onChange: value => this.convertAndSetGasLimit(value), title: this.context.t('gasLimit'), diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index a7bd5d7ea..90fb2b66c 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -89,7 +89,6 @@ CurrencyDisplay.prototype.render = function () { } = this.props const valueToRender = this.getValueToRender() - const convertedValueToRender = this.getConvertedValueToRender(valueToRender) return h('div', { @@ -97,22 +96,24 @@ CurrencyDisplay.prototype.render = function () { style: { borderColor: inError ? 'red' : null, }, - onClick: () => this.currencyInput.focus(), + onClick: () => this.currencyInput && this.currencyInput.focus(), }, [ h('div.currency-display__primary-row', [ h('div.currency-display__input-wrapper', [ - h(CurrencyInput, { + h(readOnly ? 'input' : CurrencyInput, { className: primaryBalanceClassName, value: `${valueToRender}`, placeholder: '0', readOnly, - onInputChange: newValue => { - handleChange(this.getAmount(newValue)) - }, - inputRef: input => { this.currencyInput = input }, + ...(!readOnly ? { + onInputChange: newValue => { + handleChange(this.getAmount(newValue)) + }, + inputRef: input => { this.currencyInput = input }, + } : {}), }), h('span.currency-display__currency-symbol', primaryCurrency), diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js index b3ee0899a..5d89c74aa 100644 --- a/ui/app/components/send/send-constants.js +++ b/ui/app/components/send/send-constants.js @@ -1,8 +1,8 @@ const ethUtil = require('ethereumjs-util') const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') -const MIN_GAS_PRICE_HEX = (100000000).toString(16) -const MIN_GAS_PRICE_DEC = '100000000' +const MIN_GAS_PRICE_DEC = '0' +const MIN_GAS_PRICE_HEX = (parseInt(MIN_GAS_PRICE_DEC)).toString(16) const MIN_GAS_LIMIT_DEC = '21000' const MIN_GAS_LIMIT_HEX = (parseInt(MIN_GAS_LIMIT_DEC)).toString(16) From 6de450488b8303e01d4be60c571e99f995711e51 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 27 Apr 2018 20:45:36 -0230 Subject: [PATCH 51/79] Wraps calls to symbol() and decimals() in try catch --- ui/app/components/pending-tx/index.js | 18 ++++----- ui/app/send-v2.js | 2 +- ui/app/token-util.js | 53 +++++++++++++++++---------- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js index 6ee83ba7e..392650e01 100644 --- a/ui/app/components/pending-tx/index.js +++ b/ui/app/components/pending-tx/index.js @@ -8,7 +8,7 @@ const abiDecoder = require('abi-decoder') abiDecoder.addABI(abi) const inherits = require('util').inherits const actions = require('../../actions') -const util = require('../../util') +const { getSymbolAndDecimals } = require('../../token-util') const ConfirmSendEther = require('./confirm-send-ether') const ConfirmSendToken = require('./confirm-send-token') const ConfirmDeployContract = require('./confirm-deploy-contract') @@ -26,6 +26,7 @@ function mapStateToProps (state) { const { conversionRate, identities, + tokens: existingTokens, } = state.metamask const accounts = state.metamask.accounts const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] @@ -33,6 +34,7 @@ function mapStateToProps (state) { conversionRate, identities, selectedAddress, + existingTokens, } } @@ -66,6 +68,7 @@ PendingTx.prototype.componentDidUpdate = function (prevProps, prevState) { } PendingTx.prototype.setTokenData = async function () { + const { existingTokens } = this.props const txMeta = this.gatherTxMeta() const txParams = txMeta.txParams || {} @@ -89,19 +92,14 @@ PendingTx.prototype.setTokenData = async function () { } if (isTokenTransaction) { - const token = util.getContractAtAddress(txParams.to) - const results = await Promise.all([ - token.symbol(), - token.decimals(), - ]) - const [ symbol, decimals ] = results + const { symbol, decimals } = await getSymbolAndDecimals(txParams.to, existingTokens) - if (symbol[0] && decimals[0]) { + if (symbol && decimals) { this.setState({ transactionType: TX_TYPES.SEND_TOKEN, tokenAddress: txParams.to, - tokenSymbol: symbol[0], - tokenDecimals: decimals[0], + tokenSymbol: symbol, + tokenDecimals: decimals, isFetching: false, }) } else { diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 30d3d3152..bd00b186e 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -493,7 +493,7 @@ SendTransactionScreen.prototype.renderFooter = function () { history, } = this.props - const missingTokenBalance = selectedToken && !tokenBalance + const missingTokenBalance = selectedToken && (tokenBalance === null || tokenBalance === undefined) const noErrors = !amountError && toError === null return h('div.page-container__footer', [ diff --git a/ui/app/token-util.js b/ui/app/token-util.js index f84051ef5..2ae3b15b9 100644 --- a/ui/app/token-util.js +++ b/ui/app/token-util.js @@ -1,14 +1,6 @@ -const abi = require('human-standard-token-abi') -const Eth = require('ethjs-query') -const EthContract = require('ethjs-contract') - -const tokenInfoGetter = function () { - if (typeof global.ethereumProvider === 'undefined') return - - const eth = new Eth(global.ethereumProvider) - const contract = new EthContract(eth) - const TokenContract = contract(abi) +const util = require('./util') +function tokenInfoGetter () { const tokens = {} return async (address) => { @@ -16,21 +8,41 @@ const tokenInfoGetter = function () { return tokens[address] } - const contract = TokenContract.at(address) - - const result = await Promise.all([ - contract.symbol(), - contract.decimals(), - ]) - - const [ symbol = [], decimals = [] ] = result - - tokens[address] = { symbol: symbol[0], decimals: decimals[0] } + tokens[address] = await getSymbolAndDecimals(address) return tokens[address] } } +async function getSymbolAndDecimals (tokenAddress, existingTokens = []) { + const existingToken = existingTokens.find(({ address }) => tokenAddress === address) + if (existingToken) { + return { + symbol: existingToken.symbol, + decimals: existingToken.decimals, + } + } + + let result = [] + try { + const token = util.getContractAtAddress(tokenAddress) + + result = await Promise.all([ + token.symbol(), + token.decimals(), + ]) + } catch (err) { + console.log(`symbol() and decimal() calls for token at address ${tokenAddress} resulted in error:`, err) + } + + const [ symbol = [], decimals = [] ] = result + + return { + symbol: symbol[0], + decimals: decimals[0], + } +} + function calcTokenAmount (value, decimals) { const multiplier = Math.pow(10, Number(decimals || 0)) const amount = Number(value / multiplier) @@ -42,4 +54,5 @@ function calcTokenAmount (value, decimals) { module.exports = { tokenInfoGetter, calcTokenAmount, + getSymbolAndDecimals, } From b71dbf52d190a597ad3e06e4717cc641e1abe57a Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 27 Apr 2018 21:33:56 -0230 Subject: [PATCH 52/79] Convert decimals to string in getSymbolAndDecimals; return null for symbol or decimals. --- ui/app/components/pages/add-token.js | 2 +- ui/app/components/pending-tx/index.js | 24 +++++++----------------- ui/app/token-util.js | 4 ++-- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/ui/app/components/pages/add-token.js b/ui/app/components/pages/add-token.js index 566e42450..8d52571d0 100644 --- a/ui/app/components/pages/add-token.js +++ b/ui/app/components/pages/add-token.js @@ -192,7 +192,7 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) if (symbol && decimals) { this.setState({ customSymbol: symbol, - customDecimals: decimals.toString(), + customDecimals: decimals, autoFilled: true, }) } diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js index 392650e01..fb409cb92 100644 --- a/ui/app/components/pending-tx/index.js +++ b/ui/app/components/pending-tx/index.js @@ -94,23 +94,13 @@ PendingTx.prototype.setTokenData = async function () { if (isTokenTransaction) { const { symbol, decimals } = await getSymbolAndDecimals(txParams.to, existingTokens) - if (symbol && decimals) { - this.setState({ - transactionType: TX_TYPES.SEND_TOKEN, - tokenAddress: txParams.to, - tokenSymbol: symbol, - tokenDecimals: decimals, - isFetching: false, - }) - } else { - this.setState({ - transactionType: TX_TYPES.SEND_TOKEN, - tokenAddress: txParams.to, - tokenSymbol: null, - tokenDecimals: null, - isFetching: false, - }) - } + this.setState({ + transactionType: TX_TYPES.SEND_TOKEN, + tokenAddress: txParams.to, + tokenSymbol: symbol, + tokenDecimals: decimals, + isFetching: false, + }) } else { this.setState({ transactionType: TX_TYPES.SEND_ETHER, diff --git a/ui/app/token-util.js b/ui/app/token-util.js index 2ae3b15b9..1b0091826 100644 --- a/ui/app/token-util.js +++ b/ui/app/token-util.js @@ -38,8 +38,8 @@ async function getSymbolAndDecimals (tokenAddress, existingTokens = []) { const [ symbol = [], decimals = [] ] = result return { - symbol: symbol[0], - decimals: decimals[0], + symbol: symbol[0] || null, + decimals: decimals[0] && decimals[0].toString() || null, } } From 9f9e8789cc707fa3c53d64edeb2d51e84defcbc6 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 27 Apr 2018 21:38:57 -0230 Subject: [PATCH 53/79] Simplify return of existing token in getSymbolAndDecimals --- ui/app/token-util.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui/app/token-util.js b/ui/app/token-util.js index 1b0091826..920442bfc 100644 --- a/ui/app/token-util.js +++ b/ui/app/token-util.js @@ -17,10 +17,7 @@ function tokenInfoGetter () { async function getSymbolAndDecimals (tokenAddress, existingTokens = []) { const existingToken = existingTokens.find(({ address }) => tokenAddress === address) if (existingToken) { - return { - symbol: existingToken.symbol, - decimals: existingToken.decimals, - } + return existingToken } let result = [] From b487dab2e78be64a05a8eaa9c3dcd481f4aebb84 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 17:11:44 -0700 Subject: [PATCH 54/79] test - unit - use ganache instead of deprecated block-tracker test util --- package.json | 3 ++- test/stub/provider.js | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 73892bc28..8e3501ead 100644 --- a/package.json +++ b/package.json @@ -217,9 +217,10 @@ "eslint-plugin-json": "^1.2.0", "eslint-plugin-mocha": "^5.0.0", "eslint-plugin-react": "^7.4.0", - "eth-json-rpc-middleware": "^1.2.7", + "eth-json-rpc-middleware": "^1.6.0", "fs-promise": "^2.0.3", "ganache-cli": "^6.1.0", + "ganache-core": "^2.1.0", "gifencoder": "^1.1.0", "gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed", "gulp-babel": "^7.0.0", diff --git a/test/stub/provider.js b/test/stub/provider.js index e77db4e28..a1c70486d 100644 --- a/test/stub/provider.js +++ b/test/stub/provider.js @@ -1,14 +1,28 @@ const JsonRpcEngine = require('json-rpc-engine') const scaffoldMiddleware = require('eth-json-rpc-middleware/scaffold') -const TestBlockchain = require('eth-block-tracker/test/util/testBlockMiddleware') +const providerAsMiddleware = require('eth-json-rpc-middleware/providerAsMiddleware') +const GanacheCore = require('ganache-core') module.exports = { createEngineForTestData, providerFromEngine, scaffoldMiddleware, createTestProviderTools, + getTestSeed, + getTestAccounts, } +function getTestSeed () { + return 'people carpet cluster attract ankle motor ozone mass dove original primary mask' +} + +function getTestAccounts () { + return [ + { address: '0x88bb7F89eB5e5b30D3e15a57C68DBe03C6aCCB21', key: Buffer.from('254A8D551474F35CCC816388B4ED4D20B945C96B7EB857A68064CB9E9FB2C092', 'hex') }, + { address: '0x1fe9aAB565Be19629fF4e8541ca2102fb42D7724', key: Buffer.from('6BAB5A4F2A6911AF8EE2BD32C6C05F6643AC48EF6C939CDEAAAE6B1620805A9B', 'hex') }, + { address: '0xbda5c89aa6bA1b352194291AD6822C92AbC87c7B', key: Buffer.from('9B11D7F833648F26CE94D544855558D7053ECD396E4F4563968C232C012879B0', 'hex') }, + ] +} function createEngineForTestData () { return new JsonRpcEngine() @@ -21,11 +35,13 @@ function providerFromEngine (engine) { function createTestProviderTools (opts = {}) { const engine = createEngineForTestData() - const testBlockchain = new TestBlockchain() // handle provided hooks engine.push(scaffoldMiddleware(opts.scaffold || {})) // handle block tracker methods - engine.push(testBlockchain.createMiddleware()) + engine.push(providerAsMiddleware(GanacheCore.provider({ + mnemonic: getTestSeed(), + }))) + // wrap in standard provider interface const provider = providerFromEngine(engine) - return { provider, engine, testBlockchain } + return { provider, engine } } From b8a18feb51ed10ea56af47d4486f9c90bf713a0a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 17:12:52 -0700 Subject: [PATCH 55/79] test - unit - send from test accounts to avoid not-enough-balance error from ganache --- test/unit/tx-controller-test.js | 22 +++++++++++----------- test/unit/tx-utils-test.js | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 20d6f8573..0b5c7226a 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -6,16 +6,15 @@ const ObservableStore = require('obs-store') const sinon = require('sinon') const TransactionController = require('../../app/scripts/controllers/transactions') const TxGasUtils = require('../../app/scripts/controllers/transactions/tx-gas-utils') -const { createTestProviderTools } = require('../stub/provider') +const { createTestProviderTools, getTestAccounts } = require('../stub/provider') const noop = () => true const currentNetworkId = 42 const otherNetworkId = 36 -const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex') describe('Transaction Controller', function () { - let txController, provider, providerResultStub, testBlockchain + let txController, provider, providerResultStub, query, fromAccount beforeEach(function () { providerResultStub = { @@ -24,9 +23,9 @@ describe('Transaction Controller', function () { // by default, all accounts are external accounts (not contracts) eth_getCode: '0x', } - const providerTools = createTestProviderTools({ scaffold: providerResultStub }) - provider = providerTools.provider - testBlockchain = providerTools.testBlockchain + provider = createTestProviderTools({ scaffold: providerResultStub }).provider + query = new EthjsQuery(provider) + fromAccount = getTestAccounts()[0] txController = new TransactionController({ provider, @@ -34,7 +33,7 @@ describe('Transaction Controller', function () { txHistoryLimit: 10, blockTracker: { getCurrentBlock: noop, on: noop, once: noop }, signTransaction: (ethTx) => new Promise((resolve) => { - ethTx.sign(privKey) + ethTx.sign(fromAccount.key) resolve() }), }) @@ -298,12 +297,12 @@ describe('Transaction Controller', function () { describe('#updateAndApproveTransaction', function () { let txMeta - beforeEach(function () { + beforeEach(() => { txMeta = { id: 1, status: 'unapproved', txParams: { - from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + from: fromAccount.address, to: '0x1678a085c290ebd122dc42cba69373b5953b831d', gasPrice: '0x77359400', gas: '0x7b0d', @@ -312,11 +311,12 @@ describe('Transaction Controller', function () { metamaskNetworkId: currentNetworkId, } }) - it('should update and approve transactions', function () { + it('should update and approve transactions', async () => { txController.txStateManager.addTx(txMeta) - txController.updateAndApproveTransaction(txMeta) + const approvalPromise = txController.updateAndApproveTransaction(txMeta) const tx = txController.txStateManager.getTx(1) assert.equal(tx.status, 'approved') + await approvalPromise }) }) diff --git a/test/unit/tx-utils-test.js b/test/unit/tx-utils-test.js index ae7afd285..be16225ba 100644 --- a/test/unit/tx-utils-test.js +++ b/test/unit/tx-utils-test.js @@ -95,4 +95,4 @@ describe('txUtils', function () { txUtils.validateFrom(txParams) }) }) -}) \ No newline at end of file +}) From e01b81ed9be3dbfd67ae489e277c8aaf6adf58a7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 17:19:46 -0700 Subject: [PATCH 56/79] deps - package-lock --- package-lock.json | 118 +++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index fd381e993..04a9ab336 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4369,7 +4369,7 @@ "requires": { "file-type": "5.2.0", "is-stream": "1.1.0", - "tar-stream": "1.5.6" + "tar-stream": "1.6.0" } }, "decompress-tarbz2": { @@ -5889,25 +5889,26 @@ "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-3.0.0.tgz", "integrity": "sha512-Ab6170AxlF4DK+HDImh52+AetwHPHstgg8uWtX4im26rqK7u4ziSfvUIUK2+/LK0pi0wbIFb8hZm5jPKAXDmBA==", "requires": { - "eth-json-rpc-middleware": "1.5.0", + "eth-json-rpc-middleware": "1.6.0", "json-rpc-engine": "3.6.1", "json-rpc-error": "2.0.0", "tape": "4.8.0" } }, "eth-json-rpc-middleware": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.5.0.tgz", - "integrity": "sha1-FrEFM4aqOAOxJXMqpt4H6t8Ghyk=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz", + "integrity": "sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q==", "requires": { "async": "2.6.0", "eth-query": "2.1.2", - "eth-tx-summary": "3.1.2", + "eth-tx-summary": "3.2.1", "ethereumjs-block": "1.7.0", "ethereumjs-tx": "1.3.3", - "ethereumjs-util": "5.1.5", + "ethereumjs-util": "5.2.0", "ethereumjs-vm": "2.3.2", "fetch-ponyfill": "4.1.0", + "json-rpc-engine": "3.6.1", "json-rpc-error": "2.0.0", "json-stable-stringify": "1.0.1", "promise-to-callback": "1.0.0", @@ -5915,9 +5916,9 @@ }, "dependencies": { "ethereumjs-util": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", - "integrity": "sha512-xPaSEATYJpMTCGowIt0oMZwFP4R1bxd6QsWgkcDvFL0JtXsr39p32WEcD14RscCjfP41YXZPCVWA4yAg0nrJmw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", "requires": { "bn.js": "4.11.8", "create-hash": "1.1.3", @@ -6245,11 +6246,12 @@ } }, "eth-tx-summary": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.1.2.tgz", - "integrity": "sha1-44g2/J+LVvFNdZUvD15XD4j7IiA=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.2.1.tgz", + "integrity": "sha512-mu8g5tDkQxlFah58ggFhTzolE4OnYTj6j8SVsnGsiWT7WxN722RwnEsk/bco2foy+PLSEF2Mnoiw+wCqKoY72A==", "requires": { "async": "2.6.0", + "bn.js": "4.11.8", "clone": "2.1.1", "concat-stream": "1.6.0", "end-of-stream": "1.4.0", @@ -6257,12 +6259,46 @@ "ethereumjs-block": "1.7.0", "ethereumjs-tx": "1.3.3", "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", - "ethereumjs-vm": "2.3.2", + "ethereumjs-vm": "2.3.5", "through2": "2.0.3", "treeify": "1.1.0", "web3-provider-engine": "13.8.0" }, "dependencies": { + "ethereumjs-vm": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.3.5.tgz", + "integrity": "sha512-AJ7x44+xqyE5+UO3Nns19WkTdZfyqFZ+sEjIEpvme7Ipbe3iBU1uwCcHEdiu/yY9bdhr3IfSa/NfIKNeXPaRVQ==", + "requires": { + "async": "2.6.0", + "async-eventemitter": "0.2.4", + "ethereum-common": "0.2.0", + "ethereumjs-account": "2.0.4", + "ethereumjs-block": "1.7.0", + "ethereumjs-util": "5.2.0", + "fake-merkle-patricia-tree": "1.0.1", + "functional-red-black-tree": "1.0.1", + "merkle-patricia-tree": "2.3.0", + "rustbn.js": "0.1.1", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "requires": { + "bn.js": "4.11.8", + "create-hash": "1.1.3", + "ethjs-util": "0.1.4", + "keccak": "1.4.0", + "rlp": "2.0.0", + "safe-buffer": "5.1.1", + "secp256k1": "3.4.0" + } + } + } + }, "web3-provider-engine": { "version": "13.8.0", "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz", @@ -6274,8 +6310,8 @@ "eth-sig-util": "1.4.2", "ethereumjs-block": "1.7.0", "ethereumjs-tx": "1.3.3", - "ethereumjs-util": "5.1.5", - "ethereumjs-vm": "2.3.2", + "ethereumjs-util": "5.2.0", + "ethereumjs-vm": "2.3.5", "fetch-ponyfill": "4.1.0", "json-rpc-error": "2.0.0", "json-stable-stringify": "1.0.1", @@ -6290,9 +6326,9 @@ }, "dependencies": { "ethereumjs-util": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", - "integrity": "sha512-xPaSEATYJpMTCGowIt0oMZwFP4R1bxd6QsWgkcDvFL0JtXsr39p32WEcD14RscCjfP41YXZPCVWA4yAg0nrJmw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", "requires": { "bn.js": "4.11.8", "create-hash": "1.1.3", @@ -7809,6 +7845,11 @@ "null-check": "1.0.0" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-exists-sync": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", @@ -8890,7 +8931,7 @@ "ethereumjs-account": "2.0.4", "ethereumjs-block": "1.2.2", "ethereumjs-tx": "1.3.3", - "ethereumjs-util": "5.1.5", + "ethereumjs-util": "5.2.0", "ethereumjs-vm": "2.3.3", "ethereumjs-wallet": "0.6.0", "fake-merkle-patricia-tree": "1.0.1", @@ -8911,7 +8952,7 @@ "tmp": "0.0.31", "web3": "1.0.0-beta.34", "web3-provider-engine": "13.8.0", - "websocket": "1.0.25", + "websocket": "1.0.26", "yargs": "7.1.0" }, "dependencies": { @@ -9011,9 +9052,9 @@ } }, "ethereumjs-util": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz", - "integrity": "sha512-xPaSEATYJpMTCGowIt0oMZwFP4R1bxd6QsWgkcDvFL0JtXsr39p32WEcD14RscCjfP41YXZPCVWA4yAg0nrJmw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", "requires": { "bn.js": "4.11.6", "create-hash": "1.1.3", @@ -9034,7 +9075,7 @@ "ethereum-common": "0.2.0", "ethereumjs-account": "2.0.4", "ethereumjs-block": "1.7.1", - "ethereumjs-util": "5.1.5", + "ethereumjs-util": "5.2.0", "fake-merkle-patricia-tree": "1.0.1", "functional-red-black-tree": "1.0.1", "merkle-patricia-tree": "2.3.0", @@ -9055,7 +9096,7 @@ "async": "2.6.0", "ethereum-common": "0.2.0", "ethereumjs-tx": "1.3.3", - "ethereumjs-util": "5.1.5", + "ethereumjs-util": "5.2.0", "merkle-patricia-tree": "2.3.0" } } @@ -9193,7 +9234,7 @@ "eth-sig-util": "1.4.2", "ethereumjs-block": "1.2.2", "ethereumjs-tx": "1.3.3", - "ethereumjs-util": "5.1.5", + "ethereumjs-util": "5.2.0", "ethereumjs-vm": "2.3.3", "fetch-ponyfill": "4.1.0", "json-rpc-error": "2.0.0", @@ -24264,15 +24305,16 @@ } }, "tar-stream": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.6.tgz", - "integrity": "sha512-tFG9xPbc4Y7CubEwriTss87tdcBQDsw81ejJyCbT4ALNYkNsdPqCfCD6Gkg3OpRkUkq6VO7qpNfwoQAuk/aeNQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.0.tgz", + "integrity": "sha512-lh2iAPG/BHNmN6WB9Ybdynk9rEJ5GD/dy4zscHmVlwa1dq2tpE+BH78i5vjYwYVWEaOXGBjzxr89aVACF17Cpw==", "requires": { "bl": "1.2.1", "buffer-alloc": "1.1.0", "end-of-stream": "1.4.0", + "fs-constants": "1.0.0", "readable-stream": "2.3.3", - "to-buffer": "1.1.0", + "to-buffer": "1.1.1", "xtend": "4.0.1" } }, @@ -24661,9 +24703,9 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" }, "to-buffer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.0.tgz", - "integrity": "sha1-N1vAPtrlw1qPoLP+laHzmF2x3Po=" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, "to-fast-properties": { "version": "1.0.3", @@ -26343,7 +26385,7 @@ "integrity": "sha512-uMYkEP6fga8CyNo8TMoA/7cxi6bL3V8pTvjKQikOi9iYl6/AO5xlfgniyAMElSiq2mmXz3lYa/9VYDMzt/J5aA==", "requires": { "cross-fetch": "2.1.0", - "eth-json-rpc-middleware": "1.5.0", + "eth-json-rpc-middleware": "1.6.0", "json-rpc-engine": "3.6.1", "json-rpc-error": "2.0.0", "tape": "4.8.0" @@ -27043,9 +27085,9 @@ } }, "websocket": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.25.tgz", - "integrity": "sha512-M58njvi6ZxVb5k7kpnHh2BvNKuBWiwIYvsToErBzWhvBZYwlEiLcyLrG41T1jRcrY9ettqPYEqduLI7ul54CVQ==", + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.26.tgz", + "integrity": "sha512-fjcrYDPIQxpTnqFQ9JjxUQcdvR89MFAOjPBlF+vjOt49w/XW4fJknUoMz/mDIn2eK1AdslVojcaOxOqyZZV8rw==", "requires": { "debug": "2.6.9", "nan": "2.8.0", From 5c9cb0b7872d842bc5465894e1b2844ee352556c Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 18:15:50 -0700 Subject: [PATCH 57/79] build - minify via gulp-uglify-es instead of uglifyify --- gulpfile.js | 20 ++++++++++---------- package.json | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 4f0da9d60..e844a4cba 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -484,16 +484,6 @@ function generateBundler(opts, performBundle) { NODE_ENV: opts.devMode ? 'development' : 'production', })) - // Minification - if (opts.minifyBuild) { - bundler.transform('uglifyify', { - global: true, - mangle: { - reserved: [ 'MetamaskInpageProvider' ] - }, - }) - } - if (opts.watch) { bundler = watchify(bundler) // on any file update, re-runs the bundler @@ -567,6 +557,16 @@ function bundleTask(opts) { .pipe(sourcemaps.init({ loadMaps: true })) } + // Minification + if (opts.minifyBuild) { + buildStream = buildStream + .pipe(uglify({ + mangle: { + reserved: [ 'MetamaskInpageProvider' ] + }, + })) + } + // Finalize Source Maps (writes .map file) if (opts.buildSourceMaps) { buildStream = buildStream diff --git a/package.json b/package.json index 73892bc28..953d8d7ac 100644 --- a/package.json +++ b/package.json @@ -272,7 +272,6 @@ "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", - "uglifyify": "github:MetaMask/uglifyify#keep-flags", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "watchify": "^3.9.0" From f4fe3e81c16073c06aef489252f31cabef8b2214 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 18:17:21 -0700 Subject: [PATCH 58/79] development - add sourcemap debug tool --- development/sourcemap-validator.js | 49 ++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 50 insertions(+) create mode 100644 development/sourcemap-validator.js diff --git a/development/sourcemap-validator.js b/development/sourcemap-validator.js new file mode 100644 index 000000000..edc97667a --- /dev/null +++ b/development/sourcemap-validator.js @@ -0,0 +1,49 @@ +const fs = require('fs') +const { SourceMapConsumer } = require('source-map') + +// +// Utility to help check if sourcemaps are working +// +// searches `dist/chrome/inpage.js` for "new Error" statements +// and prints their source lines using the sourcemaps. +// if not working it may error or print minified garbage +// + +start() + +async function start() { + const rawBuild = fs.readFileSync(__dirname + '/../dist/chrome/inpage.js', 'utf8') + const rawSourceMap = fs.readFileSync(__dirname + '/../dist/sourcemaps/inpage.js.map', 'utf8') + const consumer = await new SourceMapConsumer(rawSourceMap) + + console.log('hasContentsOfAllSources:', consumer.hasContentsOfAllSources(), '\n') + console.log('sources:') + consumer.sources.map((sourcePath) => console.log(sourcePath)) + + console.log('\nexamining "new Error" statements:\n') + const sourceLines = rawBuild.split('\n') + sourceLines.map(line => indicesOf('new Error', line)) + .forEach((errorIndices, lineIndex) => { + // if (errorIndex === null) return console.log('line does not contain "new Error"') + errorIndices.forEach((errorIndex) => { + const position = { line: lineIndex + 1, column: errorIndex } + const result = consumer.originalPositionFor(position) + if (!result.source) return console.warn(`!! missing source for position: ${position}`) + // filter out deps distributed minified without sourcemaps + if (result.source === 'node_modules/browserify/node_modules/browser-pack/_prelude.js') return // minified mess + if (result.source === 'node_modules/web3/dist/web3.min.js') return // minified mess + const sourceContent = consumer.sourceContentFor(result.source) + const sourceLines = sourceContent.split('\n') + const line = sourceLines[result.line-1] + console.log(`\n========================== ${result.source} ====================================\n`) + console.log(line) + console.log(`\n==============================================================================\n`) + }) + }) +} + +function indicesOf(substring, string) { + var a=[],i=-1; + while((i=string.indexOf(substring,i+1)) >= 0) a.push(i); + return a; +} diff --git a/package.json b/package.json index 953d8d7ac..39182fba0 100644 --- a/package.json +++ b/package.json @@ -269,6 +269,7 @@ "selenium-webdriver": "^3.5.0", "shell-parallel": "^1.0.3", "sinon": "^5.0.0", + "source-map": "^0.7.2", "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", From 1a60f42933739ae69f7ff0ac4ba56f4f4c9bfff5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Apr 2018 18:17:31 -0700 Subject: [PATCH 59/79] deps - package-lock --- package-lock.json | 94 ++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5b5b82450..ae9bbb420 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3603,6 +3603,14 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } } } }, @@ -4833,6 +4841,15 @@ "string_decoder": "0.10.31" } }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -10422,7 +10439,7 @@ "debug-fabulous": "1.0.0", "detect-newline": "2.1.0", "graceful-fs": "4.1.11", - "source-map": "0.4.4", + "source-map": "0.7.2", "strip-bom-string": "1.0.0", "through2": "2.0.3" }, @@ -11933,6 +11950,16 @@ "integrity": "sha1-Skxd2OT7Xps82mDIIt+tyu5m4K8=", "requires": { "source-map": "0.4.4" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + } } }, "inquirer": { @@ -21856,6 +21883,16 @@ "requires": { "js-base64": "2.4.3", "source-map": "0.4.4" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + } } }, "secp256k1": { @@ -22718,12 +22755,10 @@ "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" }, "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": "1.0.1" - } + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.2.tgz", + "integrity": "sha512-NDJB/R2BS7YJG0tP9SbE4DKwKj1idLT5RJqfVYZ7dreFX7wulZT3xxVhbYKrQo9n0JkRptl51TrX/5VK3HodMA==", + "dev": true }, "source-map-resolve": { "version": "0.5.1", @@ -22920,7 +22955,7 @@ "requires": { "esprima": "1.0.4", "estraverse": "1.3.2", - "source-map": "0.4.4" + "source-map": "0.7.2" } }, "esprima": { @@ -24972,55 +25007,12 @@ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "uglify-to-browserify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, - "uglifyify": { - "version": "github:MetaMask/uglifyify#8662585e39125a96a5379d71cb4a606829790f87", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "extend": "1.3.0", - "minimatch": "3.0.4", - "through": "2.3.8", - "uglify-es": "3.3.9" - }, - "dependencies": { - "extend": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz", - "integrity": "sha1-0VFvsP9WJNLr+RI+odrFoZlABPg=", - "dev": true - } - } - }, "ultron": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", From 805c16170453ed5106dcc1d48f0efc123831f960 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Sat, 28 Apr 2018 13:58:44 -0400 Subject: [PATCH 60/79] Update old UI ShapeshiftForm to gracefully handle errors --- old-ui/app/components/shapeshift-form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/old-ui/app/components/shapeshift-form.js b/old-ui/app/components/shapeshift-form.js index a54987c04..97068db0a 100644 --- a/old-ui/app/components/shapeshift-form.js +++ b/old-ui/app/components/shapeshift-form.js @@ -138,7 +138,7 @@ ShapeshiftForm.prototype.renderMain = function () { width: '229px', height: '82px', }, - }, this.props.warning) + }, this.props.warning + '') : this.renderInfo(), this.renderRefundAddressForCoin(coin), From b4b33b57bcbdc2f1c8572efb0d98cbe6b86eee39 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Sat, 28 Apr 2018 14:41:46 -0400 Subject: [PATCH 61/79] Fix ShapeShift purchases on new UI --- ui/app/components/shapeshift-form.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index fd4a80a4a..22ab64426 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -55,6 +55,10 @@ function ShapeshiftForm () { } } +ShapeshiftForm.prototype.getCoinPair = function () { + return `${this.state.depositCoin.toUpperCase()}_ETH` +} + ShapeshiftForm.prototype.componentWillMount = function () { this.props.shapeShiftSubview() } @@ -120,14 +124,12 @@ ShapeshiftForm.prototype.renderMetadata = function (label, value) { } ShapeshiftForm.prototype.renderMarketInfo = function () { - const { depositCoin } = this.state - const coinPair = `${depositCoin}_eth` const { tokenExchangeRates } = this.props const { limit, rate, minimum, - } = tokenExchangeRates[coinPair] || {} + } = tokenExchangeRates[this.getCoinPair()] || {} return h('div.shapeshift-form__metadata', {}, [ @@ -172,10 +174,9 @@ ShapeshiftForm.prototype.renderQrCode = function () { ShapeshiftForm.prototype.render = function () { const { coinOptions, btnClass, warning } = this.props - const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state - const coinPair = `${depositCoin}_eth` + const { errorMessage, showQrCode, depositAddress } = this.state const { tokenExchangeRates } = this.props - const token = tokenExchangeRates[coinPair] + const token = tokenExchangeRates[this.getCoinPair()] return h('div.shapeshift-form-wrapper', [ showQrCode From 873f85d9962ff569baa8a5bbf2c1337c3b920419 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Sun, 29 Apr 2018 13:46:07 -0700 Subject: [PATCH 62/79] guard adainst raven erroring --- app/scripts/background.js | 6 +++++- app/scripts/lib/setupRaven.js | 37 +++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 38b871bb5..69d549c85 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -261,7 +261,11 @@ function setupController (initState, initLangCode) { controller.txController.on(`tx:status-update`, (txId, status) => { if (status !== 'failed') return const txMeta = controller.txController.txStateManager.getTx(txId) - reportFailedTxToSentry({ raven, txMeta }) + try { + reportFailedTxToSentry({ raven, txMeta }) + } catch (e) { + console.error(e) + } }) // setup state persistence diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index 9ec9a256f..48b941c3d 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -24,19 +24,20 @@ function setupRaven(opts) { transport: function(opts) { const report = opts.data // simplify certain complex error messages - report.exception.values.forEach(item => { - let errorMessage = item.value - // simplify ethjs error messages - errorMessage = extractEthjsErrorMessage(errorMessage) - // simplify 'Transaction Failed: known transaction' - if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { - // cut the hash from the error message - errorMessage = 'Transaction Failed: known transaction' - } - // finalize - item.value = errorMessage - }) - + if (report.exception && report.exception.values) { + report.exception.values.forEach(item => { + let errorMessage = item.value + // simplify ethjs error messages + errorMessage = extractEthjsErrorMessage(errorMessage) + // simplify 'Transaction Failed: known transaction' + if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { + // cut the hash from the error message + errorMessage = 'Transaction Failed: known transaction' + } + // finalize + item.value = errorMessage + }) + } // modify report urls rewriteReportUrls(report) // make request normally @@ -52,11 +53,13 @@ function rewriteReportUrls(report) { // update request url report.request.url = toMetamaskUrl(report.request.url) // update exception stack trace - report.exception.values.forEach(item => { - item.stacktrace.frames.forEach(frame => { - frame.filename = toMetamaskUrl(frame.filename) + if (report.exception && report.exception.values) { + report.exception.values.forEach(item => { + item.stacktrace.frames.forEach(frame => { + frame.filename = toMetamaskUrl(frame.filename) + }) }) - }) + } } function toMetamaskUrl(origUrl) { From daaaab3ab06688cc7b3d349ce436c59909f54f5c Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Sun, 29 Apr 2018 23:18:34 -0400 Subject: [PATCH 63/79] Verify identities state after restoring new accounts (#4091) --- test/unit/metamask-controller-test.js | 33 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js index adeca9b5f..18c3f9ab9 100644 --- a/test/unit/metamask-controller-test.js +++ b/test/unit/metamask-controller-test.js @@ -6,6 +6,12 @@ const MetaMaskController = require('../../app/scripts/metamask-controller') const blacklistJSON = require('../stub/blacklist') const firstTimeState = require('../../app/scripts/first-time-state') +const DEFAULT_LABEL = 'Account 1' +const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' +const TEST_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc' +const TEST_SEED_ALT = 'setup olympic issue mobile velvet surge alcohol burger horse view reopen gentle' +const TEST_ADDRESS_ALT = '0xc42edfcc21ed14dda456aa0756c153f7985d8813' + describe('MetaMaskController', function () { let metamaskController const sandbox = sinon.sandbox.create() @@ -87,17 +93,28 @@ describe('MetaMaskController', function () { describe('#createNewVaultAndRestore', function () { it('should be able to call newVaultAndRestore despite a mistake.', async function () { - const password = 'what-what-what' - const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' - const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' - await metamaskController.createNewVaultAndRestore(password, wrongSeed) - .catch((e) => { - return - }) - await metamaskController.createNewVaultAndRestore(password, rightSeed) + await metamaskController.createNewVaultAndRestore(password, TEST_SEED.slice(0, -1)).catch((e) => null) + await metamaskController.createNewVaultAndRestore(password, TEST_SEED) assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) }) + + it('should clear previous identities after vault restoration', async () => { + await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED) + assert.deepEqual(metamaskController.getState().identities, { + [TEST_ADDRESS]: { address: TEST_ADDRESS, name: DEFAULT_LABEL }, + }) + + await metamaskController.keyringController.saveAccountLabel(TEST_ADDRESS, 'Account Foo') + assert.deepEqual(metamaskController.getState().identities, { + [TEST_ADDRESS]: { address: TEST_ADDRESS, name: 'Account Foo' }, + }) + + await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED_ALT) + assert.deepEqual(metamaskController.getState().identities, { + [TEST_ADDRESS_ALT]: { address: TEST_ADDRESS_ALT, name: DEFAULT_LABEL }, + }) + }) }) }) From 2a8d3ea450791c9c932bff4908aab3e62a1408f5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Apr 2018 12:07:48 -0700 Subject: [PATCH 64/79] sentry - wrap report modifiers in a try-catch --- app/scripts/lib/setupRaven.js | 40 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index 48b941c3d..b69e08bae 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -23,23 +23,14 @@ function setupRaven(opts) { release, transport: function(opts) { const report = opts.data - // simplify certain complex error messages - if (report.exception && report.exception.values) { - report.exception.values.forEach(item => { - let errorMessage = item.value - // simplify ethjs error messages - errorMessage = extractEthjsErrorMessage(errorMessage) - // simplify 'Transaction Failed: known transaction' - if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { - // cut the hash from the error message - errorMessage = 'Transaction Failed: known transaction' - } - // finalize - item.value = errorMessage - }) + try { + // simplify certain complex error messages (e.g. Ethjs) + simplifyErrorMessages(report) + // modify report urls + rewriteReportUrls(report) + } catch (err) { + console.warn(err) } - // modify report urls - rewriteReportUrls(report) // make request normally client._makeRequest(opts) }, @@ -49,6 +40,23 @@ function setupRaven(opts) { return Raven } +function simplifyErrorMessages(report) { + if (report.exception && report.exception.values) { + report.exception.values.forEach(item => { + let errorMessage = item.value + // simplify ethjs error messages + errorMessage = extractEthjsErrorMessage(errorMessage) + // simplify 'Transaction Failed: known transaction' + if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) { + // cut the hash from the error message + errorMessage = 'Transaction Failed: known transaction' + } + // finalize + item.value = errorMessage + }) + } +} + function rewriteReportUrls(report) { // update request url report.request.url = toMetamaskUrl(report.request.url) From e881ea7aaf1faea8fdbf730c09a1aadbf2366d45 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Apr 2018 12:10:15 -0700 Subject: [PATCH 65/79] sentry - report error-like messages using the obj message --- app/scripts/lib/setupRaven.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index b69e08bae..b1b67f771 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -24,6 +24,8 @@ function setupRaven(opts) { transport: function(opts) { const report = opts.data try { + // handle error-like non-error exceptions + nonErrorException(report) // simplify certain complex error messages (e.g. Ethjs) simplifyErrorMessages(report) // modify report urls @@ -40,6 +42,14 @@ function setupRaven(opts) { return Raven } +function nonErrorException(report) { + // handle errors that lost their error-ness in serialization + if (report.message.includes('Non-Error exception captured with keys: message')) { + if (!(report.extra && report.extra.__serialized__)) return + report.message = `Non-Error Exception: ${report.extra.__serialized__.message}` + } +} + function simplifyErrorMessages(report) { if (report.exception && report.exception.values) { report.exception.values.forEach(item => { From 18a8a211799645886efeaba875b5aa8f80773e7f Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 30 Apr 2018 14:35:47 -0700 Subject: [PATCH 66/79] Blacklist problematic shopify iFrame --- app/scripts/contentscript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index dbf1c6d4c..ddf1a9432 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -174,6 +174,7 @@ function blacklistedDomainCheck () { 'uscourts.gov', 'dropbox.com', 'webbyawards.com', + 'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html', ] var currentUrl = window.location.href var currentRegex From bce7adac8528b71402d515374983b811a56ddf57 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Apr 2018 14:46:53 -0700 Subject: [PATCH 67/79] ui - actions - importNewAccount - use async and pify for cleaner syntax --- ui/app/actions.js | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 62875c629..e817fa907 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1,4 +1,5 @@ const abi = require('human-standard-token-abi') +const pify = require('pify') const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url') const { getTokenAddressFromTokenObject } = require('./util') const ethUtil = require('ethereumjs-util') @@ -523,31 +524,26 @@ function addNewKeyring (type, opts) { } function importNewAccount (strategy, args) { - return (dispatch) => { - dispatch(actions.showLoadingIndication('This may take a while, be patient.')) - log.debug(`background.importAccountWithStrategy`) - return new Promise((resolve, reject) => { - background.importAccountWithStrategy(strategy, args, (err) => { - if (err) { - dispatch(actions.displayWarning(err.message)) - return reject(err) - } - log.debug(`background.getState`) - background.getState((err, newState) => { - dispatch(actions.hideLoadingIndication()) - if (err) { - dispatch(actions.displayWarning(err.message)) - return reject(err) - } - dispatch(actions.updateMetamaskState(newState)) - dispatch({ - type: actions.SHOW_ACCOUNT_DETAIL, - value: newState.selectedAddress, - }) - resolve(newState) - }) - }) + return async (dispatch) => { + let newState + dispatch(actions.showLoadingIndication('This may take a while, please be patient.')) + try { + log.debug(`background.importAccountWithStrategy`) + await pify(background.importAccountWithStrategy).call(background, strategy, args) + log.debug(`background.getState`) + newState = await pify(background.getState).call(background) + } catch (err) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(err.message)) + return + } + dispatch(actions.hideLoadingIndication()) + dispatch(actions.updateMetamaskState(newState)) + dispatch({ + type: actions.SHOW_ACCOUNT_DETAIL, + value: newState.selectedAddress, }) + return newState } } From 5ec631cad34a31a1268c990f0b952453ce97090b Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Mon, 30 Apr 2018 18:07:25 -0400 Subject: [PATCH 68/79] Handle Promise rejections when importing accounts (#4142) * Silently catch import failures since errors exist in Redux state * Add comment about no-op catch statement --- mascara/src/app/first-time/import-account-screen.js | 4 ++++ old-ui/app/accounts/import/json.js | 2 ++ old-ui/app/accounts/import/private-key.js | 2 ++ ui/app/components/pages/create-account/import-account/json.js | 2 ++ .../pages/create-account/import-account/private-key.js | 2 ++ 5 files changed, 12 insertions(+) diff --git a/mascara/src/app/first-time/import-account-screen.js b/mascara/src/app/first-time/import-account-screen.js index ab0aca0f0..555a26386 100644 --- a/mascara/src/app/first-time/import-account-screen.js +++ b/mascara/src/app/first-time/import-account-screen.js @@ -70,10 +70,14 @@ class ImportAccountScreen extends Component { switch (this.state.selectedOption) { case OPTIONS.JSON_FILE: return importNewAccount('JSON File', [ jsonFile, password ]) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() .then(next) case OPTIONS.PRIVATE_KEY: default: return importNewAccount('Private Key', [ privateKey ]) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() .then(next) } } diff --git a/old-ui/app/accounts/import/json.js b/old-ui/app/accounts/import/json.js index 3ee3b95df..8388f17a7 100644 --- a/old-ui/app/accounts/import/json.js +++ b/old-ui/app/accounts/import/json.js @@ -96,6 +96,8 @@ class JsonImportSubview extends Component { } this.props.importNewAccount([ fileContents, password ]) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() } } diff --git a/old-ui/app/accounts/import/private-key.js b/old-ui/app/accounts/import/private-key.js index 105191105..92e42549d 100644 --- a/old-ui/app/accounts/import/private-key.js +++ b/old-ui/app/accounts/import/private-key.js @@ -64,4 +64,6 @@ PrivateKeyImportView.prototype.createNewKeychain = function () { const input = document.getElementById('private-key-box') const privateKey = input.value this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ])) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() } diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js index 946907a47..0a3314b2a 100644 --- a/ui/app/components/pages/create-account/import-account/json.js +++ b/ui/app/components/pages/create-account/import-account/json.js @@ -105,6 +105,8 @@ class JsonImportSubview extends Component { } this.props.importNewJsonAccount([ fileContents, password ]) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() } } diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js index c77612ea4..df7ac910a 100644 --- a/ui/app/components/pages/create-account/import-account/private-key.js +++ b/ui/app/components/pages/create-account/import-account/private-key.js @@ -91,5 +91,7 @@ PrivateKeyImportView.prototype.createNewKeychain = function () { const { importNewAccount, history } = this.props importNewAccount('Private Key', [ privateKey ]) + // JS runtime requires caught rejections but failures are handled by Redux + .catch() .then(() => history.push(DEFAULT_ROUTE)) } From ffda954add95fc17049cd0f5386952dabfc4168b Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 30 Apr 2018 18:28:34 -0700 Subject: [PATCH 69/79] Fix styling of the app spinner --- ui/app/app.js | 3 +- ui/app/components/buy-button-subview.js | 2 +- ui/app/components/loading-screen/index.js | 2 + .../loading-screen.component.js} | 13 ++-- ui/app/components/pending-tx/index.js | 2 +- ui/app/components/spinner/index.js | 2 + .../components/spinner/spinner.component.js | 78 +++++++++++++++++++ ui/app/conf-tx.js | 2 +- .../css/itcss/components/loading-overlay.scss | 21 +++++ 9 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 ui/app/components/loading-screen/index.js rename ui/app/components/{loading.js => loading-screen/loading-screen.component.js} (71%) create mode 100644 ui/app/components/spinner/index.js create mode 100644 ui/app/components/spinner/spinner.component.js diff --git a/ui/app/app.js b/ui/app/app.js index 0b38b1326..f09d7cf79 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -29,7 +29,7 @@ const AddTokenPage = require('./components/pages/add-token') const CreateAccountPage = require('./components/pages/create-account') const NoticeScreen = require('./components/pages/notice') -const Loading = require('./components/loading') +const Loading = require('./components/loading-screen') const NetworkIndicator = require('./components/network') const Identicon = require('./components/identicon') const ReactCSSTransitionGroup = require('react-addons-css-transition-group') @@ -144,6 +144,7 @@ class App extends Component { (isLoading || isLoadingNetwork) && h(Loading, { loadingMessage: loadMessage, + fullScreen: true, }), // content diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js index fda7c3e17..c6957d2aa 100644 --- a/ui/app/components/buy-button-subview.js +++ b/ui/app/components/buy-button-subview.js @@ -6,7 +6,7 @@ const connect = require('react-redux').connect const actions = require('../actions') const CoinbaseForm = require('./coinbase-form') const ShapeshiftForm = require('./shapeshift-form') -const Loading = require('./loading') +const Loading = require('./loading-screen') const AccountPanel = require('./account-panel') const RadioList = require('./custom-radio-list') const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util') diff --git a/ui/app/components/loading-screen/index.js b/ui/app/components/loading-screen/index.js new file mode 100644 index 000000000..191d953f7 --- /dev/null +++ b/ui/app/components/loading-screen/index.js @@ -0,0 +1,2 @@ +const LoadingScreen = require('./loading-screen.component') +module.exports = LoadingScreen diff --git a/ui/app/components/loading.js b/ui/app/components/loading-screen/loading-screen.component.js similarity index 71% rename from ui/app/components/loading.js rename to ui/app/components/loading-screen/loading-screen.component.js index b9afc550f..bce2a4aac 100644 --- a/ui/app/components/loading.js +++ b/ui/app/components/loading-screen/loading-screen.component.js @@ -2,8 +2,9 @@ const { Component } = require('react') const h = require('react-hyperscript') const PropTypes = require('prop-types') const classnames = require('classnames') +const Spinner = require('../spinner') -class LoadingIndicator extends Component { +class LoadingScreen extends Component { renderMessage () { const { loadingMessage } = this.props return loadingMessage && h('span', loadingMessage) @@ -14,9 +15,9 @@ class LoadingIndicator extends Component { h('.loading-overlay', { className: classnames({ 'loading-overlay--full-screen': this.props.fullScreen }), }, [ - h('.flex-center.flex-column', [ - h('img', { - src: 'images/loading.svg', + h('.loading-overlay__container', [ + h(Spinner, { + color: '#F7C06C', }), this.renderMessage(), @@ -26,9 +27,9 @@ class LoadingIndicator extends Component { } } -LoadingIndicator.propTypes = { +LoadingScreen.propTypes = { loadingMessage: PropTypes.string, fullScreen: PropTypes.bool, } -module.exports = LoadingIndicator +module.exports = LoadingScreen diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js index 6ee83ba7e..ace52748c 100644 --- a/ui/app/components/pending-tx/index.js +++ b/ui/app/components/pending-tx/index.js @@ -12,7 +12,7 @@ const util = require('../../util') const ConfirmSendEther = require('./confirm-send-ether') const ConfirmSendToken = require('./confirm-send-token') const ConfirmDeployContract = require('./confirm-deploy-contract') -const Loading = require('../loading') +const Loading = require('../loading-screen') const TX_TYPES = { DEPLOY_CONTRACT: 'deploy_contract', diff --git a/ui/app/components/spinner/index.js b/ui/app/components/spinner/index.js new file mode 100644 index 000000000..9589efcf0 --- /dev/null +++ b/ui/app/components/spinner/index.js @@ -0,0 +1,2 @@ +const Spinner = require('./spinner.component') +module.exports = Spinner diff --git a/ui/app/components/spinner/spinner.component.js b/ui/app/components/spinner/spinner.component.js new file mode 100644 index 000000000..b9a2eb52a --- /dev/null +++ b/ui/app/components/spinner/spinner.component.js @@ -0,0 +1,78 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const Spinner = ({ className = '', color = '#000000' }) => { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ) +} + +Spinner.propTypes = { + className: PropTypes.string, + color: PropTypes.string, +} + +module.exports = Spinner diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index b71538e31..fb38aaa76 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -13,7 +13,7 @@ const SignatureRequest = require('./components/signature-request') // const PendingMsg = require('./components/pending-msg') // const PendingPersonalMsg = require('./components/pending-personal-msg') // const PendingTypedMsg = require('./components/pending-typed-msg') -const Loading = require('./components/loading') +const Loading = require('./components/loading-screen') const { DEFAULT_ROUTE } = require('./routes') module.exports = compose( diff --git a/ui/app/css/itcss/components/loading-overlay.scss b/ui/app/css/itcss/components/loading-overlay.scss index a92fffec5..c18b7fa59 100644 --- a/ui/app/css/itcss/components/loading-overlay.scss +++ b/ui/app/css/itcss/components/loading-overlay.scss @@ -26,4 +26,25 @@ width: 100vw; margin-top: 0; } + + &__container { + position: absolute; + top: 33%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + &__message { + margin-top: 32px; + font-weight: 400; + font-size: 20px; + color: $manatee; + } +} + +.spinner { + height: 58px; + width: 58px; } From 0e48ddfceb0e0ac4b271b1d946c567539c8d4432 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 1 May 2018 11:16:35 -0700 Subject: [PATCH 70/79] ui - actions - importNewAccount - rethrow err on failure --- ui/app/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index e817fa907..f060e40bd 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -535,7 +535,7 @@ function importNewAccount (strategy, args) { } catch (err) { dispatch(actions.hideLoadingIndication()) dispatch(actions.displayWarning(err.message)) - return + throw err } dispatch(actions.hideLoadingIndication()) dispatch(actions.updateMetamaskState(newState)) From 62bf76db53cf0702739d2735edfe8ffcb142b7c2 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Tue, 1 May 2018 13:57:14 -0700 Subject: [PATCH 71/79] fix - getTxsByMetaData check if the key is in the object not if the value is truthy --- app/scripts/controllers/transactions/tx-state-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 53428c333..380214c1d 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -262,7 +262,7 @@ class TransactionStateManager extends EventEmitter { */ getTxsByMetaData (key, value, txList = this.getTxList()) { return txList.filter((txMeta) => { - if (txMeta.txParams[key]) { + if (key in txMeta.txParams) { return txMeta.txParams[key] === value } else { return txMeta[key] === value From a45cb754358ff798dce25fa0b44d6b182abc7692 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Tue, 1 May 2018 13:57:43 -0700 Subject: [PATCH 72/79] transactions - add a nonce check utility for ui use --- app/scripts/controllers/transactions/index.js | 15 ++++++++++ app/scripts/metamask-controller.js | 1 + test/unit/tx-controller-test.js | 30 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 541f1db73..a1588cfef 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -111,6 +111,21 @@ class TransactionController extends EventEmitter { this.txStateManager.wipeTransactions(address) } + /** + Check if a txMeta in the list with the same nonce has been confirmed in a block + if the txParams dont have a nonce will return false + @returns {boolean} weather the nonce has been used in a transaction confirmed in a block + @param {object} txMeta - the txMeta object + */ + async isNonceTaken (txMeta) { + const { from, nonce } = txMeta.txParams + if ('nonce' in txMeta.txParams) { + const sameNonceTxList = this.txStateManager.getFilteredTxList({from, nonce, status: 'confirmed'}) + return (sameNonceTxList.length >= 1) + } + return false + } + /** add a new unapproved transaction to the pipeline diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c4a73d8ea..a90acb4d5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -382,6 +382,7 @@ module.exports = class MetamaskController extends EventEmitter { updateTransaction: nodeify(txController.updateTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), retryTransaction: nodeify(this.retryTransaction, this), + isNonceTaken: nodeify(txController.isNonceTaken, txController), // messageManager signMessage: nodeify(this.signMessage, this), diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 0b5c7226a..ddd921652 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -40,6 +40,36 @@ describe('Transaction Controller', function () { txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }) }) + describe('#isNonceTaken', function () { + it('should return true', function (done) { + txController.txStateManager._saveTxList([ + { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + { id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + ]) + txController.isNonceTaken({txParams: {nonce:0, from:'0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'}}) + .then((isNonceTaken) => { + assert(isNonceTaken) + done() + }).catch(done) + + }) + it('should return false', function (done) { + txController.txStateManager._saveTxList([ + { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {nonce: 0, from: '0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'} }, + ]) + + txController.isNonceTaken({txParams: {nonce:0, from:'0x8ACCE2391C0d510a6C5E5D8f819A678F79B7E675'}}) + .then((isNonceTaken) => { + assert(!isNonceTaken) + done() + }).catch(done) + + }) + }) + describe('#getState', function () { it('should return a state object with the right keys and datat types', function () { const exposedState = txController.getState() From 4a54deadf9c4d947407eff61d15a1895a6aecc3a Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 1 May 2018 14:28:33 -0700 Subject: [PATCH 73/79] Adds css-loader and file-loader needed for storybook integration --- package-lock.json | 3116 +++++++++++++++++++++++++-------------------- package.json | 2 + 2 files changed, 1723 insertions(+), 1395 deletions(-) diff --git a/package-lock.json b/package-lock.json index 884f53ce8..dea791316 100644 --- a/package-lock.json +++ b/package-lock.json @@ -474,6 +474,11 @@ "repeat-string": "1.6.1" } }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -602,24 +607,11 @@ "normalize-path": "2.1.1" } }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", - "requires": { - "buffer-equal": "1.0.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, "are-we-there-yet": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", @@ -650,27 +642,11 @@ "arr-flatten": "1.1.0" } }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", - "requires": { - "make-iterator": "1.0.0" - } - }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, - "arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", - "requires": { - "make-iterator": "1.0.0" - } - }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", @@ -681,11 +657,6 @@ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" - }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -717,37 +688,6 @@ "es-abstract": "1.10.0" } }, - "array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", - "requires": { - "array-slice": "1.1.0", - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, "array-map": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", @@ -760,28 +700,6 @@ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", "dev": true }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" - }, - "array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", - "requires": { - "default-compare": "1.0.0", - "get-value": "2.0.6", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -881,17 +799,6 @@ "lodash": "4.17.4" } }, - "async-done": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.2.4.tgz", - "integrity": "sha512-mxc+yISkb0vjsuvG3dJCIZXzRWjKndQ9Zo9zNDJ1K2wh9eP0E0oGmOWm+4cFOvW4dA0tGFImTW5tQJHCtn1kIQ==", - "requires": { - "end-of-stream": "1.4.0", - "once": "1.4.0", - "process-nextick-args": "1.0.7", - "stream-exhaust": "1.0.2" - } - }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -920,14 +827,6 @@ "resolved": "https://registry.npmjs.org/async-reduce/-/async-reduce-0.0.1.tgz", "integrity": "sha1-sja183bW+uOBze2QBqp/LHOxfzE=" }, - "async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", - "requires": { - "async-done": "1.2.4" - } - }, "async.queue": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz", @@ -995,7 +894,8 @@ "atob": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", + "dev": true }, "autoprefixer": { "version": "8.1.0", @@ -2045,22 +1945,6 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, - "bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", - "requires": { - "arr-filter": "1.1.2", - "arr-flatten": "1.1.0", - "arr-map": "2.0.2", - "array-each": "1.0.1", - "array-initial": "1.1.0", - "array-last": "1.3.0", - "async-done": "1.2.4", - "async-settle": "1.0.0", - "now-and-later": "2.0.0" - } - }, "backbone": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.3.3.tgz", @@ -2106,6 +1990,7 @@ "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, "requires": { "cache-base": "1.0.1", "class-utils": "0.3.6", @@ -2119,7 +2004,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -2840,11 +2726,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" - }, "buffer-fill": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-0.1.1.tgz", @@ -2928,6 +2809,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, "requires": { "collection-visit": "1.0.0", "component-emitter": "1.2.1", @@ -2943,7 +2825,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -3035,11 +2918,32 @@ } } }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000808", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.30" + } + } + } + }, "caniuse-db": { "version": "1.0.30000808", "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000808.tgz", - "integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs=", - "dev": true + "integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs=" }, "caniuse-lite": { "version": "1.0.30000786", @@ -3212,10 +3116,19 @@ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==" }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "requires": { + "chalk": "1.1.3" + } + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, "requires": { "arr-union": "3.1.0", "define-property": "0.2.5", @@ -3227,6 +3140,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -3235,6 +3149,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -3243,6 +3158,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -3253,6 +3169,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -3261,6 +3178,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -3271,6 +3189,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -3280,12 +3199,14 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -3376,7 +3297,8 @@ "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true }, "clone-regexp": { "version": "1.0.0", @@ -3400,12 +3322,14 @@ "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true }, "cloneable-readable": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", + "dev": true, "requires": { "inherits": "2.0.3", "process-nextick-args": "1.0.7", @@ -3417,6 +3341,14 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "requires": { + "q": "1.5.1" + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3443,30 +3375,11 @@ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=" }, - "collection-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", - "requires": { - "arr-map": "2.0.2", - "for-own": "1.0.0", - "make-iterator": "1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } - } - } - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, "requires": { "map-visit": "1.0.0", "object-visit": "1.0.1" @@ -3592,6 +3505,23 @@ } } }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "requires": { + "color": "0.11.4", + "css-color-names": "0.0.4", + "has": "1.0.1" + }, + "dependencies": { + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" + } + } + }, "colors": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", @@ -3690,7 +3620,8 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true }, "component-inherit": { "version": "0.0.3", @@ -3820,16 +3751,8 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "copy-props": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.1.tgz", - "integrity": "sha1-Zl/DIEbKhKiYq6o8WUXn8kjMugA=", - "requires": { - "each-props": "1.3.1", - "is-plain-object": "2.0.4" - } + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true }, "copy-to-clipboard": { "version": "3.0.8", @@ -4068,6 +3991,68 @@ "integrity": "sha1-XQVI+iVkVu3kqaDCrHqxnT6xrYE=", "dev": true }, + "css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "requires": { + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.0", + "cssnano": "3.10.0", + "icss-utils": "2.1.0", + "loader-utils": "1.1.0", + "lodash.camelcase": "4.3.0", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-modules-extract-imports": "1.2.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0", + "postcss-value-parser": "3.3.0", + "source-list-map": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "css-rule-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/css-rule-stream/-/css-rule-stream-1.1.0.tgz", @@ -4128,6 +4113,28 @@ "nth-check": "1.0.1" } }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "requires": { + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "requires": { + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" + } + } + } + }, "css-tokenize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/css-tokenize/-/css-tokenize-1.0.1.tgz", @@ -4179,6 +4186,119 @@ "through": "2.3.8" } }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=" + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "requires": { + "autoprefixer": "6.7.7", + "decamelize": "1.2.0", + "defined": "1.0.0", + "has": "1.0.1", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-calc": "5.3.1", + "postcss-colormin": "2.2.2", + "postcss-convert-values": "2.6.1", + "postcss-discard-comments": "2.0.4", + "postcss-discard-duplicates": "2.1.0", + "postcss-discard-empty": "2.1.0", + "postcss-discard-overridden": "0.1.1", + "postcss-discard-unused": "2.2.3", + "postcss-filter-plugins": "2.0.2", + "postcss-merge-idents": "2.1.7", + "postcss-merge-longhand": "2.0.2", + "postcss-merge-rules": "2.1.2", + "postcss-minify-font-values": "1.0.5", + "postcss-minify-gradients": "1.0.5", + "postcss-minify-params": "1.2.2", + "postcss-minify-selectors": "2.1.1", + "postcss-normalize-charset": "1.1.1", + "postcss-normalize-url": "3.0.8", + "postcss-ordered-values": "2.2.3", + "postcss-reduce-idents": "2.4.0", + "postcss-reduce-initial": "1.0.1", + "postcss-reduce-transforms": "1.0.4", + "postcss-svgo": "2.1.6", + "postcss-unique-selectors": "2.0.2", + "postcss-value-parser": "3.3.0", + "postcss-zindex": "2.2.0" + }, + "dependencies": { + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000808", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.30" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "requires": { + "clap": "1.2.3", + "source-map": "0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, "cssom": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", @@ -4491,26 +4611,6 @@ "integrity": "sha1-iQa/nlJaT78bIDsq/LRkAkmCEhk=", "dev": true }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "requires": { - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" - }, "deferred-leveldown": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", @@ -4532,6 +4632,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "1.0.2" } @@ -4664,11 +4765,6 @@ "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", "dev": true }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" - }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -5067,15 +5163,6 @@ "stream-shift": "1.0.0" } }, - "each-props": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.1.tgz", - "integrity": "sha1-/BOPUeOid0KG1IWOAtbn3kYt4Vg=", - "requires": { - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0" - } - }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -6975,6 +7062,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, "requires": { "homedir-polyfill": "1.0.1" } @@ -7025,6 +7113,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "0.1.1" } @@ -7167,6 +7256,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" + }, "faye-websocket": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.7.3.tgz", @@ -7230,6 +7324,27 @@ "object-assign": "4.1.1" } }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "requires": { + "loader-utils": "1.1.0", + "schema-utils": "0.4.5" + }, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + } + } + }, "file-tree": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-tree/-/file-tree-1.0.0.tgz", @@ -7324,338 +7439,6 @@ "pinkie-promise": "2.0.1" } }, - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "requires": { - "detect-file": "1.0.0", - "is-glob": "3.1.0", - "micromatch": "3.1.9", - "resolve-dir": "1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.1", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "micromatch": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz", - "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - } - } - }, - "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", - "requires": { - "expand-tilde": "2.0.2", - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0", - "object.pick": "1.3.0", - "parse-filepath": "1.0.2" - } - }, "fireworm": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/fireworm/-/fireworm-0.7.1.tgz", @@ -7695,11 +7478,6 @@ "readable-stream": "2.3.3" } }, - "flagged-respawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", - "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=" - }, "flat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/flat/-/flat-1.0.0.tgz", @@ -7750,15 +7528,6 @@ "integrity": "sha1-N4tRKNbQtVSosvFqTKPhq5ZJ8A4=", "dev": true }, - "flush-write-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", - "integrity": "sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, "follow-redirects": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", @@ -7828,6 +7597,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, "requires": { "map-cache": "0.2.2" } @@ -7885,15 +7655,6 @@ "rimraf": "2.6.2" } }, - "fs-mkdirp-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", - "requires": { - "graceful-fs": "4.1.11", - "through2": "2.0.3" - } - }, "fs-promise": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-2.0.3.tgz", @@ -9399,7 +9160,8 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true }, "getpass": { "version": "0.1.7", @@ -9555,58 +9317,6 @@ "is-glob": "2.0.1" } }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "requires": { - "extend": "3.0.1", - "glob": "7.1.2", - "glob-parent": "3.1.0", - "is-negated-glob": "1.0.0", - "ordered-read-streams": "1.0.1", - "pumpify": "1.3.5", - "readable-stream": "2.3.3", - "remove-trailing-separator": "1.1.0", - "to-absolute-glob": "2.0.2", - "unique-stream": "2.2.1" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "2.1.1" - } - } - } - }, - "glob-watcher": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-4.0.0.tgz", - "integrity": "sha1-nmOo/25h6TLebMLK7OUHGm1zcyk=", - "requires": { - "async-done": "1.2.4", - "chokidar": "1.7.0", - "just-debounce": "1.0.0", - "object.defaults": "1.1.0" - } - }, "glob2base": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", @@ -9629,6 +9339,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, "requires": { "global-prefix": "1.0.2", "is-windows": "1.0.2", @@ -9639,6 +9350,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, "requires": { "expand-tilde": "2.0.2", "homedir-polyfill": "1.0.1", @@ -9752,75 +9464,6 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, - "gulp": { - "version": "github:gulpjs/gulp#71c094a51c7972d26f557899ddecab0210ef3776", - "requires": { - "glob-watcher": "4.0.0", - "gulp-cli": "2.0.1", - "undertaker": "1.2.0", - "vinyl-fs": "3.0.2" - }, - "dependencies": { - "gulp-cli": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz", - "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", - "requires": { - "ansi-colors": "1.0.1", - "archy": "1.0.0", - "array-sort": "1.0.0", - "color-support": "1.1.3", - "concat-stream": "1.6.0", - "copy-props": "2.0.1", - "fancy-log": "1.3.2", - "gulplog": "1.0.0", - "interpret": "1.1.0", - "isobject": "3.0.1", - "liftoff": "2.5.0", - "matchdep": "2.0.0", - "mute-stdout": "1.0.0", - "pretty-hrtime": "1.0.3", - "replace-homedir": "1.0.0", - "semver-greatest-satisfied-range": "1.1.0", - "v8flags": "3.0.2", - "yargs": "7.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "requires": { - "camelcase": "3.0.0" - } - } - } - }, "gulp-autoprefixer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-5.0.0.tgz", @@ -11373,7 +11016,8 @@ "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true }, "has-to-string-tag-x": { "version": "1.4.1", @@ -11392,6 +11036,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, "requires": { "get-value": "2.0.6", "has-values": "1.0.0", @@ -11401,7 +11046,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -11409,6 +11055,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, "requires": { "is-number": "3.0.0", "kind-of": "4.0.0" @@ -11418,6 +11065,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -11426,6 +11074,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -11436,6 +11085,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -11551,6 +11201,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, "requires": { "parse-passwd": "1.0.0" } @@ -11560,6 +11211,11 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=" + }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", @@ -11853,6 +11509,19 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "requires": { + "postcss": "6.0.19" + } + }, "idb-global": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/idb-global/-/idb-global-2.1.0.tgz", @@ -11965,8 +11634,7 @@ "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" }, "indexof": { "version": "0.0.1", @@ -11997,7 +11665,8 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true }, "inject-css": { "version": "0.1.1", @@ -12231,19 +11900,16 @@ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=" }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "requires": { - "is-relative": "1.0.0", - "is-windows": "1.0.2" - } + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { "kind-of": "6.0.2" }, @@ -12251,7 +11917,8 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -12310,6 +11977,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { "kind-of": "6.0.2" }, @@ -12317,7 +11985,8 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -12335,6 +12004,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "1.0.0", "is-data-descriptor": "1.0.0", @@ -12344,7 +12014,8 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -12443,11 +12114,6 @@ "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" - }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -12580,14 +12246,6 @@ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "requires": { - "is-unc-path": "1.0.0" - } - }, "is-resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.1.tgz", @@ -12630,6 +12288,14 @@ "integrity": "sha1-i1IMhfrnolM4LUsCZS4EVXbhO7g=", "dev": true }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "requires": { + "html-comment-regex": "1.1.1" + } + }, "is-symbol": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", @@ -12649,24 +12315,11 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "requires": { - "unc-path-regex": "0.1.2" - } - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, - "is-valid-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" - }, "is-whitespace-character": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", @@ -12675,7 +12328,8 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true }, "is-word-character": { "version": "1.0.1", @@ -13463,11 +13117,6 @@ } } }, - "just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" - }, "just-extend": { "version": "1.1.27", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", @@ -13824,31 +13473,15 @@ } } }, - "last-run": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", - "requires": { - "default-resolution": "2.0.0", - "es6-weak-map": "2.0.2" - } - }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dev": true, "requires": { "set-getter": "0.1.0" } }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "requires": { - "readable-stream": "2.3.3" - } - }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -13909,14 +13542,6 @@ } } }, - "lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", - "requires": { - "flush-write-stream": "1.0.2" - } - }, "left-pad": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz", @@ -14213,21 +13838,6 @@ } } }, - "liftoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", - "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", - "requires": { - "extend": "3.0.1", - "findup-sync": "2.0.0", - "fined": "1.1.0", - "flagged-respawn": "1.0.0", - "is-plain-object": "2.0.4", - "object.map": "1.0.1", - "rechoir": "0.6.2", - "resolve": "1.4.0" - } - }, "listr": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/listr/-/listr-0.13.0.tgz", @@ -14559,6 +14169,11 @@ "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -14697,6 +14312,11 @@ "lodash.escape": "3.2.0" } }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, "lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -15060,6 +14680,11 @@ "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.0.tgz", "integrity": "sha1-tlul/LNJopkkyOMz98alVi8uSEI=" }, + "macaddress": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=" + }, "mailcomposer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", @@ -15160,14 +14785,6 @@ "make-error": "1.3.4" } }, - "make-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz", - "integrity": "sha1-V7713IXSOSO6I3ZzJNjo+PPZaUs=", - "requires": { - "kind-of": "3.2.2" - } - }, "map-async": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/map-async/-/map-async-0.1.1.tgz", @@ -15176,7 +14793,8 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true }, "map-obj": { "version": "1.0.1", @@ -15193,6 +14811,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, "requires": { "object-visit": "1.0.1" } @@ -15208,313 +14827,6 @@ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "dev": true }, - "matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", - "requires": { - "findup-sync": "2.0.0", - "micromatch": "3.1.9", - "resolve": "1.4.0", - "stack-trace": "0.0.10" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.1", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "micromatch": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz", - "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - } - } - }, "matcher-collection": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.0.5.tgz", @@ -15524,6 +14836,11 @@ "minimatch": "3.0.4" } }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=" + }, "mathml-tag-names": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.0.1.tgz", @@ -16015,6 +15332,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", + "dev": true, "requires": { "for-in": "1.0.2", "is-extendable": "1.0.1" @@ -16024,6 +15342,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -16213,11 +15532,6 @@ "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=", "dev": true }, - "mute-stdout": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.0.tgz", - "integrity": "sha1-WzLqB+tDyd7WEwQ0z5JvRrKn/U0=" - }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -16915,14 +16229,6 @@ "sort-keys": "2.0.0" } }, - "now-and-later": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", - "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", - "requires": { - "once": "1.4.0" - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -18827,6 +18133,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, "requires": { "copy-descriptor": "0.1.1", "define-property": "0.2.5", @@ -18837,6 +18144,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -18845,6 +18153,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" } @@ -18853,6 +18162,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" } @@ -18861,6 +18171,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -18870,7 +18181,8 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } } @@ -18896,6 +18208,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, "requires": { "isobject": "3.0.1" }, @@ -18903,7 +18216,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -18911,6 +18225,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, "requires": { "define-properties": "1.1.2", "function-bind": "1.1.1", @@ -18918,32 +18233,6 @@ "object-keys": "1.0.11" } }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "requires": { - "array-each": "1.0.1", - "array-slice": "1.1.0", - "for-own": "1.0.0", - "isobject": "3.0.1" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, "object.entries": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", @@ -18956,25 +18245,6 @@ "has": "1.0.1" } }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "requires": { - "for-own": "1.0.0", - "make-iterator": "1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } - } - } - }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -18988,6 +18258,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, "requires": { "isobject": "3.0.1" }, @@ -18995,26 +18266,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "object.reduce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", - "requires": { - "for-own": "1.0.0", - "make-iterator": "1.0.0" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "1.0.2" - } + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -19189,14 +18442,6 @@ } } }, - "ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "requires": { - "readable-stream": "2.3.3" - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -19395,16 +18640,6 @@ "is-hexadecimal": "1.0.1" } }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "requires": { - "is-absolute": "1.0.0", - "map-cache": "0.2.2", - "path-root": "0.1.1" - } - }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -19436,7 +18671,8 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true }, "parse5": { "version": "3.0.3", @@ -19482,7 +18718,8 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true }, "path-browserify": { "version": "0.0.0", @@ -19492,7 +18729,8 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true }, "path-exists": { "version": "2.1.0", @@ -19548,19 +18786,6 @@ } } }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "requires": { - "path-root-regex": "0.1.2" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -19932,7 +19157,8 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true }, "post-message-stream": { "version": "3.0.0", @@ -19990,6 +19216,364 @@ } } }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "requires": { + "postcss": "5.2.18", + "postcss-message-helpers": "2.0.0", + "reduce-css-calc": "1.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "requires": { + "colormin": "1.1.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "requires": { + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "requires": { + "postcss": "5.2.18", + "uniqid": "4.1.1" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "postcss-less": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-0.14.0.tgz", @@ -20040,6 +19624,617 @@ "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", "dev": true }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "requires": { + "browserslist": "1.7.7", + "caniuse-api": "1.6.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3", + "vendors": "1.0.2" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "1.0.30000808", + "electron-to-chromium": "1.3.30" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=" + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "requires": { + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "requires": { + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "uniqs": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "requires": { + "alphanum-sort": "1.0.2", + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", + "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "requires": { + "postcss": "6.0.19" + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.19" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.19" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "requires": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.19" + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "requires": { + "is-absolute-url": "2.1.0", + "normalize-url": "1.9.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "requires": { + "object-assign": "4.1.1", + "prepend-http": "1.0.4", + "query-string": "4.3.4", + "sort-keys": "1.1.2" + } + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "requires": { + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "requires": { + "is-plain-obj": "1.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "requires": { + "postcss": "5.2.18" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "postcss-reporter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-3.0.0.tgz", @@ -20141,7 +20336,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, "requires": { "flatten": "1.0.2", "indexes-of": "1.0.1", @@ -20151,8 +20345,7 @@ "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=" } } }, @@ -20201,11 +20394,135 @@ } } }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "requires": { + "is-svg": "2.1.0", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "svgo": "0.7.2" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "requires": { + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "postcss-value-parser": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "uniqs": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", @@ -20257,11 +20574,6 @@ "integrity": "sha1-zZApTVihyk6KXQ+5yCJZmIgazwA=", "dev": true }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" - }, "printf": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/printf/-/printf-0.2.5.tgz", @@ -21208,6 +21520,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, "requires": { "resolve": "1.4.0" } @@ -21258,6 +21571,38 @@ "dev": true, "optional": true }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "requires": { + "balanced-match": "0.4.2", + "math-expression-evaluator": "1.2.17", + "reduce-function-call": "1.0.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "requires": { + "balanced-match": "0.4.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + } + } + }, "redux": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", @@ -21320,6 +21665,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "dev": true, "requires": { "extend-shallow": "2.0.1" } @@ -21376,25 +21722,6 @@ "xtend": "4.0.1" } }, - "remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", - "requires": { - "is-buffer": "1.1.6", - "is-utf8": "0.2.1" - } - }, - "remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", - "requires": { - "remove-bom-buffer": "3.0.0", - "safe-buffer": "5.1.1", - "through2": "2.0.3" - } - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -21423,16 +21750,6 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" }, - "replace-homedir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", - "requires": { - "homedir-polyfill": "1.0.1", - "is-absolute": "1.0.0", - "remove-trailing-separator": "1.1.0" - } - }, "replaceall": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", @@ -21605,6 +21922,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, "requires": { "expand-tilde": "2.0.2", "global-modules": "1.0.0" @@ -21615,14 +21933,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" }, - "resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", - "requires": { - "value-or-function": "3.0.0" - } - }, "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", @@ -21631,7 +21941,8 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, "response-stream": { "version": "0.0.0", @@ -21855,8 +22166,34 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "requires": { + "ajv": "6.4.0", + "ajv-keywords": "3.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", + "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "requires": { + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1", + "uri-js": "3.0.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" + } + } }, "scoped-regex": { "version": "1.0.0", @@ -22026,14 +22363,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, - "semver-greatest-satisfied-range": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", - "requires": { - "sver-compat": "1.5.0" - } - }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", @@ -22086,6 +22415,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, "requires": { "to-object-path": "0.3.0" } @@ -22099,6 +22429,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -22447,6 +22778,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", + "dev": true, "requires": { "base": "0.11.2", "debug": "2.6.9", @@ -22462,6 +22794,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -22470,6 +22803,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -22478,6 +22812,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -22488,6 +22823,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -22496,6 +22832,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -22506,6 +22843,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -22515,12 +22853,14 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -22528,6 +22868,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, "requires": { "define-property": "1.0.0", "isobject": "3.0.1", @@ -22537,7 +22878,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -22545,6 +22887,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, "requires": { "kind-of": "3.2.2" } @@ -22824,6 +23167,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "dev": true, "requires": { "atob": "2.0.3", "decode-uri-component": "0.2.0", @@ -22850,7 +23194,8 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true }, "sparkles": { "version": "1.0.0", @@ -22900,6 +23245,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, "requires": { "extend-shallow": "3.0.2" }, @@ -22908,6 +23254,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, "requires": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" @@ -22917,6 +23264,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -22991,7 +23339,8 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true }, "state-toggle": { "version": "1.0.0", @@ -23036,6 +23385,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, "requires": { "define-property": "0.2.5", "object-copy": "0.1.0" @@ -23045,6 +23395,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -23053,6 +23404,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23061,6 +23413,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23071,6 +23424,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23079,6 +23433,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23089,6 +23444,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -23098,7 +23454,8 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -23257,11 +23614,6 @@ } } }, - "stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" - }, "stream-http": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", @@ -24034,21 +24386,47 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, - "sver-compat": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, "svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "requires": { + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3" + } + } + } + }, "sw-controller": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sw-controller/-/sw-controller-1.0.3.tgz", @@ -24636,15 +25014,6 @@ "xtend": "4.0.1" } }, - "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", - "requires": { - "through2": "2.0.3", - "xtend": "4.0.1" - } - }, "thunkify": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", @@ -24717,15 +25086,6 @@ "os-tmpdir": "1.0.2" } }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", - "requires": { - "is-absolute": "1.0.0", - "is-negated-glob": "1.0.0" - } - }, "to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", @@ -24751,6 +25111,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, "requires": { "kind-of": "3.2.2" } @@ -24759,6 +25120,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "dev": true, "requires": { "define-property": "0.2.5", "extend-shallow": "2.0.1", @@ -24769,6 +25131,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -24777,6 +25140,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -24785,6 +25149,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -24795,6 +25160,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -24803,6 +25169,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -24813,6 +25180,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -24822,7 +25190,8 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -24830,6 +25199,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, "requires": { "is-number": "3.0.0", "repeat-string": "1.6.1" @@ -24839,20 +25209,13 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { "kind-of": "3.2.2" } } } }, - "to-through": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", - "requires": { - "through2": "2.0.3" - } - }, "toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", @@ -25118,11 +25481,6 @@ } } }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" - }, "underscore": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", @@ -25146,27 +25504,6 @@ } } }, - "undertaker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", - "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", - "requires": { - "arr-flatten": "1.1.0", - "arr-map": "2.0.2", - "bach": "1.2.0", - "collection-map": "1.0.0", - "es6-weak-map": "2.0.2", - "last-run": "1.1.1", - "object.defaults": "1.1.0", - "object.reduce": "1.0.1", - "undertaker-registry": "1.0.1" - } - }, - "undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" - }, "unherit": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", @@ -25194,6 +25531,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, "requires": { "arr-union": "3.1.0", "get-value": "2.0.6", @@ -25205,6 +25543,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -25219,15 +25558,19 @@ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" }, - "unique-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", - "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "uniqid": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", "requires": { - "json-stable-stringify": "1.0.1", - "through2-filter": "2.0.0" + "macaddress": "0.2.8" } }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, "unist-util-is": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", @@ -25268,6 +25611,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, "requires": { "has-value": "0.3.1", "isobject": "3.0.1" @@ -25277,6 +25621,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, "requires": { "get-value": "2.0.6", "has-values": "0.1.4", @@ -25287,6 +25632,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, "requires": { "isarray": "1.0.0" } @@ -25296,12 +25642,14 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -25317,10 +25665,26 @@ "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==", "dev": true }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true }, "url": { "version": "0.11.0", @@ -25361,6 +25725,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", + "dev": true, "requires": { "define-property": "0.2.5", "isobject": "3.0.1", @@ -25371,6 +25736,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -25379,6 +25745,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -25387,6 +25754,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -25397,6 +25765,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -25405,6 +25774,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -25415,6 +25785,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -25424,12 +25795,14 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -25519,14 +25892,6 @@ "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==", "dev": true }, - "v8flags": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.2.tgz", - "integrity": "sha512-6sgSKoFw1UpUPd3cFdF7QGnrH6tDeBgW1F3v9gy8gLY0mlbiBXq8soy8aQpY6xeeCjH5K+JvC62Acp7gtl7wWA==", - "requires": { - "homedir-polyfill": "1.0.1" - } - }, "valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", @@ -25546,11 +25911,6 @@ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" }, - "value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" - }, "varint": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/varint/-/varint-4.0.1.tgz", @@ -25561,6 +25921,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "vendors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", + "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -25599,6 +25964,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, "requires": { "clone": "2.1.1", "clone-buffer": "1.0.0", @@ -25669,30 +26035,6 @@ } } }, - "vinyl-fs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.2.tgz", - "integrity": "sha512-AUSFda1OukBwuLPBTbyuO4IRWgfXmqC4UTW0f8xrCa8Hkv9oyIU+NSqBlgfOLZRoUt7cHdo75hKQghCywpIyIw==", - "requires": { - "fs-mkdirp-stream": "1.0.0", - "glob-stream": "6.1.0", - "graceful-fs": "4.1.11", - "is-valid-glob": "1.0.0", - "lazystream": "1.0.0", - "lead": "1.0.0", - "object.assign": "4.1.0", - "pumpify": "1.3.5", - "readable-stream": "2.3.3", - "remove-bom-buffer": "3.0.0", - "remove-bom-stream": "1.2.0", - "resolve-options": "1.1.0", - "through2": "2.0.3", - "to-through": "2.0.0", - "value-or-function": "3.0.0", - "vinyl": "2.1.0", - "vinyl-sourcemap": "1.1.0" - } - }, "vinyl-source-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz", @@ -25703,27 +26045,6 @@ "vinyl": "2.1.0" } }, - "vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", - "requires": { - "append-buffer": "1.0.2", - "convert-source-map": "1.5.1", - "graceful-fs": "4.1.11", - "normalize-path": "2.1.1", - "now-and-later": "2.0.0", - "remove-bom-buffer": "3.0.0", - "vinyl": "2.1.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" - } - } - }, "vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", @@ -27135,6 +27456,11 @@ "dev": true, "optional": true }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=" + }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", diff --git a/package.json b/package.json index 4528ca31b..dcb9984ad 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "classnames": "^2.2.5", "clone": "^2.1.1", "copy-to-clipboard": "^3.0.8", + "css-loader": "^0.28.11", "currency-formatter": "^1.4.2", "debounce": "^1.0.0", "debounce-stream": "^2.0.0", @@ -111,6 +112,7 @@ "extensionizer": "^1.0.0", "fast-json-patch": "^2.0.4", "fast-levenshtein": "^2.0.6", + "file-loader": "^1.1.11", "fuse.js": "^3.2.0", "gulp": "github:gulpjs/gulp#4.0", "gulp-autoprefixer": "^5.0.0", From a70da53340256f078c7c0183d8768624f727bf07 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 1 May 2018 19:22:41 -0700 Subject: [PATCH 74/79] Adds path and resolve-url-loader packages for storybook integration --- package-lock.json | 165 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + 2 files changed, 167 insertions(+) diff --git a/package-lock.json b/package-lock.json index dea791316..ad08dbd19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -419,6 +419,63 @@ "dev": true, "optional": true }, + "adjust-sourcemap-loader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-1.2.0.tgz", + "integrity": "sha512-958oaHHVEXMvsY7v7cC5gEkNIcoaAVIhZ4mBReYVZJOTP9IgKmzLjIOhTtzpLMu+qriXvLsVjJ155EeInp45IQ==", + "dev": true, + "requires": { + "assert": "1.4.1", + "camelcase": "1.2.1", + "loader-utils": "1.1.0", + "lodash.assign": "4.2.0", + "lodash.defaults": "3.1.2", + "object-path": "0.9.2", + "regex-parser": "2.2.9" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "lodash.defaults": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-3.1.2.tgz", + "integrity": "sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw=", + "dev": true, + "requires": { + "lodash.assign": "3.2.0", + "lodash.restparam": "3.6.1" + }, + "dependencies": { + "lodash.assign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz", + "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", + "dev": true, + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._createassigner": "3.1.1", + "lodash.keys": "3.1.2" + } + } + } + } + } + }, "aes-js": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-0.2.4.tgz", @@ -14200,6 +14257,12 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -18204,6 +18267,12 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" }, + "object-path": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", + "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -18721,6 +18790,24 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "dev": true, + "requires": { + "process": "0.11.10", + "util": "0.10.3" + }, + "dependencies": { + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + } + } + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", @@ -21670,6 +21757,12 @@ "extend-shallow": "2.0.1" } }, + "regex-parser": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.9.tgz", + "integrity": "sha512-VncXxOF6uFlYog5prG2j+e2UGJeam5MfNiJnB/qEgo4KTnMm2XrELCg4rNZ6IlaEUZnGlb8aB6lXowCRQtTkkA==", + "dev": true + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", @@ -21944,6 +22037,54 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "resolve-url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-2.3.0.tgz", + "integrity": "sha512-RaEUWgF/B6aTg9VKaOv2o6dfm5f75/lGh8S+SQwoMcBm48WkA2nhLR+V7KEawkxXjU4lLB16IVeHCe7F69nyVw==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "1.2.0", + "camelcase": "4.1.0", + "convert-source-map": "1.5.1", + "loader-utils": "1.1.0", + "lodash.defaults": "4.2.0", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.5.7", + "urix": "0.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "response-stream": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/response-stream/-/response-stream-0.0.0.tgz", @@ -21988,6 +22129,30 @@ "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", "dev": true }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "requires": { + "convert-source-map": "0.3.5", + "css": "2.2.1" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", diff --git a/package.json b/package.json index dcb9984ad..9aa9866d6 100644 --- a/package.json +++ b/package.json @@ -259,6 +259,7 @@ "node-sass": "^4.7.2", "nyc": "^11.0.3", "open": "0.0.5", + "path": "^0.12.7", "png-file-stream": "^1.0.0", "prompt": "^1.0.0", "qs": "^6.2.0", @@ -268,6 +269,7 @@ "react-test-renderer": "^15.6.2", "react-testutils-additions": "^15.2.0", "redux-test-utils": "^0.2.2", + "resolve-url-loader": "^2.3.0", "rimraf": "^2.6.2", "selenium-webdriver": "^3.5.0", "shell-parallel": "^1.0.3", From 2c95390229d1b082faa58ece143a641343adf9d2 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 1 May 2018 20:05:14 -0700 Subject: [PATCH 75/79] Adds sass-loader and style-loader packages for storybook integration --- package-lock.json | 121 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + 2 files changed, 123 insertions(+) diff --git a/package-lock.json b/package-lock.json index ad08dbd19..6a26300e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3357,6 +3357,35 @@ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "dev": true, + "requires": { + "for-own": "1.0.0", + "is-plain-object": "2.0.4", + "kind-of": "6.0.2", + "shallow-clone": "1.0.0" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "clone-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.0.tgz", @@ -14350,6 +14379,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, "lodash.template": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", @@ -15412,6 +15447,24 @@ } } }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "0.1.8", + "is-extendable": "0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -22328,6 +22381,32 @@ } } }, + "sass-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.0.1.tgz", + "integrity": "sha512-MeVVJFejJELlAbA7jrRchi88PGP6U9yIfqyiG+bBC4a9s2PX+ulJB9h8bbEohtPBfZmlLhNZ0opQM9hovRXvlw==", + "dev": true, + "requires": { + "clone-deep": "2.0.2", + "loader-utils": "1.1.0", + "lodash.tail": "4.1.1", + "neo-async": "2.5.0", + "pify": "3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + } + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -22629,6 +22708,25 @@ "nan": "2.8.0" } }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "dev": true, + "requires": { + "is-extendable": "0.1.1", + "kind-of": "5.1.0", + "mixin-object": "2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "shallow-copy": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", @@ -24031,6 +24129,29 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, + "style-loader": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", + "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "schema-utils": "0.4.5" + }, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + } + } + }, "style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", diff --git a/package.json b/package.json index 9aa9866d6..6ed667bd5 100644 --- a/package.json +++ b/package.json @@ -271,10 +271,12 @@ "redux-test-utils": "^0.2.2", "resolve-url-loader": "^2.3.0", "rimraf": "^2.6.2", + "sass-loader": "^7.0.1", "selenium-webdriver": "^3.5.0", "shell-parallel": "^1.0.3", "sinon": "^5.0.0", "source-map": "^0.7.2", + "style-loader": "^0.21.0", "stylelint-config-standard": "^18.2.0", "tape": "^4.5.1", "testem": "^2.0.0", From 65253e114f5aeb2a801925a988ff301e5af956d2 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 2 May 2018 11:05:39 -0700 Subject: [PATCH 76/79] Adds nsp check to CircleCI tests --- .circleci/config.yml | 15 +++ package-lock.json | 303 +++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 319 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ed731225..88a611af3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,6 +15,9 @@ workflows: - test-lint: requires: - prep-deps-npm + - test-deps: + requires: + - prep-deps-npm - test-e2e: requires: - prep-deps-npm @@ -43,6 +46,7 @@ workflows: - all-tests-pass: requires: - test-lint + - test-deps - test-unit - test-e2e - test-integration-mascara-chrome @@ -145,6 +149,17 @@ jobs: name: Test command: npm run lint + test-deps: + docker: + - image: circleci/node:8-browsers + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ .Revision }} + - run: + name: Test + command: npx nsp check + test-e2e: docker: - image: circleci/node:8-browsers diff --git a/package-lock.json b/package-lock.json index 6a26300e1..42420c7ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3313,6 +3313,32 @@ } } }, + "cli-table2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz", + "integrity": "sha1-LR738hig54biFFQFYtS9F3/jLZc=", + "dev": true, + "requires": { + "colors": "1.2.3", + "lodash": "3.10.1", + "string-width": "1.0.2" + }, + "dependencies": { + "colors": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz", + "integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==", + "dev": true, + "optional": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + }, "cli-truncate": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", @@ -4424,6 +4450,12 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "cvss": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cvss/-/cvss-1.0.2.tgz", + "integrity": "sha1-32fpK/EqeW9J6Sh5nI2zunS5/NY=", + "dev": true + }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -16286,6 +16318,12 @@ "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=", "dev": true }, + "nodesecurity-npm-utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nodesecurity-npm-utils/-/nodesecurity-npm-utils-6.0.0.tgz", + "integrity": "sha512-NLRle1woNaT2orR6fue2jNqkhxDTktgJj3sZxvR/8kp21pvOY7Gwlx5wvo0H8ZVPqdgd2nE2ADB9wDu5Cl8zNg==", + "dev": true + }, "nomnom": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", @@ -16365,6 +16403,250 @@ "set-blocking": "2.0.0" } }, + "nsp": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/nsp/-/nsp-3.2.1.tgz", + "integrity": "sha512-dLmGi7IGixJEHKetErIH460MYiYIzAoxuVsloZFu9e1p9U8K0yULx7YQ1+VzrjZbB+wqq67ES1SfOvKVb/qMDQ==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "cli-table2": "0.2.0", + "cvss": "1.0.2", + "https-proxy-agent": "2.2.1", + "inquirer": "3.3.0", + "nodesecurity-npm-utils": "6.0.0", + "semver": "5.4.1", + "wreck": "12.5.1", + "yargs": "9.0.1" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", + "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yargs": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", + "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } + } + } + }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", @@ -27827,6 +28109,27 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "wreck": { + "version": "12.5.1", + "resolved": "https://registry.npmjs.org/wreck/-/wreck-12.5.1.tgz", + "integrity": "sha512-l5DUGrc+yDyIflpty1x9XuMj1ehVjC/dTbF3/BasOO77xk0EdEa4M/DuOY8W88MQDAD0fEDqyjc8bkIMHd2E9A==", + "dev": true, + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, "write": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", diff --git a/package.json b/package.json index 6ed667bd5..49743e74e 100644 --- a/package.json +++ b/package.json @@ -257,6 +257,7 @@ "mocha-sinon": "^2.0.0", "nock": "^9.0.14", "node-sass": "^4.7.2", + "nsp": "^3.2.1", "nyc": "^11.0.3", "open": "0.0.5", "path": "^0.12.7", From 3e4f158b347c82d36a94ea82ef3d8a0b757063ec Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 2 May 2018 13:10:21 -0700 Subject: [PATCH 77/79] Pin chromedriver version to 2.36.0 --- package-lock.json | 1539 +++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 1360 insertions(+), 181 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42420c7ae..1389cb0e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -664,11 +664,24 @@ "normalize-path": "2.1.1" } }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "requires": { + "buffer-equal": "1.0.0" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, "are-we-there-yet": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", @@ -699,11 +712,27 @@ "arr-flatten": "1.1.0" } }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "requires": { + "make-iterator": "1.0.1" + } + }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "requires": { + "make-iterator": "1.0.1" + } + }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", @@ -714,6 +743,11 @@ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -745,6 +779,37 @@ "es-abstract": "1.10.0" } }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "requires": { + "array-slice": "1.1.0", + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, "array-map": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", @@ -757,6 +822,28 @@ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", "dev": true }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "requires": { + "default-compare": "1.0.0", + "get-value": "2.0.6", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -856,6 +943,17 @@ "lodash": "4.17.4" } }, + "async-done": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.2.4.tgz", + "integrity": "sha512-mxc+yISkb0vjsuvG3dJCIZXzRWjKndQ9Zo9zNDJ1K2wh9eP0E0oGmOWm+4cFOvW4dA0tGFImTW5tQJHCtn1kIQ==", + "requires": { + "end-of-stream": "1.4.0", + "once": "1.4.0", + "process-nextick-args": "1.0.7", + "stream-exhaust": "1.0.2" + } + }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -884,6 +982,14 @@ "resolved": "https://registry.npmjs.org/async-reduce/-/async-reduce-0.0.1.tgz", "integrity": "sha1-sja183bW+uOBze2QBqp/LHOxfzE=" }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "requires": { + "async-done": "1.2.4" + } + }, "async.queue": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz", @@ -951,8 +1057,7 @@ "atob": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", - "dev": true + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" }, "autoprefixer": { "version": "8.1.0", @@ -2002,6 +2107,22 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "requires": { + "arr-filter": "1.1.2", + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "array-each": "1.0.1", + "array-initial": "1.1.0", + "array-last": "1.3.0", + "async-done": "1.2.4", + "async-settle": "1.0.0", + "now-and-later": "2.0.0" + } + }, "backbone": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.3.3.tgz", @@ -2047,7 +2168,6 @@ "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, "requires": { "cache-base": "1.0.1", "class-utils": "0.3.6", @@ -2061,8 +2181,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -2783,6 +2902,11 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" + }, "buffer-fill": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-0.1.1.tgz", @@ -2866,7 +2990,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, "requires": { "collection-visit": "1.0.0", "component-emitter": "1.2.1", @@ -2882,8 +3005,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -3185,7 +3307,6 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, "requires": { "arr-union": "3.1.0", "define-property": "0.2.5", @@ -3197,7 +3318,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -3206,7 +3326,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -3215,7 +3334,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -3226,7 +3344,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -3235,7 +3352,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -3246,7 +3362,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -3256,14 +3371,12 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -3380,8 +3493,7 @@ "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" }, "clone-deep": { "version": "2.0.2", @@ -3434,14 +3546,12 @@ "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" }, "cloneable-readable": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", - "dev": true, "requires": { "inherits": "2.0.3", "process-nextick-args": "1.0.7", @@ -3487,11 +3597,30 @@ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=" }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "requires": { + "arr-map": "2.0.2", + "for-own": "1.0.0", + "make-iterator": "1.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "1.0.2" + } + } + } + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, "requires": { "map-visit": "1.0.0", "object-visit": "1.0.1" @@ -3732,8 +3861,7 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "component-inherit": { "version": "0.0.3", @@ -3863,8 +3991,16 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-props": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.2.tgz", + "integrity": "sha512-/W7IE8h4Zj1jdyf26YhdEjTS/xO42ltxtlH6B9+rmFNeXO+LL7KhzqqJdKJUwwO+4ZZ4IRw7rFhm8VEw95T25A==", + "requires": { + "each-props": "1.3.1", + "is-plain-object": "2.0.4" + } }, "copy-to-clipboard": { "version": "3.0.8", @@ -4729,6 +4865,26 @@ "integrity": "sha1-iQa/nlJaT78bIDsq/LRkAkmCEhk=", "dev": true }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "requires": { + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" + }, "deferred-leveldown": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", @@ -4750,7 +4906,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "1.0.2" } @@ -4883,6 +5038,11 @@ "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", "dev": true }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -5281,6 +5441,15 @@ "stream-shift": "1.0.0" } }, + "each-props": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.1.tgz", + "integrity": "sha1-/BOPUeOid0KG1IWOAtbn3kYt4Vg=", + "requires": { + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0" + } + }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -7180,7 +7349,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, "requires": { "homedir-polyfill": "1.0.1" } @@ -7231,7 +7399,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "0.1.1" } @@ -7557,6 +7724,316 @@ "pinkie-promise": "2.0.1" } }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + }, + "dependencies": { + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + } + } + } + } + }, + "fined": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", + "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "requires": { + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" + } + }, "fireworm": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/fireworm/-/fireworm-0.7.1.tgz", @@ -7596,6 +8073,11 @@ "readable-stream": "2.3.3" } }, + "flagged-respawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", + "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=" + }, "flat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/flat/-/flat-1.0.0.tgz", @@ -7646,6 +8128,15 @@ "integrity": "sha1-N4tRKNbQtVSosvFqTKPhq5ZJ8A4=", "dev": true }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, "follow-redirects": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", @@ -7715,7 +8206,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, "requires": { "map-cache": "0.2.2" } @@ -7773,6 +8263,15 @@ "rimraf": "2.6.2" } }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "requires": { + "graceful-fs": "4.1.11", + "through2": "2.0.3" + } + }, "fs-promise": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-2.0.3.tgz", @@ -9278,8 +9777,7 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", @@ -9435,6 +9933,58 @@ "is-glob": "2.0.1" } }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "requires": { + "extend": "3.0.1", + "glob": "7.1.2", + "glob-parent": "3.1.0", + "is-negated-glob": "1.0.0", + "ordered-read-streams": "1.0.1", + "pumpify": "1.3.5", + "readable-stream": "2.3.3", + "remove-trailing-separator": "1.1.0", + "to-absolute-glob": "2.0.2", + "unique-stream": "2.2.1" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "glob-watcher": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-4.0.0.tgz", + "integrity": "sha1-nmOo/25h6TLebMLK7OUHGm1zcyk=", + "requires": { + "async-done": "1.2.4", + "chokidar": "1.7.0", + "just-debounce": "1.0.0", + "object.defaults": "1.1.0" + } + }, "glob2base": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", @@ -9457,7 +10007,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, "requires": { "global-prefix": "1.0.2", "is-windows": "1.0.2", @@ -9468,7 +10017,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, "requires": { "expand-tilde": "2.0.2", "homedir-polyfill": "1.0.1", @@ -9582,6 +10130,75 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, + "gulp": { + "version": "github:gulpjs/gulp#71c094a51c7972d26f557899ddecab0210ef3776", + "requires": { + "glob-watcher": "4.0.0", + "gulp-cli": "2.0.1", + "undertaker": "1.2.0", + "vinyl-fs": "3.0.2" + }, + "dependencies": { + "gulp-cli": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz", + "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", + "requires": { + "ansi-colors": "1.0.1", + "archy": "1.0.0", + "array-sort": "1.0.0", + "color-support": "1.1.3", + "concat-stream": "1.6.0", + "copy-props": "2.0.2", + "fancy-log": "1.3.2", + "gulplog": "1.0.0", + "interpret": "1.1.0", + "isobject": "3.0.1", + "liftoff": "2.5.0", + "matchdep": "2.0.0", + "mute-stdout": "1.0.0", + "pretty-hrtime": "1.0.3", + "replace-homedir": "1.0.0", + "semver-greatest-satisfied-range": "1.1.0", + "v8flags": "3.0.2", + "yargs": "7.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "3.0.0" + } + } + } + }, "gulp-autoprefixer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-5.0.0.tgz", @@ -11134,8 +11751,7 @@ "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-to-string-tag-x": { "version": "1.4.1", @@ -11154,7 +11770,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, "requires": { "get-value": "2.0.6", "has-values": "1.0.0", @@ -11164,8 +11779,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -11173,7 +11787,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, "requires": { "is-number": "3.0.0", "kind-of": "4.0.0" @@ -11183,7 +11796,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -11192,7 +11804,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -11203,7 +11814,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -11319,7 +11929,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, "requires": { "parse-passwd": "1.0.0" } @@ -11783,8 +12392,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "inject-css": { "version": "0.1.1", @@ -12018,6 +12626,15 @@ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=" }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "1.0.0", + "is-windows": "1.0.2" + } + }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -12027,7 +12644,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "6.0.2" }, @@ -12035,8 +12651,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -12095,7 +12710,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "6.0.2" }, @@ -12103,8 +12717,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -12122,7 +12735,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "1.0.0", "is-data-descriptor": "1.0.0", @@ -12132,8 +12744,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -12232,6 +12843,11 @@ "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -12277,7 +12893,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, "requires": { "is-number": "4.0.0" }, @@ -12285,8 +12900,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" } } }, @@ -12364,6 +12978,14 @@ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "1.0.0" + } + }, "is-resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.1.tgz", @@ -12433,11 +13055,24 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "0.1.2" + } + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" + }, "is-whitespace-character": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", @@ -12446,8 +13081,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "is-word-character": { "version": "1.0.1", @@ -13235,6 +13869,11 @@ } } }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" + }, "just-extend": { "version": "1.1.27", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", @@ -13591,15 +14230,31 @@ } } }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "requires": { + "default-resolution": "2.0.0", + "es6-weak-map": "2.0.2" + } + }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, "requires": { "set-getter": "0.1.0" } }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "2.3.3" + } + }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -13660,6 +14315,14 @@ } } }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "requires": { + "flush-write-stream": "1.0.3" + } + }, "left-pad": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz", @@ -13956,6 +14619,21 @@ } } }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "requires": { + "extend": "3.0.1", + "findup-sync": "2.0.0", + "fined": "1.1.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.4.0" + } + }, "listr": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/listr/-/listr-0.13.0.tgz", @@ -14915,6 +15593,21 @@ "make-error": "1.3.4" } }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, "map-async": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/map-async/-/map-async-0.1.1.tgz", @@ -14923,8 +15616,7 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-obj": { "version": "1.0.1", @@ -14941,7 +15633,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, "requires": { "object-visit": "1.0.1" } @@ -14957,6 +15648,291 @@ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "dev": true }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "requires": { + "findup-sync": "2.0.0", + "micromatch": "3.1.10", + "resolve": "1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + }, + "dependencies": { + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + } + } + } + } + }, "matcher-collection": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.0.5.tgz", @@ -15462,7 +16438,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", - "dev": true, "requires": { "for-in": "1.0.2", "is-extendable": "1.0.1" @@ -15472,7 +16447,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -15680,6 +16654,11 @@ "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=", "dev": true }, + "mute-stdout": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.0.tgz", + "integrity": "sha1-WzLqB+tDyd7WEwQ0z5JvRrKn/U0=" + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -15716,7 +16695,6 @@ "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, "requires": { "arr-diff": "4.0.0", "array-unique": "0.3.2", @@ -15735,20 +16713,17 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, "requires": { "is-descriptor": "1.0.2", "isobject": "3.0.1" @@ -15758,7 +16733,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" @@ -15768,7 +16742,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -15776,14 +16749,12 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -16383,6 +17354,14 @@ "sort-keys": "2.0.0" } }, + "now-and-later": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", + "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", + "requires": { + "once": "1.4.0" + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -18531,7 +19510,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, "requires": { "copy-descriptor": "0.1.1", "define-property": "0.2.5", @@ -18542,7 +19520,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -18551,7 +19528,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -18560,7 +19536,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -18569,7 +19544,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -18579,8 +19553,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } } @@ -18612,7 +19585,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, "requires": { "isobject": "3.0.1" }, @@ -18620,8 +19592,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -18629,7 +19600,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "1.1.2", "function-bind": "1.1.1", @@ -18637,6 +19607,32 @@ "object-keys": "1.0.11" } }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "1.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, "object.entries": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", @@ -18649,6 +19645,25 @@ "has": "1.0.1" } }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "1.0.0", + "make-iterator": "1.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "1.0.2" + } + } + } + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -18662,7 +19677,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, "requires": { "isobject": "3.0.1" }, @@ -18670,8 +19684,26 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "requires": { + "for-own": "1.0.0", + "make-iterator": "1.0.1" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "1.0.2" + } } } }, @@ -18846,6 +19878,14 @@ } } }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "requires": { + "readable-stream": "2.3.3" + } + }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -19044,6 +20084,16 @@ "is-hexadecimal": "1.0.1" } }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" + } + }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -19075,8 +20125,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" }, "parse5": { "version": "3.0.3", @@ -19122,8 +20171,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "path": { "version": "0.12.7", @@ -19151,8 +20199,7 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" }, "path-exists": { "version": "2.1.0", @@ -19208,6 +20255,19 @@ } } }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "0.1.2" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -19579,8 +20639,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "post-message-stream": { "version": "3.0.0", @@ -20996,6 +22055,11 @@ "integrity": "sha1-zZApTVihyk6KXQ+5yCJZmIgazwA=", "dev": true }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + }, "printf": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/printf/-/printf-0.2.5.tgz", @@ -21942,7 +23006,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, "requires": { "resolve": "1.4.0" } @@ -22087,7 +23150,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", - "dev": true, "requires": { "extend-shallow": "2.0.1" } @@ -22150,6 +23212,25 @@ "xtend": "4.0.1" } }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "requires": { + "is-buffer": "1.1.6", + "is-utf8": "0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "requires": { + "remove-bom-buffer": "3.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -22178,6 +23259,16 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "requires": { + "homedir-polyfill": "1.0.1", + "is-absolute": "1.0.0", + "remove-trailing-separator": "1.1.0" + } + }, "replaceall": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", @@ -22350,7 +23441,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, "requires": { "expand-tilde": "2.0.2", "global-modules": "1.0.0" @@ -22361,6 +23451,14 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "requires": { + "value-or-function": "3.0.0" + } + }, "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", @@ -22369,8 +23467,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "resolve-url-loader": { "version": "2.3.0", @@ -22455,8 +23552,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "revalidator": { "version": "0.1.8", @@ -22580,7 +23676,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, "requires": { "ret": "0.1.15" } @@ -22889,6 +23984,14 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "requires": { + "sver-compat": "1.5.0" + } + }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", @@ -22941,7 +24044,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dev": true, "requires": { "to-object-path": "0.3.0" } @@ -22955,7 +24057,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -23323,7 +24424,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", - "dev": true, "requires": { "base": "0.11.2", "debug": "2.6.9", @@ -23339,7 +24439,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -23348,7 +24447,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23357,7 +24455,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23368,7 +24465,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23377,7 +24473,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23388,7 +24483,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -23398,14 +24492,12 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -23413,7 +24505,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, "requires": { "define-property": "1.0.0", "isobject": "3.0.1", @@ -23423,8 +24514,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -23432,7 +24522,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -23712,7 +24801,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", - "dev": true, "requires": { "atob": "2.0.3", "decode-uri-component": "0.2.0", @@ -23739,8 +24827,7 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "sparkles": { "version": "1.0.0", @@ -23790,7 +24877,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, "requires": { "extend-shallow": "3.0.2" }, @@ -23799,7 +24885,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" @@ -23809,7 +24894,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -23884,8 +24968,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "state-toggle": { "version": "1.0.0", @@ -23930,7 +25013,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, "requires": { "define-property": "0.2.5", "object-copy": "0.1.0" @@ -23940,7 +25022,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -23949,7 +25030,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23958,7 +25038,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23969,7 +25048,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -23978,7 +25056,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -23989,7 +25066,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -23999,8 +25075,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -24159,6 +25234,11 @@ } } }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" + }, "stream-http": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", @@ -24954,6 +26034,15 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "requires": { + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } + }, "svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", @@ -25582,6 +26671,15 @@ "xtend": "4.0.1" } }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, "thunkify": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", @@ -25654,6 +26752,15 @@ "os-tmpdir": "1.0.2" } }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "requires": { + "is-absolute": "1.0.0", + "is-negated-glob": "1.0.0" + } + }, "to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", @@ -25679,7 +26786,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -25688,7 +26794,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", - "dev": true, "requires": { "define-property": "0.2.5", "extend-shallow": "2.0.1", @@ -25699,7 +26804,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -25708,7 +26812,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -25717,7 +26820,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -25728,7 +26830,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -25737,7 +26838,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -25748,7 +26848,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -25758,8 +26857,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -25767,7 +26865,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, "requires": { "is-number": "3.0.0", "repeat-string": "1.6.1" @@ -25777,13 +26874,20 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "3.2.2" } } } }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "requires": { + "through2": "2.0.3" + } + }, "toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", @@ -26049,6 +27153,11 @@ } } }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, "underscore": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", @@ -26072,6 +27181,27 @@ } } }, + "undertaker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", + "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", + "requires": { + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "bach": "1.2.0", + "collection-map": "1.0.0", + "es6-weak-map": "2.0.2", + "last-run": "1.1.1", + "object.defaults": "1.1.0", + "object.reduce": "1.0.1", + "undertaker-registry": "1.0.1" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" + }, "unherit": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", @@ -26099,7 +27229,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, "requires": { "arr-union": "3.1.0", "get-value": "2.0.6", @@ -26111,7 +27240,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -26139,6 +27267,15 @@ "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, "unist-util-is": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", @@ -26179,7 +27316,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, "requires": { "has-value": "0.3.1", "isobject": "3.0.1" @@ -26189,7 +27325,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, "requires": { "get-value": "2.0.6", "has-values": "0.1.4", @@ -26200,7 +27335,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, "requires": { "isarray": "1.0.0" } @@ -26210,14 +27344,12 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -26251,8 +27383,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url": { "version": "0.11.0", @@ -26293,7 +27424,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "dev": true, "requires": { "define-property": "0.2.5", "isobject": "3.0.1", @@ -26304,7 +27434,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -26313,7 +27442,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -26322,7 +27450,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -26333,7 +27460,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -26342,7 +27468,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -26353,7 +27478,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -26363,14 +27487,12 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -26460,6 +27582,14 @@ "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==", "dev": true }, + "v8flags": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.2.tgz", + "integrity": "sha512-6sgSKoFw1UpUPd3cFdF7QGnrH6tDeBgW1F3v9gy8gLY0mlbiBXq8soy8aQpY6xeeCjH5K+JvC62Acp7gtl7wWA==", + "requires": { + "homedir-polyfill": "1.0.1" + } + }, "valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", @@ -26479,6 +27609,11 @@ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" + }, "varint": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/varint/-/varint-4.0.1.tgz", @@ -26532,7 +27667,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", - "dev": true, "requires": { "clone": "2.1.1", "clone-buffer": "1.0.0", @@ -26603,6 +27737,30 @@ } } }, + "vinyl-fs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.2.tgz", + "integrity": "sha512-AUSFda1OukBwuLPBTbyuO4IRWgfXmqC4UTW0f8xrCa8Hkv9oyIU+NSqBlgfOLZRoUt7cHdo75hKQghCywpIyIw==", + "requires": { + "fs-mkdirp-stream": "1.0.0", + "glob-stream": "6.1.0", + "graceful-fs": "4.1.11", + "is-valid-glob": "1.0.0", + "lazystream": "1.0.0", + "lead": "1.0.0", + "object.assign": "4.1.0", + "pumpify": "1.3.5", + "readable-stream": "2.3.3", + "remove-bom-buffer": "3.0.0", + "remove-bom-stream": "1.2.0", + "resolve-options": "1.1.0", + "through2": "2.0.3", + "to-through": "2.0.0", + "value-or-function": "3.0.0", + "vinyl": "2.1.0", + "vinyl-sourcemap": "1.1.0" + } + }, "vinyl-source-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz", @@ -26613,6 +27771,27 @@ "vinyl": "2.1.0" } }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "requires": { + "append-buffer": "1.0.2", + "convert-source-map": "1.5.1", + "graceful-fs": "4.1.11", + "normalize-path": "2.1.1", + "now-and-later": "2.0.0", + "remove-bom-buffer": "3.0.0", + "vinyl": "2.1.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + } + } + }, "vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", diff --git a/package.json b/package.json index 49743e74e..9b3ee214b 100644 --- a/package.json +++ b/package.json @@ -206,7 +206,7 @@ "brfs": "^1.4.3", "browserify": "^16.1.1", "chai": "^4.1.0", - "chromedriver": "^2.34.1", + "chromedriver": "2.36.0", "compression": "^1.7.1", "coveralls": "^3.0.0", "cross-env": "^5.1.4", From 3b229775a86294b7c2a23ee64d738ca07dbacebd Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 2 May 2018 14:06:34 -0700 Subject: [PATCH 78/79] Remove eth-block-tracker package --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 9b3ee214b..832021f50 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,6 @@ "ensnare": "^1.0.0", "eslint-plugin-react": "^7.4.0", "eth-bin-to-ops": "^1.0.1", - "eth-block-tracker": "^2.3.0", "eth-contract-metadata": "^1.1.5", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.6", From caf5a6c15c3375d9d64116d80d87eb064e955e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0?= Date: Mon, 7 May 2018 21:05:34 +0200 Subject: [PATCH 79/79] Fix Slovenian translation (#4189) --- app/_locales/sl/messages.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index b089f3476..25bd0bcbb 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -181,7 +181,7 @@ "message": "DEN je vaša šifrirana shramba v MetaMasku." }, "deposit": { - "message": "Vplačilo" + "message": "Vplačaj" }, "depositBTC": { "message": "Vplačajte vaš BTC na spodnji naslov:" @@ -507,10 +507,10 @@ "message": "Ni se začelo" }, "oldUI": { - "message": "Starejši uporabniški vmesnik" + "message": "Star UI" }, "oldUIMessage": { - "message": "Vrnili ste se v starejši uporabniški vmesnik. V novega se lahko vrnete z možnostjo v spustnem meniju v zgornjem desnem kotu." + "message": "Vrnili ste se v star uporabniški vmesnik. V novega se lahko vrnete z možnostjo v spustnem meniju v zgornjem desnem kotu." }, "or": { "message": "ali", @@ -759,7 +759,7 @@ "message": "Vpišite vaše geslo" }, "uiWelcome": { - "message": "Dobrodošli v novem uporabniškem vmesniku (Beta)" + "message": "Dobrodošli v nov UI (Beta)" }, "uiWelcomeMessage": { "message": "Zdaj uporabljate novi MetaMask uporabniški vmesnik. Razglejte se, preizkusite nove funkcije, kot so pošiljanje žetonov, in nas obvestite, če imate kakšne težave."