diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml index 4909aabde..db885300a 100644 --- a/.buildkite/pipeline.yaml +++ b/.buildkite/pipeline.yaml @@ -7,7 +7,7 @@ steps: - docker#v3.0.1: image: "node:10" - - label: ":karma: Tests" + - label: ":jest: Tests" command: - "yarn install" - "yarn test" diff --git a/.eslintrc.js b/.eslintrc.js index 9a752b14f..e0b8c538e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,10 +12,12 @@ module.exports = { // babel's transform-runtime converts references to ES6 globals such as // Promise and Map to core-js polyfills, so we can use ES6 globals. es6: true, + jest: true, }, extends: ["eslint:recommended", "google"], plugins: [ "babel", + "jest", ], rules: { // rules we've always adhered to or now do diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2e38801..cc0ab61ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +Changes in [2.4.5](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.5) (2019-11-27) +================================================================================================ +[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.4...v2.4.5) + +* Relax identity server discovery checks to FAIL_PROMPT +* Expand E2EE debug logging to diagnose "unable to decrypt" errors + +Changes in [2.4.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.4) (2019-11-25) +================================================================================================ +[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.4-rc.1...v2.4.4) + + * No changes since rc.1 + +Changes in [2.4.4-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.4-rc.1) (2019-11-20) +========================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.3...v2.4.4-rc.1) + + * Fix SAS verification in encrypted DMs + [\#1077](https://github.com/matrix-org/matrix-js-sdk/pull/1077) + * Cross-signing / secret storage tweaks + [\#1078](https://github.com/matrix-org/matrix-js-sdk/pull/1078) + * Fix local trust for key backups + [\#1075](https://github.com/matrix-org/matrix-js-sdk/pull/1075) + * Add method to get last active timestamp in room + [\#1072](https://github.com/matrix-org/matrix-js-sdk/pull/1072) + * Check the right Synapse endpoint for determining admin capabilities + [\#1071](https://github.com/matrix-org/matrix-js-sdk/pull/1071) + * Cross Signing Support + [\#832](https://github.com/matrix-org/matrix-js-sdk/pull/832) + * Don't double cancel verification request + [\#1064](https://github.com/matrix-org/matrix-js-sdk/pull/1064) + * Support for verification requests in the timeline + [\#1067](https://github.com/matrix-org/matrix-js-sdk/pull/1067) + * Use stable API prefix for 3PID APIs when supported + [\#1066](https://github.com/matrix-org/matrix-js-sdk/pull/1066) + * Remove Jenkins scripts + [\#1063](https://github.com/matrix-org/matrix-js-sdk/pull/1063) + Changes in [2.4.3](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.3) (2019-11-04) ================================================================================================ [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.3-rc.1...v2.4.3) diff --git a/package.json b/package.json index 8267926d1..a3959f3d7 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,11 @@ { "name": "matrix-js-sdk", - "version": "2.4.3", + "version": "2.4.5", "description": "Matrix Client-Server SDK for Javascript", "main": "index.js", "scripts": { - "test:build": "babel -s -d specbuild spec", - "test:run": "istanbul cover --report text --report cobertura --config .istanbul.yml -i \"lib/**/*.js\" node_modules/mocha/bin/_mocha -- --recursive specbuild --colors", - "test:watch": "mocha --watch --compilers js:babel-core/register --recursive spec --colors", - "test": "yarn test:build && yarn test:run", - "check": "yarn test:build && _mocha --recursive specbuild --colors", + "test:watch": "jest spec/ --coverage --testEnvironment node --watch", + "test": "jest spec/ --coverage --testEnvironment node", "gendoc": "babel --no-babelrc --plugins transform-class-properties -d .jsdocbuild src && jsdoc -r .jsdocbuild -P package.json -R README.md -d .jsdoc", "start": "yarn start:init && yarn start:watch", "start:watch": "babel -s -w --skip-initial-build -d lib src", @@ -65,6 +62,7 @@ "devDependencies": { "babel-cli": "^6.18.0", "babel-eslint": "^10.0.1", + "babel-jest": "^23.6.0", "babel-plugin-transform-async-to-bluebird": "^1.1.1", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-runtime": "^6.23.0", @@ -75,13 +73,11 @@ "eslint": "^5.12.0", "eslint-config-google": "^0.7.1", "eslint-plugin-babel": "^5.3.0", + "eslint-plugin-jest": "^23.0.4", "exorcist": "^1.0.1", - "expect": "^1.20.2", - "istanbul": "^0.4.5", + "jest": "^23.6.0", "jsdoc": "^3.5.5", - "lolex": "^1.5.2", "matrix-mock-request": "^1.2.3", - "mocha": "^6.2.1", "olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz", "rimraf": "^3.0.0", "source-map-support": "^0.5.13", diff --git a/spec/.eslintrc.js b/spec/.eslintrc.js deleted file mode 100644 index 4cc4659d7..000000000 --- a/spec/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - env: { - mocha: true, - }, -} diff --git a/spec/TestClient.js b/spec/TestClient.js index e29fb8bfb..8aeb3c291 100644 --- a/spec/TestClient.js +++ b/spec/TestClient.js @@ -24,7 +24,6 @@ import './olm-loader'; import sdk from '..'; import testUtils from './test-utils'; import MockHttpBackend from 'matrix-mock-request'; -import expect from 'expect'; import Promise from 'bluebird'; import LocalStorageCryptoStore from '../lib/crypto/store/localStorage-crypto-store'; import logger from '../src/logger'; @@ -159,7 +158,7 @@ TestClient.prototype.awaitOneTimeKeyUpload = function() { .respond(200, (path, content) => { expect(content.device_keys).toBe(undefined); expect(content.one_time_keys).toBeTruthy(); - expect(content.one_time_keys).toNotEqual({}); + expect(content.one_time_keys).not.toEqual({}); logger.log('%s: received %i one-time keys', this, Object.keys(content.one_time_keys).length); this.oneTimeKeys = content.one_time_keys; diff --git a/spec/integ/devicelist-integ-spec.js b/spec/integ/devicelist-integ-spec.js index f59e46e3f..1d033c2f0 100644 --- a/spec/integ/devicelist-integ-spec.js +++ b/spec/integ/devicelist-integ-spec.js @@ -15,7 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import expect from 'expect'; import Promise from 'bluebird'; import TestClient from '../TestClient'; @@ -88,8 +87,6 @@ describe("DeviceList management:", function() { } beforeEach(async function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - // we create our own sessionStoreBackend so that we can use it for // another TestClient. sessionStoreBackend = new testUtils.MockStorageApi(); diff --git a/spec/integ/matrix-client-crypto.spec.js b/spec/integ/matrix-client-crypto.spec.js index 297b8537f..d74009177 100644 --- a/spec/integ/matrix-client-crypto.spec.js +++ b/spec/integ/matrix-client-crypto.spec.js @@ -30,7 +30,6 @@ import 'source-map-support/register'; // load olm before the sdk if possible import '../olm-loader'; -import expect from 'expect'; const sdk = require("../.."); import Promise from 'bluebird'; const utils = require("../../lib/utils"); @@ -56,7 +55,7 @@ function bobUploadsDeviceKeys() { bobTestClient.client.uploadKeys(), bobTestClient.httpBackend.flush(), ]).then(() => { - expect(Object.keys(bobTestClient.deviceKeys).length).toNotEqual(0); + expect(Object.keys(bobTestClient.deviceKeys).length).not.toEqual(0); }); } @@ -204,7 +203,7 @@ function aliSendsFirstMessage() { expectAliQueryKeys() .then(expectAliClaimKeys) .then(expectAliSendMessageRequest), - ]).spread(function(_, ciphertext) { + ]).then(function([_, ciphertext]) { return ciphertext; }); } @@ -219,7 +218,7 @@ function aliSendsMessage() { return Promise.all([ sendMessage(aliTestClient.client), expectAliSendMessageRequest(), - ]).spread(function(_, ciphertext) { + ]).then(function([_, ciphertext]) { return ciphertext; }); } @@ -235,7 +234,7 @@ function bobSendsReplyMessage() { sendMessage(bobTestClient.client), expectBobQueryKeys() .then(expectBobSendMessageRequest), - ]).spread(function(_, ciphertext) { + ]).then(function([_, ciphertext]) { return ciphertext; }); } @@ -280,16 +279,17 @@ function sendMessage(client) { function expectSendMessageRequest(httpBackend) { const path = "/send/m.room.encrypted/"; - const deferred = Promise.defer(); - httpBackend.when("PUT", path).respond(200, function(path, content) { - deferred.resolve(content); - return { - event_id: "asdfgh", - }; + const prom = new Promise((resolve) => { + httpBackend.when("PUT", path).respond(200, function(path, content) { + resolve(content); + return { + event_id: "asdfgh", + }; + }); }); // it can take a while to process the key query - return httpBackend.flush(path, 1).then(() => deferred.promise); + return httpBackend.flush(path, 1).then(() => prom); } function aliRecvMessage() { @@ -406,8 +406,6 @@ describe("MatrixClient crypto", function() { } beforeEach(async function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - aliTestClient = new TestClient(aliUserId, aliDeviceId, aliAccessToken); await aliTestClient.client.initCrypto(); @@ -430,15 +428,14 @@ describe("MatrixClient crypto", function() { .then(bobUploadsDeviceKeys); }); - it("Ali downloads Bobs device keys", function(done) { - Promise.resolve() + it("Ali downloads Bobs device keys", function() { + return Promise.resolve() .then(bobUploadsDeviceKeys) - .then(aliDownloadsKeys) - .nodeify(done); + .then(aliDownloadsKeys); }); - it("Ali gets keys with an invalid signature", function(done) { - Promise.resolve() + it("Ali gets keys with an invalid signature", function() { + return Promise.resolve() .then(bobUploadsDeviceKeys) .then(function() { // tamper bob's keys @@ -455,11 +452,10 @@ describe("MatrixClient crypto", function() { }).then((devices) => { // should get an empty list expect(devices).toEqual([]); - }) - .nodeify(done); + }); }); - it("Ali gets keys with an incorrect userId", function(done) { + it("Ali gets keys with an incorrect userId", function() { const eveUserId = "@eve:localhost"; const bobDeviceKeys = { @@ -488,7 +484,7 @@ describe("MatrixClient crypto", function() { return {device_keys: result}; }); - Promise.all([ + return Promise.all([ aliTestClient.client.downloadKeys([bobUserId, eveUserId]), aliTestClient.httpBackend.flush("/keys/query", 1), ]).then(function() { @@ -496,14 +492,14 @@ describe("MatrixClient crypto", function() { aliTestClient.client.getStoredDevicesForUser(bobUserId), aliTestClient.client.getStoredDevicesForUser(eveUserId), ]); - }).spread((bobDevices, eveDevices) => { + }).then(([bobDevices, eveDevices]) => { // should get an empty list expect(bobDevices).toEqual([]); expect(eveDevices).toEqual([]); - }).nodeify(done); + }); }); - it("Ali gets keys with an incorrect deviceId", function(done) { + it("Ali gets keys with an incorrect deviceId", function() { const bobDeviceKeys = { algorithms: ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'], device_id: 'bad_device', @@ -530,7 +526,7 @@ describe("MatrixClient crypto", function() { return {device_keys: result}; }); - Promise.all([ + return Promise.all([ aliTestClient.client.downloadKeys([bobUserId]), aliTestClient.httpBackend.flush("/keys/query", 1), ]).then(function() { @@ -538,7 +534,7 @@ describe("MatrixClient crypto", function() { }).then((devices) => { // should get an empty list expect(devices).toEqual([]); - }).nodeify(done); + }); }); @@ -548,19 +544,18 @@ describe("MatrixClient crypto", function() { .then(() => bobTestClient.awaitOneTimeKeyUpload()) .then((keys) => { expect(Object.keys(keys).length).toEqual(5); - expect(Object.keys(bobTestClient.deviceKeys).length).toNotEqual(0); + expect(Object.keys(bobTestClient.deviceKeys).length).not.toEqual(0); }); }); - it("Ali sends a message", function(done) { + it("Ali sends a message", function() { aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}}); - Promise.resolve() + return Promise.resolve() .then(() => aliTestClient.start()) .then(() => bobTestClient.start()) .then(() => firstSync(aliTestClient)) .then(aliEnablesEncryption) - .then(aliSendsFirstMessage) - .nodeify(done); + .then(aliSendsFirstMessage); }); it("Bob receives a message", function() { @@ -628,9 +623,9 @@ describe("MatrixClient crypto", function() { }); }); - it("Ali blocks Bob's device", function(done) { + it("Ali blocks Bob's device", function() { aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}}); - Promise.resolve() + return Promise.resolve() .then(() => aliTestClient.start()) .then(() => bobTestClient.start()) .then(() => firstSync(aliTestClient)) @@ -645,12 +640,12 @@ describe("MatrixClient crypto", function() { expect(sentContent.ciphertext).toEqual({}); }); return Promise.all([p1, p2]); - }).nodeify(done); + }); }); - it("Bob receives two pre-key messages", function(done) { + it("Bob receives two pre-key messages", function() { aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}}); - Promise.resolve() + return Promise.resolve() .then(() => aliTestClient.start()) .then(() => bobTestClient.start()) .then(() => firstSync(aliTestClient)) @@ -658,8 +653,7 @@ describe("MatrixClient crypto", function() { .then(aliSendsFirstMessage) .then(bobRecvMessage) .then(aliSendsMessage) - .then(bobRecvMessage) - .nodeify(done); + .then(bobRecvMessage); }); it("Bob replies to the message", function() { @@ -753,9 +747,9 @@ describe("MatrixClient crypto", function() { .then(() => httpBackend.when("POST", "/keys/upload") .respond(200, (path, content) => { expect(content.one_time_keys).toBeTruthy(); - expect(content.one_time_keys).toNotEqual({}); + expect(content.one_time_keys).not.toEqual({}); expect(Object.keys(content.one_time_keys).length) - .toBeGreaterThanOrEqualTo(1); + .toBeGreaterThanOrEqual(1); logger.log('received %i one-time keys', Object.keys(content.one_time_keys).length); // cancel futher calls by telling the client diff --git a/spec/integ/matrix-client-event-emitter.spec.js b/spec/integ/matrix-client-event-emitter.spec.js index 9f7b028eb..90fa5db8f 100644 --- a/spec/integ/matrix-client-event-emitter.spec.js +++ b/spec/integ/matrix-client-event-emitter.spec.js @@ -4,7 +4,6 @@ const sdk = require("../.."); const HttpBackend = require("matrix-mock-request"); const utils = require("../test-utils"); -import expect from 'expect'; import Promise from 'bluebird'; describe("MatrixClient events", function() { @@ -15,7 +14,6 @@ describe("MatrixClient events", function() { const selfAccessToken = "aseukfgwef"; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); client = sdk.createClient({ @@ -219,7 +217,7 @@ describe("MatrixClient events", function() { client.on("RoomState.events", function(event, state) { eventsInvokeCount++; const index = roomStateEventTypes.indexOf(event.getType()); - expect(index).toNotEqual( + expect(index).not.toEqual( -1, "Unexpected room state event type: " + event.getType(), ); if (index >= 0) { diff --git a/spec/integ/matrix-client-event-timeline.spec.js b/spec/integ/matrix-client-event-timeline.spec.js index a89818abc..86436c27d 100644 --- a/spec/integ/matrix-client-event-timeline.spec.js +++ b/spec/integ/matrix-client-event-timeline.spec.js @@ -83,18 +83,19 @@ function startClient(httpBackend, client) { client.startClient(); // set up a promise which will resolve once the client is initialised - const deferred = Promise.defer(); - client.on("sync", function(state) { - logger.log("sync", state); - if (state != "SYNCING") { - return; - } - deferred.resolve(); + const prom = new Promise((resolve) => { + client.on("sync", function(state) { + logger.log("sync", state); + if (state != "SYNCING") { + return; + } + resolve(); + }); }); return Promise.all([ httpBackend.flushAllExpected(), - deferred.promise, + prom, ]); } @@ -103,7 +104,6 @@ describe("getEventTimeline support", function() { let client; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); }); @@ -115,21 +115,20 @@ describe("getEventTimeline support", function() { return httpBackend.stop(); }); - it("timeline support must be enabled to work", function(done) { + it("timeline support must be enabled to work", function() { client = sdk.createClient({ baseUrl: baseUrl, userId: userId, accessToken: accessToken, }); - startClient(httpBackend, client, - ).then(function() { + return startClient(httpBackend, client).then(function() { const room = client.getRoom(roomId); const timelineSet = room.getTimelineSets()[0]; expect(function() { client.getEventTimeline(timelineSet, "event"); }).toThrow(); - }).nodeify(done); + }); }); it("timeline support works when enabled", function() { @@ -145,13 +144,13 @@ describe("getEventTimeline support", function() { const timelineSet = room.getTimelineSets()[0]; expect(function() { client.getEventTimeline(timelineSet, "event"); - }).toNotThrow(); + }).not.toThrow(); }); }); it("scrollback should be able to scroll back to before a gappy /sync", - function(done) { + function() { // need a client with timelineSupport disabled to make this work client = sdk.createClient({ baseUrl: baseUrl, @@ -160,8 +159,7 @@ describe("getEventTimeline support", function() { }); let room; - startClient(httpBackend, client, - ).then(function() { + return startClient(httpBackend, client).then(function() { room = client.getRoom(roomId); httpBackend.when("GET", "/sync").respond(200, { @@ -217,18 +215,15 @@ describe("getEventTimeline support", function() { expect(room.timeline[0].event).toEqual(EVENTS[0]); expect(room.timeline[1].event).toEqual(EVENTS[1]); expect(room.oldState.paginationToken).toEqual("pagin_end"); - }).nodeify(done); + }); }); }); -import expect from 'expect'; - describe("MatrixClient event timelines", function() { let client = null; let httpBackend = null; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); @@ -349,25 +344,25 @@ describe("MatrixClient event timelines", function() { }; }); - const deferred = Promise.defer(); - client.on("sync", function() { - client.getEventTimeline(timelineSet, EVENTS[2].event_id, - ).then(function(tl) { - expect(tl.getEvents().length).toEqual(4); - expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); - expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); - expect(tl.getEvents()[3].event).toEqual(EVENTS[3]); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) - .toEqual("start_token"); - // expect(tl.getPaginationToken(EventTimeline.FORWARDS)) - // .toEqual("s_5_4"); - }).done(() => deferred.resolve(), - (e) => deferred.reject(e)); + const prom = new Promise((resolve, reject) => { + client.on("sync", function() { + client.getEventTimeline(timelineSet, EVENTS[2].event_id, + ).then(function(tl) { + expect(tl.getEvents().length).toEqual(4); + expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); + expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); + expect(tl.getEvents()[3].event).toEqual(EVENTS[3]); + expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + .toEqual("start_token"); + // expect(tl.getPaginationToken(EventTimeline.FORWARDS)) + // .toEqual("s_5_4"); + }).done(resolve, reject); + }); }); return Promise.all([ httpBackend.flushAllExpected(), - deferred.promise, + prom, ]); }); @@ -697,7 +692,7 @@ describe("MatrixClient event timelines", function() { }); - it("should handle gappy syncs after redactions", function(done) { + it("should handle gappy syncs after redactions", function() { // https://github.com/vector-im/vector-web/issues/1389 // a state event, followed by a redaction thereof @@ -729,7 +724,7 @@ describe("MatrixClient event timelines", function() { }; httpBackend.when("GET", "/sync").respond(200, syncData); - Promise.all([ + return Promise.all([ httpBackend.flushAllExpected(), utils.syncPromise(client), ]).then(function() { @@ -765,6 +760,6 @@ describe("MatrixClient event timelines", function() { const room = client.getRoom(roomId); const tl = room.getLiveTimeline(); expect(tl.getEvents().length).toEqual(1); - }).nodeify(done); + }); }); }); diff --git a/spec/integ/matrix-client-methods.spec.js b/spec/integ/matrix-client-methods.spec.js index 55bf119f6..d8d12e7e3 100644 --- a/spec/integ/matrix-client-methods.spec.js +++ b/spec/integ/matrix-client-methods.spec.js @@ -9,8 +9,6 @@ const Filter = publicGlobals.Filter; const utils = require("../test-utils"); const MockStorageApi = require("../MockStorageApi"); -import expect from 'expect'; - describe("MatrixClient", function() { const baseUrl = "http://localhost.or.something"; let client = null; @@ -21,7 +19,6 @@ describe("MatrixClient", function() { const accessToken = "aseukfgwef"; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); store = new MemoryStore(); @@ -46,7 +43,7 @@ describe("MatrixClient", function() { describe("uploadContent", function() { const buf = new Buffer('hello world'); - it("should upload the file", function(done) { + it("should upload the file", function() { httpBackend.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { @@ -74,25 +71,26 @@ describe("MatrixClient", function() { expect(uploads[0].promise).toBe(prom); expect(uploads[0].loaded).toEqual(0); - prom.then(function(response) { + const prom2 = prom.then(function(response) { // for backwards compatibility, we return the raw JSON expect(response).toEqual("content"); const uploads = client.getCurrentUploads(); expect(uploads.length).toEqual(0); - }).nodeify(done); + }); httpBackend.flush(); + return prom2; }); - it("should parse the response if rawResponse=false", function(done) { + it("should parse the response if rawResponse=false", function() { httpBackend.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { expect(req.opts.json).toBeFalsy(); }).respond(200, { "content_uri": "uri" }); - client.uploadContent({ + const prom = client.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", @@ -100,12 +98,13 @@ describe("MatrixClient", function() { rawResponse: false, }).then(function(response) { expect(response.content_uri).toEqual("uri"); - }).nodeify(done); + }); httpBackend.flush(); + return prom; }); - it("should parse errors into a MatrixError", function(done) { + it("should parse errors into a MatrixError", function() { httpBackend.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { @@ -116,7 +115,7 @@ describe("MatrixClient", function() { "error": "broken", }); - client.uploadContent({ + const prom = client.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", @@ -126,12 +125,13 @@ describe("MatrixClient", function() { expect(error.httpStatus).toEqual(400); expect(error.errcode).toEqual("M_SNAFU"); expect(error.message).toEqual("broken"); - }).nodeify(done); + }); httpBackend.flush(); + return prom; }); - it("should return a promise which can be cancelled", function(done) { + it("should return a promise which can be cancelled", function() { const prom = client.uploadContent({ stream: buf, name: "hi.txt", @@ -143,17 +143,18 @@ describe("MatrixClient", function() { expect(uploads[0].promise).toBe(prom); expect(uploads[0].loaded).toEqual(0); - prom.then(function(response) { + const prom2 = prom.then(function(response) { throw Error("request not aborted"); }, function(error) { expect(error).toEqual("aborted"); const uploads = client.getCurrentUploads(); expect(uploads.length).toEqual(0); - }).nodeify(done); + }); const r = client.cancelUpload(prom); expect(r).toBe(true); + return prom2; }); }); @@ -310,7 +311,7 @@ describe("MatrixClient", function() { return client.initCrypto(); }); - it("should do an HTTP request and then store the keys", function(done) { + it("should do an HTTP request and then store the keys", function() { const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78"; // ed25519key = client.getDeviceEd25519Key(); const borisKeys = { @@ -372,7 +373,7 @@ describe("MatrixClient", function() { }, }); - client.downloadKeys(["boris", "chaz"]).then(function(res) { + const prom = client.downloadKeys(["boris", "chaz"]).then(function(res) { assertObjectContains(res.boris.dev1, { verified: 0, // DeviceVerification.UNVERIFIED keys: { "ed25519:dev1": ed25519key }, @@ -386,26 +387,26 @@ describe("MatrixClient", function() { algorithms: ["2"], unsigned: { "ghi": "def" }, }); - }).nodeify(done); + }); httpBackend.flush(); + return prom; }); }); describe("deleteDevice", function() { const auth = {a: 1}; - it("should pass through an auth dict", function(done) { + it("should pass through an auth dict", function() { httpBackend.when( "DELETE", "/_matrix/client/r0/devices/my_device", ).check(function(req) { expect(req.data).toEqual({auth: auth}); }).respond(200); - client.deleteDevice( - "my_device", auth, - ).nodeify(done); + const prom = client.deleteDevice("my_device", auth); httpBackend.flush(); + return prom; }); }); }); diff --git a/spec/integ/matrix-client-opts.spec.js b/spec/integ/matrix-client-opts.spec.js index 11bcf6b6d..a4b3083ef 100644 --- a/spec/integ/matrix-client-opts.spec.js +++ b/spec/integ/matrix-client-opts.spec.js @@ -5,7 +5,6 @@ const MatrixClient = sdk.MatrixClient; const HttpBackend = require("matrix-mock-request"); const utils = require("../test-utils"); -import expect from 'expect'; import Promise from 'bluebird'; describe("MatrixClient opts", function() { @@ -58,7 +57,6 @@ describe("MatrixClient opts", function() { }; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); }); @@ -101,7 +99,7 @@ describe("MatrixClient opts", function() { "m.room.create", ]; client.on("event", function(event) { - expect(expectedEventTypes.indexOf(event.getType())).toNotEqual( + expect(expectedEventTypes.indexOf(event.getType())).not.toEqual( -1, "Recv unexpected event type: " + event.getType(), ); expectedEventTypes.splice( diff --git a/spec/integ/matrix-client-retrying.spec.js b/spec/integ/matrix-client-retrying.spec.js index 0dabc0abe..e266ea98b 100644 --- a/spec/integ/matrix-client-retrying.spec.js +++ b/spec/integ/matrix-client-retrying.spec.js @@ -4,11 +4,8 @@ import Promise from 'bluebird'; const sdk = require("../.."); const HttpBackend = require("matrix-mock-request"); -const utils = require("../test-utils"); const EventStatus = sdk.EventStatus; -import expect from 'expect'; - describe("MatrixClient retrying", function() { const baseUrl = "http://localhost.or.something"; let client = null; @@ -20,7 +17,6 @@ describe("MatrixClient retrying", function() { let room; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); scheduler = new sdk.MatrixScheduler(); diff --git a/spec/integ/matrix-client-room-timeline.spec.js b/spec/integ/matrix-client-room-timeline.spec.js index f951c0cf4..b93ec1bc9 100644 --- a/spec/integ/matrix-client-room-timeline.spec.js +++ b/spec/integ/matrix-client-room-timeline.spec.js @@ -6,7 +6,6 @@ const HttpBackend = require("matrix-mock-request"); const utils = require("../test-utils"); import Promise from 'bluebird'; -import expect from 'expect'; describe("MatrixClient room timelines", function() { const baseUrl = "http://localhost.or.something"; @@ -103,8 +102,7 @@ describe("MatrixClient room timelines", function() { }); } - beforeEach(function(done) { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this + beforeEach(function() { httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); client = sdk.createClient({ @@ -122,9 +120,9 @@ describe("MatrixClient room timelines", function() { return NEXT_SYNC_DATA; }); client.startClient(); - httpBackend.flush("/pushrules").then(function() { + return httpBackend.flush("/pushrules").then(function() { return httpBackend.flush("/filter"); - }).nodeify(done); + }); }); afterEach(function() { diff --git a/spec/integ/matrix-client-syncing.spec.js b/spec/integ/matrix-client-syncing.spec.js index 2d702c8a7..f015943c7 100644 --- a/spec/integ/matrix-client-syncing.spec.js +++ b/spec/integ/matrix-client-syncing.spec.js @@ -6,7 +6,6 @@ const utils = require("../test-utils"); const MatrixEvent = sdk.MatrixEvent; const EventTimeline = sdk.EventTimeline; -import expect from 'expect'; import Promise from 'bluebird'; describe("MatrixClient syncing", function() { @@ -23,7 +22,6 @@ describe("MatrixClient syncing", function() { const roomTwo = "!bar:localhost"; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new HttpBackend(); sdk.request(httpBackend.requestFn); client = sdk.createClient({ @@ -528,7 +526,7 @@ describe("MatrixClient syncing", function() { awaitSyncEvent(), ]).then(function() { const room = client.getRoom(roomTwo); - expect(room).toExist(); + expect(room).toBeDefined(); const tok = room.getLiveTimeline() .getPaginationToken(EventTimeline.BACKWARDS); expect(tok).toEqual("roomtwotok"); @@ -693,12 +691,12 @@ describe("MatrixClient syncing", function() { include_leave: true }}); }).respond(200, { filter_id: "another_id" }); - const defer = Promise.defer(); - - httpBackend.when("GET", "/sync").check(function(req) { - expect(req.queryParams.filter).toEqual("another_id"); - defer.resolve(); - }).respond(200, {}); + const prom = new Promise((resolve) => { + httpBackend.when("GET", "/sync").check(function(req) { + expect(req.queryParams.filter).toEqual("another_id"); + resolve(); + }).respond(200, {}); + }); client.syncLeftRooms(); @@ -709,7 +707,7 @@ describe("MatrixClient syncing", function() { // flush the syncs return httpBackend.flushAllExpected(); }), - defer.promise, + prom, ]); }); diff --git a/spec/integ/megolm-integ.spec.js b/spec/integ/megolm-integ.spec.js index 1851badce..1c1693bfc 100644 --- a/spec/integ/megolm-integ.spec.js +++ b/spec/integ/megolm-integ.spec.js @@ -18,7 +18,6 @@ limitations under the License. const anotherjson = require('another-json'); import Promise from 'bluebird'; -import expect from 'expect'; const utils = require('../../lib/utils'); const testUtils = require('../test-utils'); @@ -283,8 +282,6 @@ describe("megolm", function() { } beforeEach(async function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - aliceTestClient = new TestClient( "@alice:localhost", "xzcvb", "akjgkrgjs", ); @@ -713,7 +710,7 @@ describe("megolm", function() { 'PUT', '/send/', ).respond(200, function(path, content) { logger.log('/send:', content); - expect(content.session_id).toNotEqual(megolmSessionId); + expect(content.session_id).not.toEqual(megolmSessionId); return { event_id: '$event_id', }; diff --git a/spec/test-utils.js b/spec/test-utils.js index 9a81906c3..16ec18898 100644 --- a/spec/test-utils.js +++ b/spec/test-utils.js @@ -1,5 +1,4 @@ "use strict"; -import expect from 'expect'; import Promise from 'bluebird'; // load olm before the sdk if possible @@ -41,18 +40,6 @@ module.exports.syncPromise = function(client, count) { }); }; -/** - * Perform common actions before each test case, e.g. printing the test case - * name to stdout. - * @param {Mocha.Context} context The test context - */ -module.exports.beforeEach = function(context) { - const desc = context.currentTest.fullTitle(); - - logger.log(desc); - logger.log(new Array(1 + desc.length).join("=")); -}; - /** * Create a spy for an object and automatically spy its methods. * @param {*} constr The class constructor (used with 'new') @@ -71,7 +58,7 @@ module.exports.mock = function(constr, name) { for (const key in constr.prototype) { // eslint-disable-line guard-for-in try { if (constr.prototype[key] instanceof Function) { - result[key] = expect.createSpy(); + result[key] = jest.fn(); } } catch (ex) { // Direct access to some non-function fields of DOM prototypes may @@ -377,9 +364,9 @@ module.exports.setHttpResponses = function setHttpResponses( client._http = [ "authedRequest", "authedRequestWithPrefix", "getContentUri", "request", "requestWithPrefix", "uploadContent", - ].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {}); - client._http.authedRequest.andCall(httpReq); - client._http.authedRequestWithPrefix.andCall(httpReq); - client._http.requestWithPrefix.andCall(httpReq); - client._http.request.andCall(httpReq); + ].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}); + client._http.authedRequest.mockImplementation(httpReq); + client._http.authedRequestWithPrefix.mockImplementation(httpReq); + client._http.requestWithPrefix.mockImplementation(httpReq); + client._http.request.mockImplementation(httpReq); }; diff --git a/spec/unit/autodiscovery.spec.js b/spec/unit/autodiscovery.spec.js index 6d4e5a3e9..980c2eece 100644 --- a/spec/unit/autodiscovery.spec.js +++ b/spec/unit/autodiscovery.spec.js @@ -18,11 +18,9 @@ limitations under the License. import 'source-map-support/register'; import Promise from 'bluebird'; const sdk = require("../.."); -const utils = require("../test-utils"); const AutoDiscovery = sdk.AutoDiscovery; -import expect from 'expect'; import MockHttpBackend from "matrix-mock-request"; @@ -30,7 +28,6 @@ describe("AutoDiscovery", function() { let httpBackend = null; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this httpBackend = new MockHttpBackend(); sdk.request(httpBackend.requestFn); }); @@ -416,8 +413,8 @@ describe("AutoDiscovery", function() { ]); }); - it("should return FAIL_ERROR when the identity server configuration is wrong " + - "(missing base_url)", function() { + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + + "is wrong (missing base_url)", function() { httpBackend.when("GET", "/_matrix/client/versions").check((req) => { expect(req.opts.uri) .toEqual("https://chat.example.org/_matrix/client/versions"); @@ -438,14 +435,14 @@ describe("AutoDiscovery", function() { AutoDiscovery.findClientConfig("example.org").then((conf) => { const expected = { "m.homeserver": { - state: "FAIL_ERROR", - error: AutoDiscovery.ERROR_INVALID_IS, + state: "SUCCESS", + error: null, // We still expect the base_url to be here for debugging purposes. base_url: "https://chat.example.org", }, "m.identity_server": { - state: "FAIL_ERROR", + state: "FAIL_PROMPT", error: AutoDiscovery.ERROR_INVALID_IS_BASE_URL, base_url: null, }, @@ -456,8 +453,8 @@ describe("AutoDiscovery", function() { ]); }); - it("should return FAIL_ERROR when the identity server configuration is wrong " + - "(empty base_url)", function() { + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + + "is wrong (empty base_url)", function() { httpBackend.when("GET", "/_matrix/client/versions").check((req) => { expect(req.opts.uri) .toEqual("https://chat.example.org/_matrix/client/versions"); @@ -478,14 +475,14 @@ describe("AutoDiscovery", function() { AutoDiscovery.findClientConfig("example.org").then((conf) => { const expected = { "m.homeserver": { - state: "FAIL_ERROR", - error: AutoDiscovery.ERROR_INVALID_IS, + state: "SUCCESS", + error: null, // We still expect the base_url to be here for debugging purposes. base_url: "https://chat.example.org", }, "m.identity_server": { - state: "FAIL_ERROR", + state: "FAIL_PROMPT", error: AutoDiscovery.ERROR_INVALID_IS_BASE_URL, base_url: null, }, @@ -496,8 +493,8 @@ describe("AutoDiscovery", function() { ]); }); - it("should return FAIL_ERROR when the identity server configuration is wrong " + - "(validation error: 404)", function() { + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + + "is wrong (validation error: 404)", function() { httpBackend.when("GET", "/_matrix/client/versions").check((req) => { expect(req.opts.uri) .toEqual("https://chat.example.org/_matrix/client/versions"); @@ -519,14 +516,14 @@ describe("AutoDiscovery", function() { AutoDiscovery.findClientConfig("example.org").then((conf) => { const expected = { "m.homeserver": { - state: "FAIL_ERROR", - error: AutoDiscovery.ERROR_INVALID_IS, + state: "SUCCESS", + error: null, // We still expect the base_url to be here for debugging purposes. base_url: "https://chat.example.org", }, "m.identity_server": { - state: "FAIL_ERROR", + state: "FAIL_PROMPT", error: AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER, base_url: "https://identity.example.org", }, @@ -537,8 +534,8 @@ describe("AutoDiscovery", function() { ]); }); - it("should return FAIL_ERROR when the identity server configuration is wrong " + - "(validation error: 500)", function() { + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + + "is wrong (validation error: 500)", function() { httpBackend.when("GET", "/_matrix/client/versions").check((req) => { expect(req.opts.uri) .toEqual("https://chat.example.org/_matrix/client/versions"); @@ -560,14 +557,14 @@ describe("AutoDiscovery", function() { AutoDiscovery.findClientConfig("example.org").then((conf) => { const expected = { "m.homeserver": { - state: "FAIL_ERROR", - error: AutoDiscovery.ERROR_INVALID_IS, + state: "SUCCESS", + error: null, // We still expect the base_url to be here for debugging purposes base_url: "https://chat.example.org", }, "m.identity_server": { - state: "FAIL_ERROR", + state: "FAIL_PROMPT", error: AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER, base_url: "https://identity.example.org", }, diff --git a/spec/unit/content-repo.spec.js b/spec/unit/content-repo.spec.js index 61b6fceb1..70174fc77 100644 --- a/spec/unit/content-repo.spec.js +++ b/spec/unit/content-repo.spec.js @@ -1,17 +1,10 @@ "use strict"; import 'source-map-support/register'; const ContentRepo = require("../../lib/content-repo"); -const testUtils = require("../test-utils"); - -import expect from 'expect'; describe("ContentRepo", function() { const baseUrl = "https://my.home.server"; - beforeEach(function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - describe("getHttpUriForMxc", function() { it("should do nothing to HTTP URLs when allowing direct links", function() { const httpUrl = "http://example.com/image.jpeg"; diff --git a/spec/unit/crypto.spec.js b/spec/unit/crypto.spec.js index 1c25e6f68..55acf8b04 100644 --- a/spec/unit/crypto.spec.js +++ b/spec/unit/crypto.spec.js @@ -3,7 +3,6 @@ import 'source-map-support/register'; import '../olm-loader'; import Crypto from '../../lib/crypto'; -import expect from 'expect'; import WebStorageSessionStore from '../../lib/store/session/webstorage'; import MemoryCryptoStore from '../../lib/crypto/store/memory-crypto-store.js'; @@ -12,7 +11,6 @@ import TestClient from '../TestClient'; import {MatrixEvent} from '../../lib/models/event'; import Room from '../../lib/models/room'; import olmlib from '../../lib/crypto/olmlib'; -import lolex from 'lolex'; const EventEmitter = require("events").EventEmitter; @@ -20,13 +18,15 @@ const sdk = require("../.."); const Olm = global.Olm; +jest.useFakeTimers(); + describe("Crypto", function() { if (!sdk.CRYPTO_ENABLED) { return; } - beforeEach(function(done) { - Olm.init().then(done); + beforeAll(function() { + return Olm.init(); }); it("Crypto exposes the correct olm library version", function() { @@ -76,9 +76,9 @@ describe("Crypto", function() { }); mockBaseApis = { - sendToDevice: expect.createSpy(), - getKeyBackupVersion: expect.createSpy(), - isGuest: expect.createSpy(), + sendToDevice: jest.fn(), + getKeyBackupVersion: jest.fn(), + isGuest: jest.fn(), }; mockRoomList = {}; @@ -110,15 +110,16 @@ describe("Crypto", function() { }); fakeEmitter.emit('toDeviceEvent', { - getType: expect.createSpy().andReturn('m.room.message'), - getContent: expect.createSpy().andReturn({ + getId: jest.fn().mockReturnValue("$wedged"), + getType: jest.fn().mockReturnValue('m.room.message'), + getContent: jest.fn().mockReturnValue({ msgtype: 'm.bad.encrypted', }), - getWireContent: expect.createSpy().andReturn({ + getWireContent: jest.fn().mockReturnValue({ algorithm: 'm.olm.v1.curve25519-aes-sha2', sender_key: 'this is a key', }), - getSender: expect.createSpy().andReturn('@bob:home.server'), + getSender: jest.fn().mockReturnValue('@bob:home.server'), }); await prom; @@ -245,7 +246,7 @@ describe("Crypto", function() { await bobDecryptor.onRoomKeyEvent(ksEvent); await eventPromise; expect(events[0].getContent().msgtype).toBe("m.bad.encrypted"); - expect(events[1].getContent().msgtype).toNotBe("m.bad.encrypted"); + expect(events[1].getContent().msgtype).not.toBe("m.bad.encrypted"); const cryptoStore = bobClient._cryptoStore; const eventContent = events[0].getWireContent(); @@ -260,7 +261,7 @@ describe("Crypto", function() { // the room key request should still be there, since we haven't // decrypted everything expect(await cryptoStore.getOutgoingRoomKeyRequest(roomKeyRequestBody)) - .toExist(); + .toBeDefined(); // keyshare the session key starting at the first message, so // that it can now be decrypted @@ -268,10 +269,10 @@ describe("Crypto", function() { ksEvent = await keyshareEventForEvent(events[0], 0); await bobDecryptor.onRoomKeyEvent(ksEvent); await eventPromise; - expect(events[0].getContent().msgtype).toNotBe("m.bad.encrypted"); + expect(events[0].getContent().msgtype).not.toBe("m.bad.encrypted"); // the room key request should be gone since we've now decypted everything expect(await cryptoStore.getOutgoingRoomKeyRequest(roomKeyRequestBody)) - .toNotExist(); + .toBeFalsy(); }, ); @@ -296,7 +297,7 @@ describe("Crypto", function() { sender_key: "senderkey", }; expect(await cryptoStore.getOutgoingRoomKeyRequest(roomKeyRequestBody)) - .toExist(); + .toBeDefined(); }); it("uses a new txnid for re-requesting keys", async function() { @@ -329,38 +330,32 @@ describe("Crypto", function() { aliceClient.startClient(); - const clock = lolex.install(); + let promise; + // make a room key request, and record the transaction ID for the + // sendToDevice call + ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); + await aliceClient.cancelAndResendEventRoomKeyRequest(event); + jest.runAllTimers(); + let args = await promise; + const txnId = args[2]; + jest.runAllTimers(); - try { - let promise; - // make a room key request, and record the transaction ID for the - // sendToDevice call - ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); - await aliceClient.cancelAndResendEventRoomKeyRequest(event); - clock.runToLast(); - let args = await promise; - const txnId = args[2]; - clock.runToLast(); + // give the room key request manager time to update the state + // of the request + await Promise.resolve(); - // give the room key request manager time to update the state - // of the request - await Promise.resolve(); - - // cancel and resend the room key request - ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); - await aliceClient.cancelAndResendEventRoomKeyRequest(event); - clock.runToLast(); - // the first call to sendToDevice will be the cancellation - args = await promise; - // the second call to sendToDevice will be the key request - ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); - clock.runToLast(); - args = await promise; - clock.runToLast(); - expect(args[2]).toNotBe(txnId); - } finally { - clock.uninstall(); - } + // cancel and resend the room key request + ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); + await aliceClient.cancelAndResendEventRoomKeyRequest(event); + jest.runAllTimers(); + // the first call to sendToDevice will be the cancellation + args = await promise; + // the second call to sendToDevice will be the key request + ({promise, func: aliceClient.sendToDevice} = awaitFunctionCall()); + jest.runAllTimers(); + args = await promise; + jest.runAllTimers(); + expect(args[2]).not.toBe(txnId); }); }); }); diff --git a/spec/unit/crypto/DeviceList.spec.js b/spec/unit/crypto/DeviceList.spec.js index 33c1e4955..14d4d91e3 100644 --- a/spec/unit/crypto/DeviceList.spec.js +++ b/spec/unit/crypto/DeviceList.spec.js @@ -17,11 +17,9 @@ limitations under the License. import DeviceList from '../../../lib/crypto/DeviceList'; import MemoryCryptoStore from '../../../lib/crypto/store/memory-crypto-store.js'; -import testUtils from '../../test-utils'; import utils from '../../../lib/utils'; import logger from '../../../src/logger'; -import expect from 'expect'; import Promise from 'bluebird'; const signedDeviceList = { @@ -60,11 +58,9 @@ describe('DeviceList', function() { let deviceLists = []; beforeEach(function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - deviceLists = []; - downloadSpy = expect.createSpy(); + downloadSpy = jest.fn(); cryptoStore = new MemoryCryptoStore(); }); @@ -92,7 +88,7 @@ describe('DeviceList', function() { dl.startTrackingDeviceList('@test1:sw1v.org'); const queryDefer1 = Promise.defer(); - downloadSpy.andReturn(queryDefer1.promise); + downloadSpy.mockReturnValue(queryDefer1.promise); const prom1 = dl.refreshOutdatedDeviceLists(); expect(downloadSpy).toHaveBeenCalledWith(['@test1:sw1v.org'], {}); @@ -111,15 +107,15 @@ describe('DeviceList', function() { dl.startTrackingDeviceList('@test1:sw1v.org'); const queryDefer1 = Promise.defer(); - downloadSpy.andReturn(queryDefer1.promise); + downloadSpy.mockReturnValue(queryDefer1.promise); const prom1 = dl.refreshOutdatedDeviceLists(); expect(downloadSpy).toHaveBeenCalledWith(['@test1:sw1v.org'], {}); - downloadSpy.reset(); + downloadSpy.mockReset(); // outdated notif arrives while the request is in flight. const queryDefer2 = Promise.defer(); - downloadSpy.andReturn(queryDefer2.promise); + downloadSpy.mockReturnValue(queryDefer2.promise); dl.invalidateUserDeviceList('@test1:sw1v.org'); dl.refreshOutdatedDeviceLists(); @@ -136,10 +132,10 @@ describe('DeviceList', function() { // uh-oh; user restarts before second request completes. The new instance // should know we never got a complete device list. logger.log("Creating new devicelist to simulate app reload"); - downloadSpy.reset(); + downloadSpy.mockReset(); const dl2 = createTestDeviceList(); const queryDefer3 = Promise.defer(); - downloadSpy.andReturn(queryDefer3.promise); + downloadSpy.mockReturnValue(queryDefer3.promise); const prom3 = dl2.refreshOutdatedDeviceLists(); expect(downloadSpy).toHaveBeenCalledWith(['@test1:sw1v.org'], {}); diff --git a/spec/unit/crypto/algorithms/megolm.spec.js b/spec/unit/crypto/algorithms/megolm.spec.js index 0616ff0ae..ad5e7f20a 100644 --- a/spec/unit/crypto/algorithms/megolm.spec.js +++ b/spec/unit/crypto/algorithms/megolm.spec.js @@ -1,6 +1,5 @@ import '../../../olm-loader'; -import expect from 'expect'; import Promise from 'bluebird'; import sdk from '../../../..'; @@ -26,16 +25,16 @@ describe("MegolmDecryption", function() { return; } + beforeAll(function() { + return Olm.init(); + }); + let megolmDecryption; let mockOlmLib; let mockCrypto; let mockBaseApis; beforeEach(async function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - - await Olm.init(); - mockCrypto = testUtils.mock(Crypto, 'Crypto'); mockBaseApis = {}; @@ -55,9 +54,9 @@ describe("MegolmDecryption", function() { // we stub out the olm encryption bits mockOlmLib = {}; - mockOlmLib.ensureOlmSessionsForDevices = expect.createSpy(); + mockOlmLib.ensureOlmSessionsForDevices = jest.fn(); mockOlmLib.encryptMessageForDevice = - expect.createSpy().andReturn(Promise.resolve()); + jest.fn().mockReturnValue(Promise.resolve()); megolmDecryption.olmlib = mockOlmLib; }); @@ -135,22 +134,22 @@ describe("MegolmDecryption", function() { // set up some pre-conditions for the share call const deviceInfo = {}; - mockCrypto.getStoredDevice.andReturn(deviceInfo); + mockCrypto.getStoredDevice.mockReturnValue(deviceInfo); - mockOlmLib.ensureOlmSessionsForDevices.andReturn( + mockOlmLib.ensureOlmSessionsForDevices.mockReturnValue( Promise.resolve({'@alice:foo': {'alidevice': { sessionId: 'alisession', }}}), ); const awaitEncryptForDevice = new Promise((res, rej) => { - mockOlmLib.encryptMessageForDevice.andCall(() => { + mockOlmLib.encryptMessageForDevice.mockImplementation(() => { res(); return Promise.resolve(); }); }); - mockBaseApis.sendToDevice = expect.createSpy(); + mockBaseApis.sendToDevice = jest.fn(); // do the share megolmDecryption.shareKeysWithDevice(keyRequest); @@ -160,21 +159,20 @@ describe("MegolmDecryption", function() { }).then(() => { // check that it called encryptMessageForDevice with // appropriate args. - expect(mockOlmLib.encryptMessageForDevice.calls.length) - .toEqual(1); + expect(mockOlmLib.encryptMessageForDevice).toBeCalledTimes(1); - const call = mockOlmLib.encryptMessageForDevice.calls[0]; - const payload = call.arguments[6]; + const call = mockOlmLib.encryptMessageForDevice.mock.calls[0]; + const payload = call[6]; expect(payload.type).toEqual("m.forwarded_room_key"); - expect(payload.content).toInclude({ + expect(payload.content).toMatchObject({ sender_key: "SENDER_CURVE25519", sender_claimed_ed25519_key: "SENDER_ED25519", session_id: groupSession.session_id(), chain_index: 0, forwarding_curve25519_key_chain: [], }); - expect(payload.content.session_key).toExist(); + expect(payload.content.session_key).toBeDefined(); }); }); @@ -201,13 +199,12 @@ describe("MegolmDecryption", function() { origin_server_ts: 1507753886000, }); - const successHandler = expect.createSpy(); - const failureHandler = expect.createSpy() - .andCall((err) => { - expect(err.toString()).toMatch( - /Duplicate message index, possible replay attack/, - ); - }); + const successHandler = jest.fn(); + const failureHandler = jest.fn((err) => { + expect(err.toString()).toMatch( + /Duplicate message index, possible replay attack/, + ); + }); return megolmDecryption.decryptEvent(event1).then((res) => { const event2 = new MatrixEvent({ @@ -228,7 +225,7 @@ describe("MegolmDecryption", function() { successHandler, failureHandler, ).then(() => { - expect(successHandler).toNotHaveBeenCalled(); + expect(successHandler).not.toHaveBeenCalled(); expect(failureHandler).toHaveBeenCalled(); }); }); @@ -266,10 +263,10 @@ describe("MegolmDecryption", function() { const cryptoStore = new MemoryCryptoStore(mockStorage); const olmDevice = new OlmDevice(cryptoStore); - olmDevice.verifySignature = expect.createSpy(); + olmDevice.verifySignature = jest.fn(); await olmDevice.init(); - mockBaseApis.claimOneTimeKeys = expect.createSpy().andReturn(Promise.resolve({ + mockBaseApis.claimOneTimeKeys = jest.fn().mockReturnValue(Promise.resolve({ one_time_keys: { '@alice:home.server': { aliceDevice: { @@ -285,18 +282,18 @@ describe("MegolmDecryption", function() { }, }, })); - mockBaseApis.sendToDevice = expect.createSpy().andReturn(Promise.resolve()); + mockBaseApis.sendToDevice = jest.fn().mockReturnValue(Promise.resolve()); - mockCrypto.downloadKeys.andReturn(Promise.resolve({ + mockCrypto.downloadKeys.mockReturnValue(Promise.resolve({ '@alice:home.server': { aliceDevice: { deviceId: 'aliceDevice', - isBlocked: expect.createSpy().andReturn(false), - isUnverified: expect.createSpy().andReturn(false), - getIdentityKey: expect.createSpy().andReturn( + isBlocked: jest.fn().mockReturnValue(false), + isUnverified: jest.fn().mockReturnValue(false), + getIdentityKey: jest.fn().mockReturnValue( 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE', ), - getFingerprint: expect.createSpy().andReturn(''), + getFingerprint: jest.fn().mockReturnValue(''), }, }, })); @@ -312,10 +309,10 @@ describe("MegolmDecryption", function() { }, }); const mockRoom = { - getEncryptionTargetMembers: expect.createSpy().andReturn( + getEncryptionTargetMembers: jest.fn().mockReturnValue( [{userId: "@alice:home.server"}], ), - getBlacklistUnverifiedDevices: expect.createSpy().andReturn(false), + getBlacklistUnverifiedDevices: jest.fn().mockReturnValue(false), }; const ct1 = await megolmEncryption.encryptMessage(mockRoom, "a.fake.type", { body: "Some text", @@ -323,25 +320,25 @@ describe("MegolmDecryption", function() { expect(mockRoom.getEncryptionTargetMembers).toHaveBeenCalled(); // this should have claimed a key for alice as it's starting a new session - expect(mockBaseApis.claimOneTimeKeys).toHaveBeenCalled( + expect(mockBaseApis.claimOneTimeKeys).toHaveBeenCalledWith( [['@alice:home.server', 'aliceDevice']], 'signed_curve25519', ); expect(mockCrypto.downloadKeys).toHaveBeenCalledWith( ['@alice:home.server'], false, ); expect(mockBaseApis.sendToDevice).toHaveBeenCalled(); - expect(mockBaseApis.claimOneTimeKeys).toHaveBeenCalled( + expect(mockBaseApis.claimOneTimeKeys).toHaveBeenCalledWith( [['@alice:home.server', 'aliceDevice']], 'signed_curve25519', ); - mockBaseApis.claimOneTimeKeys.reset(); + mockBaseApis.claimOneTimeKeys.mockReset(); const ct2 = await megolmEncryption.encryptMessage(mockRoom, "a.fake.type", { body: "Some more text", }); // this should *not* have claimed a key as it should be using the same session - expect(mockBaseApis.claimOneTimeKeys).toNotHaveBeenCalled(); + expect(mockBaseApis.claimOneTimeKeys).not.toHaveBeenCalled(); // likewise they should show the same session ID expect(ct2.session_id).toEqual(ct1.session_id); diff --git a/spec/unit/crypto/algorithms/olm.spec.js b/spec/unit/crypto/algorithms/olm.spec.js index 14e6bbf66..04bd0c322 100644 --- a/spec/unit/crypto/algorithms/olm.spec.js +++ b/spec/unit/crypto/algorithms/olm.spec.js @@ -16,10 +16,8 @@ limitations under the License. import '../../../olm-loader'; -import expect from 'expect'; import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js'; import MockStorageApi from '../../../MockStorageApi'; -import testUtils from '../../../test-utils'; import logger from '../../../../src/logger'; import OlmDevice from '../../../../lib/crypto/OlmDevice'; @@ -50,14 +48,14 @@ describe("OlmDecryption", function() { return; } + beforeAll(function() { + return global.Olm.init(); + }); + let aliceOlmDevice; let bobOlmDevice; beforeEach(async function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - - await global.Olm.init(); - aliceOlmDevice = makeOlmDevice(); bobOlmDevice = makeOlmDevice(); await aliceOlmDevice.init(); diff --git a/spec/unit/crypto/backup.spec.js b/spec/unit/crypto/backup.spec.js index 236a7ed28..7413ee0da 100644 --- a/spec/unit/crypto/backup.spec.js +++ b/spec/unit/crypto/backup.spec.js @@ -16,7 +16,6 @@ limitations under the License. import '../../olm-loader'; -import expect from 'expect'; import Promise from 'bluebird'; import sdk from '../../..'; @@ -98,16 +97,16 @@ function makeTestClient(sessionStore, cryptoStore) { const scheduler = [ "getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction", - ].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {}); + ].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}); const store = [ "getRoom", "getRooms", "getUser", "getSyncToken", "scrollback", "save", "wantsSave", "setSyncToken", "storeEvents", "storeRoom", "storeUser", "getFilterIdByName", "setFilterIdByName", "getFilter", "storeFilter", "getSyncAccumulator", "startup", "deleteAllData", - ].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {}); - store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null)); - store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null)); - store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null)); + ].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}); + store.getSavedSync = jest.fn().mockReturnValue(Promise.resolve(null)); + store.getSavedSyncToken = jest.fn().mockReturnValue(Promise.resolve(null)); + store.setSyncData = jest.fn().mockReturnValue(Promise.resolve(null)); return new MatrixClient({ baseUrl: "https://my.home.server", idBaseUrl: "https://identity.server", @@ -129,6 +128,10 @@ describe("MegolmBackup", function() { return; } + beforeAll(function() { + return Olm.init(); + }); + let olmDevice; let mockOlmLib; let mockCrypto; @@ -137,9 +140,6 @@ describe("MegolmBackup", function() { let cryptoStore; let megolmDecryption; beforeEach(async function() { - await Olm.init(); - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - mockCrypto = testUtils.mock(Crypto, 'Crypto'); mockCrypto.backupKey = new Olm.PkEncryption(); mockCrypto.backupKey.set_recipient_key( @@ -155,9 +155,9 @@ describe("MegolmBackup", function() { // we stub out the olm encryption bits mockOlmLib = {}; - mockOlmLib.ensureOlmSessionsForDevices = expect.createSpy(); + mockOlmLib.ensureOlmSessionsForDevices = jest.fn(); mockOlmLib.encryptMessageForDevice = - expect.createSpy().andReturn(Promise.resolve()); + jest.fn().mockReturnValue(Promise.resolve()); }); describe("backup", function() { @@ -218,7 +218,7 @@ describe("MegolmBackup", function() { }; mockCrypto.cancelRoomKeyRequest = function() {}; - mockCrypto.backupGroupSession = expect.createSpy(); + mockCrypto.backupGroupSession = jest.fn(); return event.attemptDecryption(mockCrypto).then(() => { return megolmDecryption.onRoomKeyEvent(event); @@ -279,7 +279,7 @@ describe("MegolmBackup", function() { callback, method, path, queryParams, data, opts, ) { ++numCalls; - expect(numCalls).toBeLessThanOrEqualTo(1); + expect(numCalls).toBeLessThanOrEqual(1); if (numCalls >= 2) { // exit out of retry loop if there's something wrong reject(new Error("authedRequest called too many timmes")); @@ -288,8 +288,8 @@ describe("MegolmBackup", function() { expect(method).toBe("PUT"); expect(path).toBe("/room_keys/keys"); expect(queryParams.version).toBe(1); - expect(data.rooms[ROOM_ID].sessions).toExist(); - expect(data.rooms[ROOM_ID].sessions).toIncludeKey( + expect(data.rooms[ROOM_ID].sessions).toBeDefined(); + expect(data.rooms[ROOM_ID].sessions).toHaveProperty( groupSession.session_id(), ); resolve(); @@ -343,7 +343,7 @@ describe("MegolmBackup", function() { callback, method, path, queryParams, data, opts, ) { ++numCalls; - expect(numCalls).toBeLessThanOrEqualTo(1); + expect(numCalls).toBeLessThanOrEqual(1); if (numCalls >= 2) { // exit out of retry loop if there's something wrong reject(new Error("authedRequest called too many timmes")); @@ -382,16 +382,16 @@ describe("MegolmBackup", function() { const scheduler = [ "getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction", - ].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {}); + ].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}); const store = [ "getRoom", "getRooms", "getUser", "getSyncToken", "scrollback", "save", "wantsSave", "setSyncToken", "storeEvents", "storeRoom", "storeUser", "getFilterIdByName", "setFilterIdByName", "getFilter", "storeFilter", "getSyncAccumulator", "startup", "deleteAllData", - ].reduce((r, k) => {r[k] = expect.createSpy(); return r;}, {}); - store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null)); - store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null)); - store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null)); + ].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}); + store.getSavedSync = jest.fn().mockReturnValue(Promise.resolve(null)); + store.getSavedSyncToken = jest.fn().mockReturnValue(Promise.resolve(null)); + store.setSyncData = jest.fn().mockReturnValue(Promise.resolve(null)); const client = new MatrixClient({ baseUrl: "https://my.home.server", idBaseUrl: "https://identity.server", @@ -449,7 +449,7 @@ describe("MegolmBackup", function() { callback, method, path, queryParams, data, opts, ) { ++numCalls; - expect(numCalls).toBeLessThanOrEqualTo(2); + expect(numCalls).toBeLessThanOrEqual(2); if (numCalls >= 3) { // exit out of retry loop if there's something wrong reject(new Error("authedRequest called too many timmes")); @@ -458,8 +458,8 @@ describe("MegolmBackup", function() { expect(method).toBe("PUT"); expect(path).toBe("/room_keys/keys"); expect(queryParams.version).toBe(1); - expect(data.rooms[ROOM_ID].sessions).toExist(); - expect(data.rooms[ROOM_ID].sessions).toIncludeKey( + expect(data.rooms[ROOM_ID].sessions).toBeDefined(); + expect(data.rooms[ROOM_ID].sessions).toHaveProperty( groupSession.session_id(), ); if (numCalls > 1) { diff --git a/spec/unit/crypto/cross-signing.spec.js b/spec/unit/crypto/cross-signing.spec.js index c26356d98..06840b74a 100644 --- a/spec/unit/crypto/cross-signing.spec.js +++ b/spec/unit/crypto/cross-signing.spec.js @@ -17,7 +17,6 @@ limitations under the License. import '../../olm-loader'; -import expect from 'expect'; import anotherjson from 'another-json'; import olmlib from '../../../lib/crypto/olmlib'; @@ -56,21 +55,20 @@ describe("Cross Signing", function() { return; } - beforeEach(async function() { - await global.Olm.init(); + beforeAll(function() { + return global.Olm.init(); }); it("should sign the master key with the device key", async function() { const alice = await makeTestClient( {userId: "@alice:example.com", deviceId: "Osborne2"}, ); - alice.uploadDeviceSigningKeys = expect.createSpy() - .andCall(async (auth, keys) => { - await olmlib.verifySignature( - alice._crypto._olmDevice, keys.master_key, "@alice:example.com", - "Osborne2", alice._crypto._olmDevice.deviceEd25519Key, - ); - }); + alice.uploadDeviceSigningKeys = jest.fn(async (auth, keys) => { + await olmlib.verifySignature( + alice._crypto._olmDevice, keys.master_key, "@alice:example.com", + "Osborne2", alice._crypto._olmDevice.deviceEd25519Key, + ); + }); alice.uploadKeySignatures = async () => {}; // set Alice's cross-signing key await alice.resetCrossSigningKeys(); @@ -146,7 +144,7 @@ describe("Cross Signing", function() { }); const uploadSigsPromise = new Promise((resolve, reject) => { - alice.uploadKeySignatures = expect.createSpy().andCall(async (content) => { + alice.uploadKeySignatures = jest.fn(async (content) => { await olmlib.verifySignature( alice._crypto._olmDevice, content["@alice:example.com"][ @@ -732,7 +730,7 @@ describe("Cross Signing", function() { { cryptoCallbacks: { shouldUpgradeDeviceVerifications: (verifs) => { - expect(verifs.users["@bob:example.com"]).toExist(); + expect(verifs.users["@bob:example.com"]).toBeDefined(); upgradeResolveFunc(); return ["@bob:example.com"]; }, diff --git a/spec/unit/crypto/secrets.spec.js b/spec/unit/crypto/secrets.spec.js index fb7948143..845f756d3 100644 --- a/spec/unit/crypto/secrets.spec.js +++ b/spec/unit/crypto/secrets.spec.js @@ -16,7 +16,6 @@ limitations under the License. import '../../olm-loader'; -import expect from 'expect'; import { MatrixEvent } from '../../../lib/models/event'; import { SECRET_STORAGE_ALGORITHM_V1 } from '../../../lib/crypto/SecretStorage'; @@ -41,8 +40,8 @@ describe("Secrets", function() { return; } - beforeEach(async function() { - await global.Olm.init(); + beforeAll(function() { + return global.Olm.init(); }); it("should store and retrieve a secret", async function() { @@ -62,7 +61,7 @@ describe("Secrets", function() { }, }; - const getKey = expect.createSpy().andCall(e => { + const getKey = jest.fn(e => { expect(Object.keys(e.keys)).toEqual(["abc"]); return ['abc', privkey]; }); diff --git a/spec/unit/crypto/verification/qr_code.spec.js b/spec/unit/crypto/verification/qr_code.spec.js index a64c5d5af..b59bddb72 100644 --- a/spec/unit/crypto/verification/qr_code.spec.js +++ b/spec/unit/crypto/verification/qr_code.spec.js @@ -21,7 +21,6 @@ try { logger.warn("unable to run device verification tests: libolm not available"); } -import expect from 'expect'; import DeviceInfo from '../../../../lib/crypto/deviceinfo'; import {ShowQRCode, ScanQRCode} from '../../../../lib/crypto/verification/QRCode'; @@ -34,8 +33,8 @@ describe("QR code verification", function() { return; } - beforeEach(async function() { - await Olm.init(); + beforeAll(function() { + return Olm.init(); }); describe("showing", function() { @@ -47,7 +46,7 @@ describe("QR code verification", function() { return "device+ed25519+key"; }, }); - const spy = expect.createSpy().andCall((e) => { + const spy = jest.fn((e) => { qrCode.done(); }); qrCode.on("show_qr_code", spy); @@ -77,8 +76,8 @@ describe("QR code verification", function() { "ABCDEFG", ); const client = { - getStoredDevice: expect.createSpy().andReturn(device), - setDeviceVerified: expect.createSpy(), + getStoredDevice: jest.fn().mockReturnValue(device), + setDeviceVerified: jest.fn(), }; const qrCode = new ScanQRCode(client); qrCode.on("confirm_user_id", ({userId, confirm}) => { @@ -100,18 +99,18 @@ describe("QR code verification", function() { it("should error when the user ID doesn't match", async function() { const client = { - getStoredDevice: expect.createSpy(), - setDeviceVerified: expect.createSpy(), + getStoredDevice: jest.fn(), + setDeviceVerified: jest.fn(), }; const qrCode = new ScanQRCode(client, "@bob:example.com", "ABCDEFG"); qrCode.on("scan", ({done}) => { done(QR_CODE_URL); }); - const spy = expect.createSpy(); + const spy = jest.fn(); await qrCode.verify().catch(spy); expect(spy).toHaveBeenCalled(); - expect(client.getStoredDevice).toNotHaveBeenCalled(); - expect(client.setDeviceVerified).toNotHaveBeenCalled(); + expect(client.getStoredDevice).not.toHaveBeenCalled(); + expect(client.setDeviceVerified).not.toHaveBeenCalled(); }); it("should error if the key doesn't match", async function() { @@ -129,18 +128,18 @@ describe("QR code verification", function() { "ABCDEFG", ); const client = { - getStoredDevice: expect.createSpy().andReturn(device), - setDeviceVerified: expect.createSpy(), + getStoredDevice: jest.fn().mockReturnValue(device), + setDeviceVerified: jest.fn(), }; const qrCode = new ScanQRCode(client, "@alice:example.com", "ABCDEFG"); qrCode.on("scan", ({done}) => { done(QR_CODE_URL); }); - const spy = expect.createSpy(); + const spy = jest.fn(); await qrCode.verify().catch(spy); expect(spy).toHaveBeenCalled(); expect(client.getStoredDevice).toHaveBeenCalled(); - expect(client.setDeviceVerified).toNotHaveBeenCalled(); + expect(client.setDeviceVerified).not.toHaveBeenCalled(); }); }); }); diff --git a/spec/unit/crypto/verification/request.spec.js b/spec/unit/crypto/verification/request.spec.js index 355597d04..4fb103457 100644 --- a/spec/unit/crypto/verification/request.spec.js +++ b/spec/unit/crypto/verification/request.spec.js @@ -21,8 +21,6 @@ try { logger.warn("unable to run device verification tests: libolm not available"); } -import expect from 'expect'; - import {verificationMethods} from '../../../../lib/crypto'; import SAS from '../../../../lib/crypto/verification/SAS'; @@ -37,8 +35,8 @@ describe("verification request", function() { return; } - beforeEach(async function() { - await Olm.init(); + beforeAll(function() { + return Olm.init(); }); it("should request and accept a verification", async function() { @@ -74,7 +72,7 @@ describe("verification request", function() { bobVerifier._endTimer(); }); const aliceVerifier = await alice.client.requestVerification("@bob:example.com"); - expect(aliceVerifier).toBeAn(SAS); + expect(aliceVerifier).toBeInstanceOf(SAS); // XXX: Private function access (but it's a test, so we're okay) aliceVerifier._endTimer(); diff --git a/spec/unit/crypto/verification/sas.spec.js b/spec/unit/crypto/verification/sas.spec.js index cee4d1449..5511c6652 100644 --- a/spec/unit/crypto/verification/sas.spec.js +++ b/spec/unit/crypto/verification/sas.spec.js @@ -21,7 +21,6 @@ try { logger.warn("unable to run device verification tests: libolm not available"); } -import expect from 'expect'; import olmlib from '../../../../lib/crypto/olmlib'; import sdk from '../../../..'; @@ -46,8 +45,8 @@ describe("SAS verification", function() { return; } - beforeEach(async function() { - await Olm.init(); + beforeAll(function() { + return Olm.init(); }); it("should error on an unexpected event", async function() { @@ -57,16 +56,15 @@ describe("SAS verification", function() { type: "es.inquisition", content: {}, })); - const spy = expect.createSpy(); - await sas.verify() - .catch(spy); + const spy = jest.fn(); + await sas.verify().catch(spy); expect(spy).toHaveBeenCalled(); // Cancel the SAS for cleanup (we started a verification, so abort) sas.cancel(); }); - describe("verification", function() { + describe("verification", () => { let alice; let bob; let aliceSasEvent; @@ -74,7 +72,7 @@ describe("SAS verification", function() { let aliceVerifier; let bobPromise; - beforeEach(async function() { + beforeEach(async () => { [alice, bob] = await makeTestClients( [ {userId: "@alice:example.com", deviceId: "Osborne2"}, @@ -115,14 +113,14 @@ describe("SAS verification", function() { alice.client._crypto._deviceList.storeDevicesForUser( "@bob:example.com", BOB_DEVICES, ); - alice.downloadKeys = () => { + alice.client.downloadKeys = () => { return Promise.resolve(); }; bob.client._crypto._deviceList.storeDevicesForUser( "@alice:example.com", ALICE_DEVICES, ); - bob.downloadKeys = () => { + bob.client.downloadKeys = () => { return Promise.resolve(); }; @@ -171,16 +169,22 @@ describe("SAS verification", function() { } }); }); + afterEach(async () => { + await Promise.all([ + alice.stop(), + bob.stop(), + ]); + }); - it("should verify a key", async function() { + it("should verify a key", async () => { let macMethod; - const origSendToDevice = alice.client.sendToDevice; + const origSendToDevice = bob.client.sendToDevice.bind(bob.client); bob.client.sendToDevice = function(type, map) { if (type === "m.key.verification.accept") { macMethod = map[alice.client.getUserId()][alice.client.deviceId] .message_authentication_code; } - return origSendToDevice.call(this, type, map); + return origSendToDevice(type, map); }; alice.httpBackend.when('POST', '/keys/query').respond(200, { @@ -215,12 +219,12 @@ describe("SAS verification", function() { expect(aliceDevice.isVerified()).toBeTruthy(); }); - it("should be able to verify using the old MAC", async function() { + it("should be able to verify using the old MAC", async () => { // pretend that Alice can only understand the old (incorrect) MAC, // and make sure that she can still verify with Bob let macMethod; - const origSendToDevice = alice.client.sendToDevice; - alice.client.sendToDevice = function(type, map) { + const aliceOrigSendToDevice = alice.client.sendToDevice.bind(alice.client); + alice.client.sendToDevice = (type, map) => { if (type === "m.key.verification.start") { // Note: this modifies not only the message that Bob // receives, but also the copy of the message that Alice @@ -230,14 +234,15 @@ describe("SAS verification", function() { map[bob.client.getUserId()][bob.client.deviceId] .message_authentication_codes = ['hmac-sha256']; } - return origSendToDevice.call(this, type, map); + return aliceOrigSendToDevice(type, map); }; - bob.client.sendToDevice = function(type, map) { + const bobOrigSendToDevice = bob.client.sendToDevice.bind(bob.client); + bob.client.sendToDevice = (type, map) => { if (type === "m.key.verification.accept") { macMethod = map[alice.client.getUserId()][alice.client.deviceId] .message_authentication_code; } - return origSendToDevice.call(this, type, map); + return bobOrigSendToDevice(type, map); }; alice.httpBackend.when('POST', '/keys/query').respond(200, { @@ -270,7 +275,7 @@ describe("SAS verification", function() { expect(aliceDevice.isVerified()).toBeTruthy(); }); - it("should verify a cross-signing key", async function() { + it("should verify a cross-signing key", async () => { alice.httpBackend.when('POST', '/keys/device_signing/upload').respond( 200, {}, ); @@ -289,33 +294,17 @@ describe("SAS verification", function() { }, ); - alice.httpBackend.when('POST', '/keys/query').respond(200, { - failures: {}, - device_keys: { - "@bob:example.com": BOB_DEVICES, - }, - }); - bob.httpBackend.when('POST', '/keys/query').respond(200, { - failures: {}, - device_keys: { - "@alice:example.com": ALICE_DEVICES, - }, - }); - const verifyProm = Promise.all([ aliceVerifier.verify(), bobPromise.then((verifier) => { bob.httpBackend.when( 'POST', '/keys/signatures/upload', ).respond(200, {}); - bob.httpBackend.flush(undefined, 2); + bob.httpBackend.flush(undefined, 1, 2000); return verifier.verify(); }), ]); - await alice.httpBackend.flush(undefined, 1); - console.log("alice reqs flushed"); - await verifyProm; const bobDeviceTrust = alice.client.checkDeviceTrust( @@ -346,11 +335,11 @@ describe("SAS verification", function() { verificationMethods: [verificationMethods.SAS], }, ); - alice.client.setDeviceVerified = expect.createSpy(); + alice.client.setDeviceVerified = jest.fn(); alice.client.downloadKeys = () => { return Promise.resolve(); }; - bob.client.setDeviceVerified = expect.createSpy(); + bob.client.setDeviceVerified = jest.fn(); bob.client.downloadKeys = () => { return Promise.resolve(); }; @@ -368,8 +357,8 @@ describe("SAS verification", function() { verificationMethods.SAS, bob.client.getUserId(), bob.client.deviceId, ); - const aliceSpy = expect.createSpy(); - const bobSpy = expect.createSpy(); + const aliceSpy = jest.fn(); + const bobSpy = jest.fn(); await Promise.all([ aliceVerifier.verify().catch(aliceSpy), bobPromise.then((verifier) => verifier.verify()).catch(bobSpy), @@ -377,9 +366,9 @@ describe("SAS verification", function() { expect(aliceSpy).toHaveBeenCalled(); expect(bobSpy).toHaveBeenCalled(); expect(alice.client.setDeviceVerified) - .toNotHaveBeenCalled(); + .not.toHaveBeenCalled(); expect(bob.client.setDeviceVerified) - .toNotHaveBeenCalled(); + .not.toHaveBeenCalled(); }); describe("verification in DM", function() { @@ -401,7 +390,7 @@ describe("SAS verification", function() { }, ); - alice.client.setDeviceVerified = expect.createSpy(); + alice.client.setDeviceVerified = jest.fn(); alice.client.getDeviceEd25519Key = () => { return "alice+base64+ed25519+key"; }; @@ -419,7 +408,7 @@ describe("SAS verification", function() { return Promise.resolve(); }; - bob.client.setDeviceVerified = expect.createSpy(); + bob.client.setDeviceVerified = jest.fn(); bob.client.getStoredDevice = () => { return DeviceInfo.fromStorage( { @@ -445,7 +434,7 @@ describe("SAS verification", function() { const content = event.getContent(); if (event.getType() === "m.room.message" && content.msgtype === "m.key.verification.request") { - expect(content.methods).toInclude(SAS.NAME); + expect(content.methods).toContain(SAS.NAME); expect(content.to).toBe(bob.client.getUserId()); const verifier = bob.client.acceptVerificationDM(event, SAS.NAME); verifier.on("show_sas", (e) => { diff --git a/spec/unit/event-timeline.spec.js b/spec/unit/event-timeline.spec.js index 85f8a8215..ab4ce9f38 100644 --- a/spec/unit/event-timeline.spec.js +++ b/spec/unit/event-timeline.spec.js @@ -9,8 +9,6 @@ function mockRoomStates(timeline) { timeline._endState = utils.mock(sdk.RoomState, "endState"); } -import expect from 'expect'; - describe("EventTimeline", function() { const roomId = "!foo:bar"; const userA = "@alice:bar"; @@ -18,8 +16,6 @@ describe("EventTimeline", function() { let timeline; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - // XXX: this is a horrid hack; should use sinon or something instead to mock const timelineSet = { room: { roomId: roomId }}; timelineSet.room.getUnfilteredTimelineSet = function() { @@ -78,7 +74,7 @@ describe("EventTimeline", function() { expect(function() { timeline.initialiseState(state); - }).toNotThrow(); + }).not.toThrow(); timeline.addEvent(event, false); expect(function() { timeline.initialiseState(state); @@ -121,7 +117,7 @@ describe("EventTimeline", function() { const next = {b: "b"}; expect(function() { timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS); - }).toNotThrow(); + }).not.toThrow(); expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) .toBe(prev); expect(function() { @@ -130,7 +126,7 @@ describe("EventTimeline", function() { expect(function() { timeline.setNeighbouringTimeline(next, EventTimeline.FORWARDS); - }).toNotThrow(); + }).not.toThrow(); expect(timeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) .toBe(next); expect(function() { @@ -187,14 +183,14 @@ describe("EventTimeline", function() { name: "Old Alice", }; timeline.getState(EventTimeline.FORWARDS).getSentinelMember - .andCall(function(uid) { + .mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); timeline.getState(EventTimeline.BACKWARDS).getSentinelMember - .andCall(function(uid) { + .mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; } @@ -229,14 +225,14 @@ describe("EventTimeline", function() { name: "Old Alice", }; timeline.getState(EventTimeline.FORWARDS).getSentinelMember - .andCall(function(uid) { + .mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); timeline.getState(EventTimeline.BACKWARDS).getSentinelMember - .andCall(function(uid) { + .mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; } @@ -281,7 +277,7 @@ describe("EventTimeline", function() { expect(events[1].forwardLooking).toBe(true); expect(timeline.getState(EventTimeline.BACKWARDS).setStateEvents). - toNotHaveBeenCalled(); + not.toHaveBeenCalled(); }); @@ -311,7 +307,7 @@ describe("EventTimeline", function() { expect(events[1].forwardLooking).toBe(false); expect(timeline.getState(EventTimeline.FORWARDS).setStateEvents). - toNotHaveBeenCalled(); + not.toHaveBeenCalled(); }); }); diff --git a/spec/unit/event.spec.js b/spec/unit/event.spec.js index add962d88..cda4580f9 100644 --- a/spec/unit/event.spec.js +++ b/spec/unit/event.spec.js @@ -17,17 +17,10 @@ limitations under the License. import sdk from '../..'; const MatrixEvent = sdk.MatrixEvent; -import testUtils from '../test-utils'; - -import expect from 'expect'; import Promise from 'bluebird'; import logger from '../../src/logger'; describe("MatrixEvent", () => { - beforeEach(function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - describe(".attemptDecryption", () => { let encryptedEvent; @@ -45,6 +38,7 @@ describe("MatrixEvent", () => { let callCount = 0; let prom2; + let prom2Fulfilled = false; const crypto = { decryptEvent: function() { @@ -54,12 +48,13 @@ describe("MatrixEvent", () => { // schedule a second decryption attempt while // the first one is still running. prom2 = encryptedEvent.attemptDecryption(crypto); + prom2.then(() => prom2Fulfilled = true); const error = new Error("nope"); error.name = 'DecryptionError'; return Promise.reject(error); } else { - expect(prom2.isFulfilled()).toBe( + expect(prom2Fulfilled).toBe( false, 'second attemptDecryption resolved too soon'); return Promise.resolve({ diff --git a/spec/unit/filter.spec.js b/spec/unit/filter.spec.js index 249eaa171..ed7722cf8 100644 --- a/spec/unit/filter.spec.js +++ b/spec/unit/filter.spec.js @@ -2,9 +2,6 @@ import 'source-map-support/register'; const sdk = require("../.."); const Filter = sdk.Filter; -const utils = require("../test-utils"); - -import expect from 'expect'; describe("Filter", function() { const filterId = "f1lt3ring15g00d4ursoul"; @@ -12,7 +9,6 @@ describe("Filter", function() { let filter; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this filter = new Filter(userId); }); diff --git a/spec/unit/interactive-auth.spec.js b/spec/unit/interactive-auth.spec.js index 110cb714c..c4633f554 100644 --- a/spec/unit/interactive-auth.spec.js +++ b/spec/unit/interactive-auth.spec.js @@ -18,12 +18,10 @@ limitations under the License. import 'source-map-support/register'; import Promise from 'bluebird'; const sdk = require("../.."); -const utils = require("../test-utils"); const InteractiveAuth = sdk.InteractiveAuth; const MatrixError = sdk.MatrixError; -import expect from 'expect'; import logger from '../../src/logger'; // Trivial client object to test interactive auth @@ -35,13 +33,9 @@ class FakeClient { } describe("InteractiveAuth", function() { - beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - - it("should start an auth stage and complete it", function(done) { - const doRequest = expect.createSpy(); - const stateUpdated = expect.createSpy(); + it("should start an auth stage and complete it", function() { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); const ia = new InteractiveAuth({ matrixClient: new FakeClient(), @@ -64,7 +58,7 @@ describe("InteractiveAuth", function() { }); // first we expect a call here - stateUpdated.andCall(function(stage) { + stateUpdated.mockImplementation(function(stage) { logger.log('aaaa'); expect(stage).toEqual("logintype"); ia.submitAuthDict({ @@ -75,7 +69,7 @@ describe("InteractiveAuth", function() { // .. which should trigger a call here const requestRes = {"a": "b"}; - doRequest.andCall(function(authData) { + doRequest.mockImplementation(function(authData) { logger.log('cccc'); expect(authData).toEqual({ session: "sessionId", @@ -85,16 +79,16 @@ describe("InteractiveAuth", function() { return Promise.resolve(requestRes); }); - ia.attemptAuth().then(function(res) { + return ia.attemptAuth().then(function(res) { expect(res).toBe(requestRes); - expect(doRequest.calls.length).toEqual(1); - expect(stateUpdated.calls.length).toEqual(1); - }).nodeify(done); + expect(doRequest).toBeCalledTimes(1); + expect(stateUpdated).toBeCalledTimes(1); + }); }); - it("should make a request if no authdata is provided", function(done) { - const doRequest = expect.createSpy(); - const stateUpdated = expect.createSpy(); + it("should make a request if no authdata is provided", function() { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); const ia = new InteractiveAuth({ matrixClient: new FakeClient(), @@ -106,7 +100,7 @@ describe("InteractiveAuth", function() { expect(ia.getStageParams("logintype")).toBe(undefined); // first we expect a call to doRequest - doRequest.andCall(function(authData) { + doRequest.mockImplementation(function(authData) { logger.log("request1", authData); expect(authData).toEqual({}); const err = new MatrixError({ @@ -124,7 +118,7 @@ describe("InteractiveAuth", function() { // .. which should be followed by a call to stateUpdated const requestRes = {"a": "b"}; - stateUpdated.andCall(function(stage) { + stateUpdated.mockImplementation(function(stage) { expect(stage).toEqual("logintype"); expect(ia.getSessionId()).toEqual("sessionId"); expect(ia.getStageParams("logintype")).toEqual({ @@ -132,7 +126,7 @@ describe("InteractiveAuth", function() { }); // submitAuthDict should trigger another call to doRequest - doRequest.andCall(function(authData) { + doRequest.mockImplementation(function(authData) { logger.log("request2", authData); expect(authData).toEqual({ session: "sessionId", @@ -148,10 +142,10 @@ describe("InteractiveAuth", function() { }); }); - ia.attemptAuth().then(function(res) { + return ia.attemptAuth().then(function(res) { expect(res).toBe(requestRes); - expect(doRequest.calls.length).toEqual(2); - expect(stateUpdated.calls.length).toEqual(1); - }).nodeify(done); + expect(doRequest).toBeCalledTimes(2); + expect(stateUpdated).toBeCalledTimes(1); + }); }); }); diff --git a/spec/unit/login.spec.js b/spec/unit/login.spec.js index 97c0dade2..0a294adc3 100644 --- a/spec/unit/login.spec.js +++ b/spec/unit/login.spec.js @@ -1,4 +1,3 @@ -import expect from 'expect'; import TestClient from '../TestClient'; describe('Login request', function() { diff --git a/spec/unit/matrix-client.spec.js b/spec/unit/matrix-client.spec.js index 029ae1bb5..19f426609 100644 --- a/spec/unit/matrix-client.spec.js +++ b/spec/unit/matrix-client.spec.js @@ -3,12 +3,11 @@ import 'source-map-support/register'; import Promise from 'bluebird'; const sdk = require("../.."); const MatrixClient = sdk.MatrixClient; -const utils = require("../test-utils"); -import expect from 'expect'; -import lolex from 'lolex'; import logger from '../../src/logger'; +jest.useFakeTimers(); + describe("MatrixClient", function() { const userId = "@alice:bar"; const identityServerUrl = "https://identity.server"; @@ -16,7 +15,6 @@ describe("MatrixClient", function() { let client; let store; let scheduler; - let clock; const KEEP_ALIVE_PATH = "/_matrix/client/versions"; @@ -125,24 +123,22 @@ describe("MatrixClient", function() { } beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - clock = lolex.install(); scheduler = [ "getQueueForEvent", "queueEvent", "removeEventFromQueue", "setProcessFunction", - ].reduce((r, k) => { r[k] = expect.createSpy(); return r; }, {}); + ].reduce((r, k) => { r[k] = jest.fn(); return r; }, {}); store = [ "getRoom", "getRooms", "getUser", "getSyncToken", "scrollback", "save", "wantsSave", "setSyncToken", "storeEvents", "storeRoom", "storeUser", "getFilterIdByName", "setFilterIdByName", "getFilter", "storeFilter", "getSyncAccumulator", "startup", "deleteAllData", - ].reduce((r, k) => { r[k] = expect.createSpy(); return r; }, {}); - store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null)); - store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null)); - store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null)); - store.getClientOptions = expect.createSpy().andReturn(Promise.resolve(null)); - store.storeClientOptions = expect.createSpy().andReturn(Promise.resolve(null)); - store.isNewlyCreated = expect.createSpy().andReturn(Promise.resolve(true)); + ].reduce((r, k) => { r[k] = jest.fn(); return r; }, {}); + store.getSavedSync = jest.fn().mockReturnValue(Promise.resolve(null)); + store.getSavedSyncToken = jest.fn().mockReturnValue(Promise.resolve(null)); + store.setSyncData = jest.fn().mockReturnValue(Promise.resolve(null)); + store.getClientOptions = jest.fn().mockReturnValue(Promise.resolve(null)); + store.storeClientOptions = jest.fn().mockReturnValue(Promise.resolve(null)); + store.isNewlyCreated = jest.fn().mockReturnValue(Promise.resolve(true)); client = new MatrixClient({ baseUrl: "https://my.home.server", idBaseUrl: identityServerUrl, @@ -155,9 +151,9 @@ describe("MatrixClient", function() { // FIXME: We shouldn't be yanking _http like this. client._http = [ "authedRequest", "getContentUri", "request", "uploadContent", - ].reduce((r, k) => { r[k] = expect.createSpy(); return r; }, {}); - client._http.authedRequest.andCall(httpReq); - client._http.request.andCall(httpReq); + ].reduce((r, k) => { r[k] = jest.fn(); return r; }, {}); + client._http.authedRequest.mockImplementation(httpReq); + client._http.request.mockImplementation(httpReq); // set reasonable working defaults acceptKeepalives = true; @@ -169,13 +165,12 @@ describe("MatrixClient", function() { }); afterEach(function() { - clock.uninstall(); // need to re-stub the requests with NOPs because there are no guarantees // clients from previous tests will be GC'd before the next test. This // means they may call /events and then fail an expect() which will fail // a DIFFERENT test (pollution between tests!) - we return unresolved // promises to stop the client from continuing to run. - client._http.authedRequest.andCall(function() { + client._http.authedRequest.mockImplementation(function() { return Promise.defer().promise; }); }); @@ -185,10 +180,10 @@ describe("MatrixClient", function() { httpLookups.push(PUSH_RULES_RESPONSE); httpLookups.push(SYNC_RESPONSE); const filterId = "ehfewf"; - store.getFilterIdByName.andReturn(filterId); + store.getFilterIdByName.mockReturnValue(filterId); const filter = new sdk.Filter(0, filterId); filter.setDefinition({"room": {"timeline": {"limit": 8}}}); - store.getFilter.andReturn(filter); + store.getFilter.mockReturnValue(filter); const syncPromise = new Promise((resolve, reject) => { client.on("sync", function syncListener(state) { if (state === "SYNCING") { @@ -249,7 +244,7 @@ describe("MatrixClient", function() { }, }); httpLookups.push(FILTER_RESPONSE); - store.getFilterIdByName.andReturn(invalidFilterId); + store.getFilterIdByName.mockReturnValue(invalidFilterId); const filterName = getFilterName(client.credentials.userId); client.store.setFilterIdByName(filterName, invalidFilterId); @@ -281,7 +276,7 @@ describe("MatrixClient", function() { if (state === "ERROR" && httpLookups.length > 0) { expect(httpLookups.length).toEqual(2); expect(client.retryImmediately()).toBe(true); - clock.tick(1); + jest.advanceTimersByTime(1); } else if (state === "PREPARED" && httpLookups.length === 0) { client.removeListener("sync", syncListener); done(); @@ -307,9 +302,9 @@ describe("MatrixClient", function() { expect(client.retryImmediately()).toBe( true, "retryImmediately returned false", ); - clock.tick(1); + jest.advanceTimersByTime(1); } else if (state === "RECONNECTING" && httpLookups.length > 0) { - clock.tick(10000); + jest.advanceTimersByTime(10000); } else if (state === "SYNCING" && httpLookups.length === 0) { client.removeListener("sync", syncListener); done(); @@ -331,7 +326,7 @@ describe("MatrixClient", function() { if (state === "ERROR" && httpLookups.length > 0) { expect(httpLookups.length).toEqual(3); expect(client.retryImmediately()).toBe(true); - clock.tick(1); + jest.advanceTimersByTime(1); } else if (state === "PREPARED" && httpLookups.length === 0) { client.removeListener("sync", syncListener); done(); @@ -362,7 +357,7 @@ describe("MatrixClient", function() { done(); } // standard retry time is 5 to 10 seconds - clock.tick(10000); + jest.advanceTimersByTime(10000); }; } diff --git a/spec/unit/pushprocessor.spec.js b/spec/unit/pushprocessor.spec.js index 78d28473e..32580dec2 100644 --- a/spec/unit/pushprocessor.spec.js +++ b/spec/unit/pushprocessor.spec.js @@ -3,8 +3,6 @@ import 'source-map-support/register'; const PushProcessor = require("../../lib/pushprocessor"); const utils = require("../test-utils"); -import expect from 'expect'; - describe('NotificationService', function() { const testUserId = "@ali:matrix.org"; const testDisplayName = "Alice M"; diff --git a/spec/unit/realtime-callbacks.spec.js b/spec/unit/realtime-callbacks.spec.js index c78cf82db..70510b26a 100644 --- a/spec/unit/realtime-callbacks.spec.js +++ b/spec/unit/realtime-callbacks.spec.js @@ -1,53 +1,46 @@ "use strict"; import 'source-map-support/register'; -const callbacks = require("../../lib/realtime-callbacks"); -const testUtils = require("../test-utils.js"); +const callbacks = require("../../src/realtime-callbacks"); -import expect from 'expect'; -import lolex from 'lolex'; +let wallTime = 1234567890; +jest.useFakeTimers(); describe("realtime-callbacks", function() { - let clock; - function tick(millis) { - clock.tick(millis); + wallTime += millis; + jest.advanceTimersByTime(millis); } beforeEach(function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - clock = lolex.install(); - const fakeDate = clock.Date; - callbacks.setNow(fakeDate.now.bind(fakeDate)); + callbacks.setNow(() => wallTime); }); afterEach(function() { callbacks.setNow(); - clock.uninstall(); }); describe("setTimeout", function() { it("should call the callback after the timeout", function() { - const callback = expect.createSpy(); + const callback = jest.fn(); callbacks.setTimeout(callback, 100); - expect(callback).toNotHaveBeenCalled(); + expect(callback).not.toHaveBeenCalled(); tick(100); expect(callback).toHaveBeenCalled(); }); - it("should default to a zero timeout", function() { - const callback = expect.createSpy(); + const callback = jest.fn(); callbacks.setTimeout(callback); - expect(callback).toNotHaveBeenCalled(); + expect(callback).not.toHaveBeenCalled(); tick(0); expect(callback).toHaveBeenCalled(); }); it("should pass any parameters to the callback", function() { - const callback = expect.createSpy(); + const callback = jest.fn(); callbacks.setTimeout(callback, 0, "a", "b", "c"); tick(0); expect(callback).toHaveBeenCalledWith("a", "b", "c"); @@ -66,10 +59,10 @@ describe("realtime-callbacks", function() { }); it("should handle timeouts of several seconds", function() { - const callback = expect.createSpy(); + const callback = jest.fn(); callbacks.setTimeout(callback, 2000); - expect(callback).toNotHaveBeenCalled(); + expect(callback).not.toHaveBeenCalled(); for (let i = 0; i < 4; i++) { tick(500); } @@ -77,24 +70,24 @@ describe("realtime-callbacks", function() { }); it("should call multiple callbacks in the right order", function() { - const callback1 = expect.createSpy(); - const callback2 = expect.createSpy(); - const callback3 = expect.createSpy(); + const callback1 = jest.fn(); + const callback2 = jest.fn(); + const callback3 = jest.fn(); callbacks.setTimeout(callback2, 200); callbacks.setTimeout(callback1, 100); callbacks.setTimeout(callback3, 300); - expect(callback1).toNotHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); - expect(callback3).toNotHaveBeenCalled(); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + expect(callback3).not.toHaveBeenCalled(); tick(100); expect(callback1).toHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); - expect(callback3).toNotHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + expect(callback3).not.toHaveBeenCalled(); tick(100); expect(callback1).toHaveBeenCalled(); expect(callback2).toHaveBeenCalled(); - expect(callback3).toNotHaveBeenCalled(); + expect(callback3).not.toHaveBeenCalled(); tick(100); expect(callback1).toHaveBeenCalled(); expect(callback2).toHaveBeenCalled(); @@ -102,35 +95,34 @@ describe("realtime-callbacks", function() { }); it("should treat -ve timeouts the same as a zero timeout", function() { - const callback1 = expect.createSpy(); - const callback2 = expect.createSpy(); + const callback1 = jest.fn(); + const callback2 = jest.fn(); // check that cb1 is called before cb2 - callback1.andCall(function() { - expect(callback2).toNotHaveBeenCalled(); + callback1.mockImplementation(function() { + expect(callback2).not.toHaveBeenCalled(); }); callbacks.setTimeout(callback1); callbacks.setTimeout(callback2, -100); - expect(callback1).toNotHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); tick(0); expect(callback1).toHaveBeenCalled(); expect(callback2).toHaveBeenCalled(); }); it("should not get confused by chained calls", function() { - const callback2 = expect.createSpy(); - const callback1 = expect.createSpy(); - callback1.andCall(function() { + const callback2 = jest.fn(); + const callback1 = jest.fn(function() { callbacks.setTimeout(callback2, 0); - expect(callback2).toNotHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); }); callbacks.setTimeout(callback1); - expect(callback1).toNotHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); tick(0); expect(callback1).toHaveBeenCalled(); // the fake timer won't actually run callbacks registered during @@ -140,16 +132,15 @@ describe("realtime-callbacks", function() { }); it("should be immune to exceptions", function() { - const callback1 = expect.createSpy(); - callback1.andCall(function() { + const callback1 = jest.fn(function() { throw new Error("prepare to die"); }); - const callback2 = expect.createSpy(); + const callback2 = jest.fn(); callbacks.setTimeout(callback1, 0); callbacks.setTimeout(callback2, 0); - expect(callback1).toNotHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); tick(0); expect(callback1).toHaveBeenCalled(); expect(callback2).toHaveBeenCalled(); @@ -158,16 +149,16 @@ describe("realtime-callbacks", function() { describe("cancelTimeout", function() { it("should cancel a pending timeout", function() { - const callback = expect.createSpy(); + const callback = jest.fn(); const k = callbacks.setTimeout(callback); callbacks.clearTimeout(k); tick(0); - expect(callback).toNotHaveBeenCalled(); + expect(callback).not.toHaveBeenCalled(); }); it("should not affect sooner timeouts", function() { - const callback1 = expect.createSpy(); - const callback2 = expect.createSpy(); + const callback1 = jest.fn(); + const callback2 = jest.fn(); callbacks.setTimeout(callback1, 100); const k = callbacks.setTimeout(callback2, 200); @@ -175,10 +166,10 @@ describe("realtime-callbacks", function() { tick(100); expect(callback1).toHaveBeenCalled(); - expect(callback2).toNotHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); tick(150); - expect(callback2).toNotHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); }); }); }); diff --git a/spec/unit/room-member.spec.js b/spec/unit/room-member.spec.js index e07de6c96..9c7a8ee32 100644 --- a/spec/unit/room-member.spec.js +++ b/spec/unit/room-member.spec.js @@ -4,8 +4,6 @@ const sdk = require("../.."); const RoomMember = sdk.RoomMember; const utils = require("../test-utils"); -import expect from 'expect'; - describe("RoomMember", function() { const roomId = "!foo:bar"; const userA = "@alice:bar"; @@ -14,7 +12,6 @@ describe("RoomMember", function() { let member; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this member = new RoomMember(roomId, userA); }); @@ -36,7 +33,7 @@ describe("RoomMember", function() { const url = member.getAvatarUrl(hsUrl); // we don't care about how the mxc->http conversion is done, other // than it contains the mxc body. - expect(url.indexOf("flibble/wibble")).toNotEqual(-1); + expect(url.indexOf("flibble/wibble")).not.toEqual(-1); }); it("should return an identicon HTTP URL if allowDefault was set and there " + @@ -255,9 +252,9 @@ describe("RoomMember", function() { member.setMembershipEvent(joinEvent); expect(member.name).toEqual("Alice"); // prefer displayname member.setMembershipEvent(joinEvent, roomState); - expect(member.name).toNotEqual("Alice"); // it should disambig. + expect(member.name).not.toEqual("Alice"); // it should disambig. // user_id should be there somewhere - expect(member.name.indexOf(userA)).toNotEqual(-1); + expect(member.name.indexOf(userA)).not.toEqual(-1); }); it("should emit 'RoomMember.membership' if the membership changes", function() { @@ -328,9 +325,9 @@ describe("RoomMember", function() { }; expect(member.name).toEqual(userA); // default = user_id member.setMembershipEvent(joinEvent, roomState); - expect(member.name).toNotEqual("Alíce"); // it should disambig. + expect(member.name).not.toEqual("Alíce"); // it should disambig. // user_id should be there somewhere - expect(member.name.indexOf(userA)).toNotEqual(-1); + expect(member.name.indexOf(userA)).not.toEqual(-1); }); }); }); diff --git a/spec/unit/room-state.spec.js b/spec/unit/room-state.spec.js index 553f424ff..87aedaf79 100644 --- a/spec/unit/room-state.spec.js +++ b/spec/unit/room-state.spec.js @@ -5,8 +5,6 @@ const RoomState = sdk.RoomState; const RoomMember = sdk.RoomMember; const utils = require("../test-utils"); -import expect from 'expect'; - describe("RoomState", function() { const roomId = "!foo:bar"; const userA = "@alice:bar"; @@ -17,7 +15,6 @@ describe("RoomState", function() { let state; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this state = new RoomState(roomId); state.setStateEvents([ utils.mkMembership({ // userA joined @@ -49,8 +46,8 @@ describe("RoomState", function() { const members = state.getMembers(); expect(members.length).toEqual(2); // ordering unimportant - expect([userA, userB].indexOf(members[0].userId)).toNotEqual(-1); - expect([userA, userB].indexOf(members[1].userId)).toNotEqual(-1); + expect([userA, userB].indexOf(members[0].userId)).not.toEqual(-1); + expect([userA, userB].indexOf(members[1].userId)).not.toEqual(-1); }); }); @@ -120,8 +117,8 @@ describe("RoomState", function() { const events = state.getStateEvents("m.room.member"); expect(events.length).toEqual(2); // ordering unimportant - expect([userA, userB].indexOf(events[0].getStateKey())).toNotEqual(-1); - expect([userA, userB].indexOf(events[1].getStateKey())).toNotEqual(-1); + expect([userA, userB].indexOf(events[0].getStateKey())).not.toEqual(-1); + expect([userA, userB].indexOf(events[1].getStateKey())).not.toEqual(-1); }); it("should return a single MatrixEvent if a state_key was specified", @@ -258,7 +255,7 @@ describe("RoomState", function() { }); state.setStateEvents([memberEvent]); - expect(state.members[userA].setMembershipEvent).toNotHaveBeenCalled(); + expect(state.members[userA].setMembershipEvent).not.toHaveBeenCalled(); expect(state.members[userB].setMembershipEvent).toHaveBeenCalledWith( memberEvent, state, ); @@ -306,7 +303,7 @@ describe("RoomState", function() { state.markOutOfBandMembersStarted(); state.setOutOfBandMembers([oobMemberEvent]); const memberA = state.getMember(userA); - expect(memberA.events.member.getId()).toNotEqual(oobMemberEvent.getId()); + expect(memberA.events.member.getId()).not.toEqual(oobMemberEvent.getId()); expect(memberA.isOutOfBand()).toEqual(false); }); diff --git a/spec/unit/room.spec.js b/spec/unit/room.spec.js index b257ec637..c2f7a5521 100644 --- a/spec/unit/room.spec.js +++ b/spec/unit/room.spec.js @@ -8,8 +8,6 @@ const EventStatus = sdk.EventStatus; const EventTimeline = sdk.EventTimeline; const utils = require("../test-utils"); -import expect from 'expect'; - describe("Room", function() { const roomId = "!foo:bar"; const userA = "@alice:bar"; @@ -19,7 +17,6 @@ describe("Room", function() { let room; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this room = new Room(roomId); // mock RoomStates room.oldState = room.getLiveTimeline()._startState = @@ -32,7 +29,7 @@ describe("Room", function() { const hsUrl = "https://my.home.server"; it("should return the URL from m.room.avatar preferentially", function() { - room.currentState.getStateEvents.andCall(function(type, key) { + room.currentState.getStateEvents.mockImplementation(function(type, key) { if (type === "m.room.avatar" && key === "") { return utils.mkEvent({ event: true, @@ -49,7 +46,7 @@ describe("Room", function() { const url = room.getAvatarUrl(hsUrl); // we don't care about how the mxc->http conversion is done, other // than it contains the mxc body. - expect(url.indexOf("flibble/wibble")).toNotEqual(-1); + expect(url.indexOf("flibble/wibble")).not.toEqual(-1); }); it("should return an identicon HTTP URL if allowDefault was set and there " + @@ -67,13 +64,13 @@ describe("Room", function() { describe("getMember", function() { beforeEach(function() { - room.currentState.getMember.andCall(function(userId) { + room.currentState.getMember.mockImplementation(function(userId) { return { "@alice:bar": { userId: userA, roomId: roomId, }, - }[userId]; + }[userId] || null; }); }); @@ -82,7 +79,7 @@ describe("Room", function() { }); it("should return the member from current state", function() { - expect(room.getMember(userA)).toNotEqual(null); + expect(room.getMember(userA)).not.toEqual(null); }); }); @@ -174,7 +171,7 @@ describe("Room", function() { ); expect(events[0].forwardLooking).toBe(true); expect(events[1].forwardLooking).toBe(true); - expect(room.oldState.setStateEvents).toNotHaveBeenCalled(); + expect(room.oldState.setStateEvents).not.toHaveBeenCalled(); }); it("should synthesize read receipts for the senders of events", function() { @@ -183,7 +180,7 @@ describe("Room", function() { membership: "join", name: "Alice", }; - room.currentState.getSentinelMember.andCall(function(uid) { + room.currentState.getSentinelMember.mockImplementation(function(uid) { if (uid === userA) { return sentinel; } @@ -292,13 +289,13 @@ describe("Room", function() { membership: "join", name: "Old Alice", }; - room.currentState.getSentinelMember.andCall(function(uid) { + room.currentState.getSentinelMember.mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); - room.oldState.getSentinelMember.andCall(function(uid) { + room.oldState.getSentinelMember.mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; } @@ -331,13 +328,13 @@ describe("Room", function() { membership: "join", name: "Old Alice", }; - room.currentState.getSentinelMember.andCall(function(uid) { + room.currentState.getSentinelMember.mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); - room.oldState.getSentinelMember.andCall(function(uid) { + room.oldState.getSentinelMember.mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; } @@ -379,7 +376,7 @@ describe("Room", function() { ); expect(events[0].forwardLooking).toBe(false); expect(events[1].forwardLooking).toBe(false); - expect(room.currentState.setStateEvents).toNotHaveBeenCalled(); + expect(room.currentState.setStateEvents).not.toHaveBeenCalled(); }); }); @@ -545,7 +542,7 @@ describe("Room", function() { describe("getJoinedMembers", function() { it("should return members whose membership is 'join'", function() { - room.currentState.getMembers.andCall(function() { + room.currentState.getMembers.mockImplementation(function() { return [ { userId: "@alice:bar", membership: "join" }, { userId: "@bob:bar", membership: "invite" }, @@ -558,7 +555,7 @@ describe("Room", function() { }); it("should return an empty list if no membership is 'join'", function() { - room.currentState.getMembers.andCall(function() { + room.currentState.getMembers.mockImplementation(function() { return [ { userId: "@bob:bar", membership: "invite" }, ]; @@ -571,7 +568,7 @@ describe("Room", function() { describe("hasMembershipState", function() { it("should return true for a matching userId and membership", function() { - room.currentState.getMember.andCall(function(userId) { + room.currentState.getMember.mockImplementation(function(userId) { return { "@alice:bar": { userId: "@alice:bar", membership: "join" }, "@bob:bar": { userId: "@bob:bar", membership: "invite" }, @@ -582,7 +579,7 @@ describe("Room", function() { it("should return false if match membership but no match userId", function() { - room.currentState.getMember.andCall(function(userId) { + room.currentState.getMember.mockImplementation(function(userId) { return { "@alice:bar": { userId: "@alice:bar", membership: "join" }, }[userId]; @@ -592,7 +589,7 @@ describe("Room", function() { it("should return false if match userId but no match membership", function() { - room.currentState.getMember.andCall(function(userId) { + room.currentState.getMember.mockImplementation(function(userId) { return { "@alice:bar": { userId: "@alice:bar", membership: "join" }, }[userId]; @@ -602,7 +599,7 @@ describe("Room", function() { it("should return false if no match membership or userId", function() { - room.currentState.getMember.andCall(function(userId) { + room.currentState.getMember.mockImplementation(function(userId) { return { "@alice:bar": { userId: "@alice:bar", membership: "join" }, }[userId]; @@ -814,8 +811,8 @@ describe("Room", function() { addMember(userC); room.recalculate(); const name = room.name; - expect(name.indexOf(userB)).toNotEqual(-1, name); - expect(name.indexOf(userC)).toNotEqual(-1, name); + expect(name.indexOf(userB)).not.toEqual(-1, name); + expect(name.indexOf(userC)).not.toEqual(-1, name); }); it("should return the names of members in a public (public join_rules)" + @@ -827,8 +824,8 @@ describe("Room", function() { addMember(userC); room.recalculate(); const name = room.name; - expect(name.indexOf(userB)).toNotEqual(-1, name); - expect(name.indexOf(userC)).toNotEqual(-1, name); + expect(name.indexOf(userB)).not.toEqual(-1, name); + expect(name.indexOf(userC)).not.toEqual(-1, name); }); it("should show the other user's name for public (public join_rules)" + @@ -839,7 +836,7 @@ describe("Room", function() { addMember(userB); room.recalculate(); const name = room.name; - expect(name.indexOf(userB)).toNotEqual(-1, name); + expect(name.indexOf(userB)).not.toEqual(-1, name); }); it("should show the other user's name for private " + @@ -850,7 +847,7 @@ describe("Room", function() { addMember(userB); room.recalculate(); const name = room.name; - expect(name.indexOf(userB)).toNotEqual(-1, name); + expect(name.indexOf(userB)).not.toEqual(-1, name); }); it("should show the other user's name for private" + @@ -860,7 +857,7 @@ describe("Room", function() { addMember(userB); room.recalculate(); const name = room.name; - expect(name.indexOf(userB)).toNotEqual(-1, name); + expect(name.indexOf(userB)).not.toEqual(-1, name); }); it("should show the room alias if one exists for private " + @@ -1004,7 +1001,7 @@ describe("Room", function() { it("should emit an event when a receipt is added", function() { - const listener = expect.createSpy(); + const listener = jest.fn(); room.on("Room.receipt", listener); const ts = 13787898424; @@ -1175,7 +1172,7 @@ describe("Room", function() { it("should emit Room.tags event when new tags are " + "received on the event stream", function() { - const listener = expect.createSpy(); + const listener = jest.fn(); room.on("Room.tags", listener); const tags = { "m.foo": { "order": 0.5 } }; diff --git a/spec/unit/scheduler.spec.js b/spec/unit/scheduler.spec.js index b518e8323..941223a5a 100644 --- a/spec/unit/scheduler.spec.js +++ b/spec/unit/scheduler.spec.js @@ -8,11 +8,9 @@ const MatrixScheduler = sdk.MatrixScheduler; const MatrixError = sdk.MatrixError; const utils = require("../test-utils"); -import expect from 'expect'; -import lolex from 'lolex'; +jest.useFakeTimers(); describe("MatrixScheduler", function() { - let clock; let scheduler; let retryFn; let queueFn; @@ -26,8 +24,6 @@ describe("MatrixScheduler", function() { }); beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - clock = lolex.install(); scheduler = new MatrixScheduler(function(ev, attempts, err) { if (retryFn) { return retryFn(ev, attempts, err); @@ -44,10 +40,6 @@ describe("MatrixScheduler", function() { defer = Promise.defer(); }); - afterEach(function() { - clock.uninstall(); - }); - it("should process events in a queue in a FIFO manner", async function() { retryFn = function() { return 0; @@ -112,7 +104,7 @@ describe("MatrixScheduler", function() { defer.reject({}); await retryDefer.promise; expect(procCount).toEqual(1); - clock.tick(waitTimeMs); + jest.advanceTimersByTime(waitTimeMs); await Promise.resolve(); expect(procCount).toEqual(2); }); @@ -203,7 +195,7 @@ describe("MatrixScheduler", function() { setTimeout(function() { deferA.resolve({}); }, 1000); - clock.tick(1000); + jest.advanceTimersByTime(1000); }); describe("queueEvent", function() { diff --git a/spec/unit/sync-accumulator.spec.js b/spec/unit/sync-accumulator.spec.js index 5f067b0b7..6dfd76043 100644 --- a/spec/unit/sync-accumulator.spec.js +++ b/spec/unit/sync-accumulator.spec.js @@ -16,9 +16,7 @@ limitations under the License. "use strict"; import 'source-map-support/register'; -import utils from "../test-utils"; import sdk from "../.."; -import expect from 'expect'; const SyncAccumulator = sdk.SyncAccumulator; @@ -26,7 +24,6 @@ describe("SyncAccumulator", function() { let sa; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this sa = new SyncAccumulator({ maxTimelineEntries: 10, }); diff --git a/spec/unit/timeline-window.spec.js b/spec/unit/timeline-window.spec.js index 3637fe968..35520b1d7 100644 --- a/spec/unit/timeline-window.spec.js +++ b/spec/unit/timeline-window.spec.js @@ -7,7 +7,6 @@ const TimelineWindow = sdk.TimelineWindow; const TimelineIndex = require("../../lib/timeline-window").TimelineIndex; const utils = require("../test-utils"); -import expect from 'expect'; const ROOM_ID = "roomId"; const USER_ID = "userId"; @@ -67,10 +66,6 @@ function createLinkedTimelines() { describe("TimelineIndex", function() { - beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - describe("minIndex", function() { it("should return the min index relative to BaseIndex", function() { const timelineIndex = new TimelineIndex(createTimeline(), 0); @@ -153,7 +148,7 @@ describe("TimelineWindow", function() { let timelineSet; let client; function createWindow(timeline, opts) { - timelineSet = {}; + timelineSet = {getTimelineForEvent: () => null}; client = {}; client.getEventTimeline = function(timelineSet0, eventId0) { expect(timelineSet0).toBe(timelineSet); @@ -163,12 +158,8 @@ describe("TimelineWindow", function() { return new TimelineWindow(client, timelineSet, opts); } - beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - describe("load", function() { - it("should initialise from the live timeline", function(done) { + it("should initialise from the live timeline", function() { const liveTimeline = createTimeline(); const room = {}; room.getLiveTimeline = function() { @@ -176,17 +167,17 @@ describe("TimelineWindow", function() { }; const timelineWindow = new TimelineWindow(undefined, room); - timelineWindow.load(undefined, 2).then(function() { + return timelineWindow.load(undefined, 2).then(function() { const expectedEvents = liveTimeline.getEvents().slice(1); expect(timelineWindow.getEvents()).toEqual(expectedEvents); - }).nodeify(done); + }); }); - it("should initialise from a specific event", function(done) { + it("should initialise from a specific event", function() { const timeline = createTimeline(); const eventId = timeline.getEvents()[1].getId(); - const timelineSet = {}; + const timelineSet = {getTimelineForEvent: () => null}; const client = {}; client.getEventTimeline = function(timelineSet0, eventId0) { expect(timelineSet0).toBe(timelineSet); @@ -195,21 +186,20 @@ describe("TimelineWindow", function() { }; const timelineWindow = new TimelineWindow(client, timelineSet); - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = timeline.getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); - }).nodeify(done); + }); }); - it("canPaginate should return false until load has returned", - function(done) { + it("canPaginate should return false until load has returned", function() { const timeline = createTimeline(); timeline.setPaginationToken("toktok1", EventTimeline.BACKWARDS); timeline.setPaginationToken("toktok2", EventTimeline.FORWARDS); const eventId = timeline.getEvents()[1].getId(); - const timelineSet = {}; + const timelineSet = {getTimelineForEvent: () => null}; const client = {}; const timelineWindow = new TimelineWindow(client, timelineSet); @@ -222,25 +212,24 @@ describe("TimelineWindow", function() { return Promise.resolve(timeline); }; - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = timeline.getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS)) .toBe(true); expect(timelineWindow.canPaginate(EventTimeline.FORWARDS)) .toBe(true); - }).nodeify(done); + }); }); }); describe("pagination", function() { - it("should be able to advance across the initial timeline", - function(done) { + it("should be able to advance across the initial timeline", function() { const timeline = createTimeline(); const eventId = timeline.getEvents()[1].getId(); const timelineWindow = createWindow(timeline); - timelineWindow.load(eventId, 1).then(function() { + return timelineWindow.load(eventId, 1).then(function() { const expectedEvents = [timeline.getEvents()[1]]; expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -277,15 +266,15 @@ describe("TimelineWindow", function() { return timelineWindow.paginate(EventTimeline.BACKWARDS, 2); }).then(function(success) { expect(success).toBe(false); - }).nodeify(done); + }); }); - it("should advance into next timeline", function(done) { + it("should advance into next timeline", function() { const tls = createLinkedTimelines(); const eventId = tls[0].getEvents()[1].getId(); const timelineWindow = createWindow(tls[0], {windowLimit: 5}); - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = tls[0].getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -322,15 +311,15 @@ describe("TimelineWindow", function() { return timelineWindow.paginate(EventTimeline.FORWARDS, 2); }).then(function(success) { expect(success).toBe(false); - }).nodeify(done); + }); }); - it("should retreat into previous timeline", function(done) { + it("should retreat into previous timeline", function() { const tls = createLinkedTimelines(); const eventId = tls[1].getEvents()[1].getId(); const timelineWindow = createWindow(tls[1], {windowLimit: 5}); - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = tls[1].getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -367,10 +356,10 @@ describe("TimelineWindow", function() { return timelineWindow.paginate(EventTimeline.BACKWARDS, 2); }).then(function(success) { expect(success).toBe(false); - }).nodeify(done); + }); }); - it("should make forward pagination requests", function(done) { + it("should make forward pagination requests", function() { const timeline = createTimeline(); timeline.setPaginationToken("toktok", EventTimeline.FORWARDS); @@ -386,7 +375,7 @@ describe("TimelineWindow", function() { return Promise.resolve(true); }; - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = timeline.getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -399,11 +388,11 @@ describe("TimelineWindow", function() { expect(success).toBe(true); const expectedEvents = timeline.getEvents().slice(0, 5); expect(timelineWindow.getEvents()).toEqual(expectedEvents); - }).nodeify(done); + }); }); - it("should make backward pagination requests", function(done) { + it("should make backward pagination requests", function() { const timeline = createTimeline(); timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS); @@ -419,7 +408,7 @@ describe("TimelineWindow", function() { return Promise.resolve(true); }; - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = timeline.getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -432,11 +421,10 @@ describe("TimelineWindow", function() { expect(success).toBe(true); const expectedEvents = timeline.getEvents().slice(1, 6); expect(timelineWindow.getEvents()).toEqual(expectedEvents); - }).nodeify(done); + }); }); - it("should limit the number of unsuccessful pagination requests", - function(done) { + it("should limit the number of unsuccessful pagination requests", function() { const timeline = createTimeline(); timeline.setPaginationToken("toktok", EventTimeline.FORWARDS); @@ -452,7 +440,7 @@ describe("TimelineWindow", function() { return Promise.resolve(true); }; - timelineWindow.load(eventId, 3).then(function() { + return timelineWindow.load(eventId, 3).then(function() { const expectedEvents = timeline.getEvents(); expect(timelineWindow.getEvents()).toEqual(expectedEvents); @@ -471,7 +459,7 @@ describe("TimelineWindow", function() { .toBe(false); expect(timelineWindow.canPaginate(EventTimeline.FORWARDS)) .toBe(true); - }).nodeify(done); + }); }); }); }); diff --git a/spec/unit/user.spec.js b/spec/unit/user.spec.js index 7448cdec9..8cea9c03d 100644 --- a/spec/unit/user.spec.js +++ b/spec/unit/user.spec.js @@ -4,14 +4,11 @@ const sdk = require("../.."); const User = sdk.User; const utils = require("../test-utils"); -import expect from 'expect'; - describe("User", function() { const userId = "@alice:bar"; let user; beforeEach(function() { - utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this user = new User(userId); }); diff --git a/spec/unit/utils.spec.js b/spec/unit/utils.spec.js index ca451eb81..c52d00d2b 100644 --- a/spec/unit/utils.spec.js +++ b/spec/unit/utils.spec.js @@ -1,15 +1,8 @@ "use strict"; import 'source-map-support/register'; const utils = require("../../lib/utils"); -const testUtils = require("../test-utils"); - -import expect from 'expect'; describe("utils", function() { - beforeEach(function() { - testUtils.beforeEach(this); // eslint-disable-line babel/no-invalid-this - }); - describe("encodeParams", function() { it("should url encode and concat with &s", function() { const params = { @@ -135,7 +128,7 @@ describe("utils", function() { utils.checkObjectHasKeys({ foo: "bar", }, ["foo"]); - }).toNotThrow(); + }).not.toThrow(); }); }); @@ -152,7 +145,7 @@ describe("utils", function() { utils.checkObjectHasNoAdditionalKeys({ foo: "bar", }, ["foo"]); - }).toNotThrow(); + }).not.toThrow(); }); }); diff --git a/src/autodiscovery.js b/src/autodiscovery.js index df52b368b..c8dc8c661 100644 --- a/src/autodiscovery.js +++ b/src/autodiscovery.js @@ -1,5 +1,6 @@ /* Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -275,21 +276,11 @@ export class AutoDiscovery { let isUrl = ""; if (wellknown["m.identity_server"]) { // We prepare a failing identity server response to save lines later - // in this branch. Note that we also fail the homeserver check in the - // object because according to the spec we're supposed to FAIL_ERROR - // if *anything* goes wrong with the IS validation, including invalid - // format. This means we're supposed to stop discovery completely. + // in this branch. const failingClientConfig = { - "m.homeserver": { - state: AutoDiscovery.FAIL_ERROR, - error: AutoDiscovery.ERROR_INVALID_IS, - - // We'll provide the base_url that was previously valid for - // debugging purposes. - base_url: clientConfig["m.homeserver"].base_url, - }, + "m.homeserver": clientConfig["m.homeserver"], "m.identity_server": { - state: AutoDiscovery.FAIL_ERROR, + state: AutoDiscovery.FAIL_PROMPT, error: AutoDiscovery.ERROR_INVALID_IS, base_url: null, }, diff --git a/src/base-apis.js b/src/base-apis.js index ccb16420e..645ba6e23 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -2333,6 +2333,12 @@ MatrixBaseApis.prototype.sendToDevice = function( messages: contentMap, }; + const targets = Object.keys(contentMap).reduce((obj, key) => { + obj[key] = Object.keys(contentMap[key]); + return obj; + }, {}); + logger.log(`PUT ${path}`, targets); + return this._http.authedRequest(undefined, "PUT", path, undefined, body); }; diff --git a/src/client.js b/src/client.js index 5b8738eab..eeb1ba246 100644 --- a/src/client.js +++ b/src/client.js @@ -19,6 +19,7 @@ limitations under the License. "use strict"; const PushProcessor = require('./pushprocessor'); +import {sleep} from './utils'; /** * This is an internal module. See {@link MatrixClient} for the public class. @@ -72,7 +73,7 @@ function keysFromRecoverySession(sessions, decryptionKey, roomId) { decrypted.room_id = roomId; keys.push(decrypted); } catch (e) { - logger.log("Failed to decrypt session from backup"); + logger.log("Failed to decrypt megolm session from backup", e); } } return keys; @@ -1646,7 +1647,7 @@ MatrixClient.prototype._restoreKeyBackup = function( key.session_id = targetSessionId; keys.push(key); } catch (e) { - logger.log("Failed to decrypt session from backup"); + logger.log("Failed to decrypt megolm session from backup", e); } } @@ -1892,33 +1893,33 @@ MatrixClient.prototype.joinRoom = function(roomIdOrAlias, opts, callback) { const reqOpts = {qsStringifyOptions: {arrayFormat: 'repeat'}}; - const defer = Promise.defer(); - const self = this; - sign_promise.then(function(signed_invite_object) { - const data = {}; - if (signed_invite_object) { - data.third_party_signed = signed_invite_object; - } + const prom = new Promise((resolve, reject) => { + sign_promise.then(function(signed_invite_object) { + const data = {}; + if (signed_invite_object) { + data.third_party_signed = signed_invite_object; + } - const path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias}); - return self._http.authedRequest( - undefined, "POST", path, queryString, data, reqOpts); - }).then(function(res) { - const roomId = res.room_id; - const syncApi = new SyncApi(self, self._clientOpts); - const room = syncApi.createRoom(roomId); - if (opts.syncRoom) { - // v2 will do this for us - // return syncApi.syncRoom(room); - } - return Promise.resolve(room); - }).done(function(room) { - _resolve(callback, defer, room); - }, function(err) { - _reject(callback, defer, err); + const path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias}); + return self._http.authedRequest( + undefined, "POST", path, queryString, data, reqOpts); + }).then(function(res) { + const roomId = res.room_id; + const syncApi = new SyncApi(self, self._clientOpts); + const room = syncApi.createRoom(roomId); + if (opts.syncRoom) { + // v2 will do this for us + // return syncApi.syncRoom(room); + } + return Promise.resolve(room); + }).done(function(room) { + _resolve(callback, resolve, room); + }, function(err) { + _reject(callback, reject, err); + }); }); - return defer.promise; + return prom; }; /** @@ -3234,42 +3235,45 @@ MatrixClient.prototype.scrollback = function(room, limit, callback) { // reduce the required number of events appropriately limit = limit - numAdded; - const defer = Promise.defer(); + const self = this; + const prom = new Promise((resolve, reject) => { + // wait for a time before doing this request + // (which may be 0 in order not to special case the code paths) + sleep(timeToWaitMs).then(function() { + return self._createMessagesRequest( + room.roomId, + room.oldState.paginationToken, + limit, + 'b'); + }).done(function(res) { + const matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper(self)); + if (res.state) { + const stateEvents = utils.map(res.state, _PojoToMatrixEventMapper(self)); + room.currentState.setUnknownStateEvents(stateEvents); + } + room.addEventsToTimeline(matrixEvents, true, room.getLiveTimeline()); + room.oldState.paginationToken = res.end; + if (res.chunk.length === 0) { + room.oldState.paginationToken = null; + } + self.store.storeEvents(room, matrixEvents, res.end, true); + self._ongoingScrollbacks[room.roomId] = null; + _resolve(callback, resolve, room); + }, function(err) { + self._ongoingScrollbacks[room.roomId] = { + errorTs: Date.now(), + }; + _reject(callback, reject, err); + }); + }); + info = { - promise: defer.promise, + promise: prom, errorTs: null, }; - const self = this; - // wait for a time before doing this request - // (which may be 0 in order not to special case the code paths) - Promise.delay(timeToWaitMs).then(function() { - return self._createMessagesRequest( - room.roomId, - room.oldState.paginationToken, - limit, - 'b'); - }).done(function(res) { - const matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper(self)); - if (res.state) { - const stateEvents = utils.map(res.state, _PojoToMatrixEventMapper(self)); - room.currentState.setUnknownStateEvents(stateEvents); - } - room.addEventsToTimeline(matrixEvents, true, room.getLiveTimeline()); - room.oldState.paginationToken = res.end; - if (res.chunk.length === 0) { - room.oldState.paginationToken = null; - } - self.store.storeEvents(room, matrixEvents, res.end, true); - self._ongoingScrollbacks[room.roomId] = null; - _resolve(callback, defer, room); - }, function(err) { - self._ongoingScrollbacks[room.roomId] = { - errorTs: Date.now(), - }; - _reject(callback, defer, err); - }); + this._ongoingScrollbacks[room.roomId] = info; - return defer.promise; + return prom; }; /** @@ -3887,7 +3891,7 @@ MatrixClient.prototype.setRoomMutePushRule = function(scope, roomId, mute) { } else if (!hasDontNotifyRule) { // Remove the existing one before setting the mute push rule // This is a workaround to SYN-590 (Push rule update fails) - deferred = Promise.defer(); + deferred = utils.defer(); this.deletePushRule(scope, "room", roomPushRule.rule_id) .done(function() { self.addPushRule(scope, "room", roomId, { @@ -3906,26 +3910,26 @@ MatrixClient.prototype.setRoomMutePushRule = function(scope, roomId, mute) { } if (deferred) { - // Update this.pushRules when the operation completes - const ruleRefreshDeferred = Promise.defer(); - deferred.done(function() { - self.getPushRules().done(function(result) { - self.pushRules = result; - ruleRefreshDeferred.resolve(); + return new Promise((resolve, reject) => { + // Update this.pushRules when the operation completes + deferred.done(function() { + self.getPushRules().done(function(result) { + self.pushRules = result; + resolve(); + }, function(err) { + reject(err); + }); }, function(err) { - ruleRefreshDeferred.reject(err); - }); - }, function(err) { - // Update it even if the previous operation fails. This can help the - // app to recover when push settings has been modifed from another client - self.getPushRules().done(function(result) { - self.pushRules = result; - ruleRefreshDeferred.reject(err); - }, function(err2) { - ruleRefreshDeferred.reject(err); + // Update it even if the previous operation fails. This can help the + // app to recover when push settings has been modifed from another client + self.getPushRules().done(function(result) { + self.pushRules = result; + reject(err); + }, function(err2) { + reject(err); + }); }); }); - return ruleRefreshDeferred.promise; } }; @@ -4655,15 +4659,20 @@ function setupCallEventHandler(client) { // callId: [Candidate] }; - // Maintain a buffer of events before the client has synced for the first time. - // This buffer will be inspected to see if we should send incoming call - // notifications. It needs to be buffered to correctly determine if an - // incoming call has had a matching answer/hangup. + // The sync code always emits one event at a time, so it will patiently + // wait for us to finish processing a call invite before delivering the + // next event, even if that next event is a hangup. We therefore accumulate + // all our call events and then process them on the 'sync' event, ie. + // each time a sync has completed. This way, we can avoid emitting incoming + // call events if we get both the invite and answer/hangup in the same sync. + // This happens quite often, eg. replaying sync from storage, catchup sync + // after loading and after we've been offline for a bit. let callEventBuffer = []; - let isClientPrepared = false; - client.on("sync", function(state) { - if (state === "PREPARED") { - isClientPrepared = true; + function evaluateEventBuffer() { + if (client.getSyncState() === "SYNCING") { + // don't process any events until they are all decrypted + if (callEventBuffer.some((e) => e.isBeingDecrypted())) return; + const ignoreCallIds = {}; // Set // inspect the buffer and mark all calls which have been answered // or hung up before passing them to the call event handler. @@ -4676,39 +4685,51 @@ function setupCallEventHandler(client) { } // now loop through the buffer chronologically and inject them callEventBuffer.forEach(function(e) { - if (ignoreCallIds[e.getContent().call_id]) { - // This call has previously been ansered or hung up: ignore it + if ( + e.getType() === "m.call.invite" && + ignoreCallIds[e.getContent().call_id] + ) { + // This call has previously been answered or hung up: ignore it return; } callEventHandler(e); }); callEventBuffer = []; } - }); - - client.on("event", onEvent); + } + client.on("sync", evaluateEventBuffer); function onEvent(event) { - if (event.getType().indexOf("m.call.") !== 0) { - // not a call event - if (event.isBeingDecrypted() || event.isDecryptionFailure()) { - // not *yet* a call event, but might become one... - event.once("Event.decrypted", onEvent); - } - return; - } - if (!isClientPrepared) { + // any call events or ones that might be once they're decrypted + if (event.getType().indexOf("m.call.") === 0 || event.isBeingDecrypted()) { + // queue up for processing once all events from this sync have been + // processed (see above). callEventBuffer.push(event); - return; } - callEventHandler(event); + + if (event.isBeingDecrypted() || event.isDecryptionFailure()) { + // add an event listener for once the event is decrypted. + event.once("Event.decrypted", () => { + if (event.getType().indexOf("m.call.") === -1) return; + + if (callEventBuffer.includes(event)) { + // we were waiting for that event to decrypt, so recheck the buffer + evaluateEventBuffer(); + } else { + // This one wasn't buffered so just run the event handler for it + // straight away + callEventHandler(event); + } + }); + } } + client.on("event", onEvent); function callEventHandler(event) { const content = event.getContent(); let call = content.call_id ? client.callList[content.call_id] : undefined; let i; - //console.log("RECV %s content=%s", event.getType(), JSON.stringify(content)); + //console.info("RECV %s content=%s", event.getType(), JSON.stringify(content)); if (event.getType() === "m.call.invite") { if (event.getSender() === client.credentials.userId) { @@ -4879,18 +4900,18 @@ function checkTurnServers(client) { }); } -function _reject(callback, defer, err) { +function _reject(callback, reject, err) { if (callback) { callback(err); } - defer.reject(err); + reject(err); } -function _resolve(callback, defer, res) { +function _resolve(callback, resolve, res) { if (callback) { callback(null, res); } - defer.resolve(res); + resolve(res); } function _PojoToMatrixEventMapper(client) { @@ -5222,6 +5243,8 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED; * @param {object} data * @param {MatrixEvent} data.event the original verification request message * @param {Array} data.methods the verification methods that can be used + * @param {Number} data.timeout the amount of milliseconds that should be waited + * before cancelling the request automatically. * @param {Function} data.beginKeyVerification a function to call if a key * verification should be performed. The function takes one argument: the * name of the key verification method (taken from data.methods) to use. diff --git a/src/crypto/DeviceList.js b/src/crypto/DeviceList.js index 94b3140b0..27b1a23e4 100644 --- a/src/crypto/DeviceList.js +++ b/src/crypto/DeviceList.js @@ -31,6 +31,7 @@ import DeviceInfo from './deviceinfo'; import {CrossSigningInfo} from './CrossSigning'; import olmlib from './olmlib'; import IndexedDBCryptoStore from './store/indexeddb-crypto-store'; +import {sleep} from '../utils'; /* State transition diagram for DeviceList._deviceTrackingStatus @@ -763,7 +764,7 @@ class DeviceListUpdateSerialiser { // this serves as an easy solution for now. let prom = Promise.resolve(); for (const userId of downloadUsers) { - prom = prom.delay(5).then(() => { + prom = prom.then(sleep(5)).then(() => { return this._processQueryResponseForUser( userId, dk[userId], { master: masterKeys[userId], diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 7060074a7..b86b4f858 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -462,7 +462,7 @@ OlmDevice.prototype.createInboundSession = async function( */ OlmDevice.prototype.getSessionIdsForDevice = async function(theirDeviceIdentityKey) { if (this._sessionsInProgress[theirDeviceIdentityKey]) { - logger.log("waiting for session to be created"); + logger.log("waiting for olm session to be created"); try { await this._sessionsInProgress[theirDeviceIdentityKey]; } catch (e) { @@ -543,7 +543,7 @@ OlmDevice.prototype.getSessionIdForDevice = async function( */ OlmDevice.prototype.getSessionInfoForDevice = async function(deviceIdentityKey, nowait) { if (this._sessionsInProgress[deviceIdentityKey] && !nowait) { - logger.log("waiting for session to be created"); + logger.log("waiting for olm session to be created"); try { await this._sessionsInProgress[deviceIdentityKey]; } catch (e) { @@ -595,8 +595,8 @@ OlmDevice.prototype.encryptMessage = async function( (txn) => { this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => { const sessionDesc = sessionInfo.session.describe(); - console.log( - "Session ID " + sessionId + " to " + + logger.log( + "encryptMessage: Olm Session ID " + sessionId + " to " + theirDeviceIdentityKey + ": " + sessionDesc, ); res = sessionInfo.session.encrypt(payloadString); @@ -627,8 +627,8 @@ OlmDevice.prototype.decryptMessage = async function( (txn) => { this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => { const sessionDesc = sessionInfo.session.describe(); - console.log( - "Session ID " + sessionId + " to " + + logger.log( + "decryptMessage: Olm Session ID " + sessionId + " from " + theirDeviceIdentityKey + ": " + sessionDesc, ); payloadString = sessionInfo.session.decrypt(messageType, ciphertext); @@ -740,6 +740,8 @@ OlmDevice.prototype.createOutboundGroupSession = function() { OlmDevice.prototype.encryptGroupMessage = function(sessionId, payloadString) { const self = this; + logger.log(`encrypting msg with megolm session ${sessionId}`); + checkPayloadLength(payloadString); return this._getOutboundGroupSession(sessionId, function(session) { @@ -886,7 +888,9 @@ OlmDevice.prototype.addInboundGroupSession = async function( <= session.first_known_index()) { // existing session has lower index (i.e. can // decrypt more), so keep it - logger.log("Keeping existing session"); + logger.log( + `Keeping existing megolm session ${sessionId}`, + ); return; } } diff --git a/src/crypto/algorithms/megolm.js b/src/crypto/algorithms/megolm.js index 4262c2cee..286c29804 100644 --- a/src/crypto/algorithms/megolm.js +++ b/src/crypto/algorithms/megolm.js @@ -104,7 +104,7 @@ OutboundSessionInfo.prototype.sharedWithTooManyDevices = function( } if (!devicesInRoom.hasOwnProperty(userId)) { - logger.log("Starting new session because we shared with " + userId); + logger.log("Starting new megolm session because we shared with " + userId); return true; } @@ -115,7 +115,7 @@ OutboundSessionInfo.prototype.sharedWithTooManyDevices = function( if (!devicesInRoom[userId].hasOwnProperty(deviceId)) { logger.log( - "Starting new session because we shared with " + + "Starting new megolm session because we shared with " + userId + ":" + deviceId, ); return true; @@ -200,6 +200,8 @@ MegolmEncryption.prototype._ensureOutboundSession = function(devicesInRoom) { if (!session) { logger.log(`Starting new megolm session for room ${self._roomId}`); session = await self._prepareNewSession(); + logger.log(`Started new megolm session ${session.sessionId} ` + + `for room ${self._roomId}`); self._outboundSessions[session.sessionId] = session; } @@ -278,7 +280,7 @@ MegolmEncryption.prototype._prepareNewSession = async function() { ).catch((e) => { // This throws if the upload failed, but this is fine // since it will have written it to the db and will retry. - logger.log("Failed to back up group session", e); + logger.log("Failed to back up megolm session", e); }); } @@ -440,19 +442,19 @@ MegolmEncryption.prototype.reshareKeyWithDevice = async function( ) { const obSessionInfo = this._outboundSessions[sessionId]; if (!obSessionInfo) { - logger.debug("Session ID " + sessionId + " not found: not re-sharing keys"); + logger.debug(`megolm session ${sessionId} not found: not re-sharing keys`); return; } // The chain index of the key we previously sent this device if (obSessionInfo.sharedWithDevices[userId] === undefined) { - logger.debug("Session ID " + sessionId + " never shared with user " + userId); + logger.debug(`megolm session ${sessionId} never shared with user ${userId}`); return; } const sentChainIndex = obSessionInfo.sharedWithDevices[userId][device.deviceId]; if (sentChainIndex === undefined) { logger.debug( - "Session ID " + sessionId + " never shared with device " + + "megolm session ID " + sessionId + " never shared with device " + userId + ":" + device.deviceId, ); return; @@ -466,7 +468,7 @@ MegolmEncryption.prototype.reshareKeyWithDevice = async function( if (!key) { logger.warn( - "No outbound session key found for " + sessionId + ": not re-sharing keys", + `No inbound session key found for megolm ${sessionId}: not re-sharing keys`, ); return; } @@ -513,9 +515,8 @@ MegolmEncryption.prototype.reshareKeyWithDevice = async function( [device.deviceId]: encryptedContent, }, }); - logger.debug( - `Re-shared key for session ${sessionId} with ${userId}:${device.deviceId}`, - ); + logger.debug(`Re-shared key for megolm session ${sessionId} ` + + `with ${userId}:${device.deviceId}`); }; /** @@ -550,10 +551,10 @@ MegolmEncryption.prototype._shareKeyWithDevices = async function(session, device await this._encryptAndSendKeysToDevices( session, key.chain_index, userDeviceMaps[i], payload, ); - logger.log(`Completed megolm keyshare in ${this._roomId} ` - + `(slice ${i + 1}/${userDeviceMaps.length})`); + logger.log(`Completed megolm keyshare for ${session.sessionId} ` + + `in ${this._roomId} (slice ${i + 1}/${userDeviceMaps.length})`); } catch (e) { - logger.log(`megolm keyshare in ${this._roomId} ` + logger.log(`megolm keyshare for ${session.sessionId} in ${this._roomId} ` + `(slice ${i + 1}/${userDeviceMaps.length}) failed`); throw e; @@ -922,7 +923,7 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) { keysClaimed = event.getKeysClaimed(); } - logger.log(`Adding key for megolm session ${senderKey}|${sessionId}`); + logger.log(`Received and adding key for megolm session ${senderKey}|${sessionId}`); return this._olmDevice.addInboundGroupSession( content.room_id, senderKey, forwardingKeyChain, sessionId, content.session_key, keysClaimed, @@ -955,7 +956,7 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) { ).catch((e) => { // This throws if the upload failed, but this is fine // since it will have written it to the db and will retry. - logger.log("Failed to back up group session", e); + logger.log("Failed to back up megolm session", e); }); } }).catch((e) => { @@ -1088,7 +1089,7 @@ MegolmDecryption.prototype.importRoomKey = function(session) { ).catch((e) => { // This throws if the upload failed, but this is fine // since it will have written it to the db and will retry. - logger.log("Failed to back up group session", e); + logger.log("Failed to back up megolm session", e); }); } // have another go at decrypting events sent with this session. diff --git a/src/crypto/algorithms/olm.js b/src/crypto/algorithms/olm.js index f233e8f9f..93d651d49 100644 --- a/src/crypto/algorithms/olm.js +++ b/src/crypto/algorithms/olm.js @@ -139,7 +139,7 @@ OlmEncryption.prototype.encryptMessage = async function(room, eventType, content } } - return await Promise.all(promises).return(encryptedContent); + return await Promise.all(promises).then(() => encryptedContent); }; /** diff --git a/src/crypto/index.js b/src/crypto/index.js index db0368755..37f3b2d0f 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -54,6 +54,7 @@ import { newUnexpectedMessageError, newUnknownMethodError, } from './verification/Error'; +import {sleep} from '../utils'; const defaultVerificationMethods = { [ScanQRCode.NAME]: ScanQRCode, @@ -70,10 +71,44 @@ export const verificationMethods = { SAS: SAS.NAME, }; +// the recommended amount of time before a verification request +// should be (automatically) cancelled without user interaction +// and ignored. +const VERIFICATION_REQUEST_TIMEOUT = 5 * 60 * 1000; //5m +// to avoid almost expired verification notifications +// from showing a notification and almost immediately +// disappearing, also ignore verification requests that +// are this amount of time away from expiring. +const VERIFICATION_REQUEST_MARGIN = 3 * 1000; //3s + export function isCryptoAvailable() { return Boolean(global.Olm); } +/* subscribes to timeline events / to_device events for SAS verification */ +function listenForEvents(client, roomId, listener) { + let isEncrypted = false; + if (roomId) { + isEncrypted = client.isRoomEncrypted(roomId); + } + + if (isEncrypted) { + client.on("Event.decrypted", listener); + } + client.on("event", listener); + let subscribed = true; + return function() { + if (subscribed) { + if (isEncrypted) { + client.off("Event.decrypted", listener); + } + client.off("event", listener); + subscribed = false; + } + return null; + }; +} + const MIN_FORCE_SESSION_INTERVAL_MS = 60 * 60 * 1000; const KEY_BACKUP_KEYS_PER_REQUEST = 200; @@ -979,6 +1014,14 @@ Crypto.prototype.registerEventHandlers = function(eventEmitter) { eventEmitter.on("toDeviceEvent", function(event) { crypto._onToDeviceEvent(event); }); + + eventEmitter.on("Room.timeline", function(event) { + crypto._onTimelineEvent(event); + }); + + eventEmitter.on("Event.decrypted", function(event) { + crypto._onTimelineEvent(event); + }); }; @@ -1362,13 +1405,15 @@ function verificationEventHandler(target, userId, roomId, eventId) { || event.getSender() !== userId) { return; } - const content = event.getContent(); - if (!content["m.relates_to"]) { + // ignore events that haven't been decrypted yet. + // we also listen for undecrypted events, just in case + // the other side would be sending unencrypted events in an e2ee room + if (event.getType() === "m.room.encrypted") { return; } - const relatesTo - = content["m.relationship"] || content["m.relates_to"]; - if (!relatesTo.rel_type + const relatesTo = event.getRelation(); + if (!relatesTo + || !relatesTo.rel_type || relatesTo.rel_type !== "m.reference" || !relatesTo.event_id || relatesTo.event_id !== eventId) { @@ -1425,7 +1470,9 @@ Crypto.prototype.requestVerificationDM = async function(userId, roomId, methods) ); // this handler gets removed when the verification finishes // (see the verify method of crypto/verification/Base.js) - this._baseApis.on("event", verifier.handler); + const subscription = + listenForEvents(this._baseApis, roomId, verifier.handler); + verifier.setEventsSubscription(subscription); resolve(verifier); break; } @@ -1435,14 +1482,19 @@ Crypto.prototype.requestVerificationDM = async function(userId, roomId, methods) } } }; - this._baseApis.on("event", listener); + let initialResponseSubscription = + listenForEvents(this._baseApis, roomId, listener); const resolve = (...args) => { - this._baseApis.off("event", listener); + if (initialResponseSubscription) { + initialResponseSubscription = initialResponseSubscription(); + } _resolve(...args); }; const reject = (...args) => { - this._baseApis.off("event", listener); + if (initialResponseSubscription) { + initialResponseSubscription = initialResponseSubscription(); + } _reject(...args); }; }); @@ -1477,7 +1529,9 @@ Crypto.prototype.acceptVerificationDM = function(event, Method) { verifier.handler = verificationEventHandler( verifier, event.getSender(), event.getRoomId(), event.getId(), ); - this._baseApis.on("event", verifier.handler); + const subscription = listenForEvents( + this._baseApis, event.getRoomId(), verifier.handler); + verifier.setEventsSubscription(subscription); return verifier; }; @@ -1668,7 +1722,7 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic // It would otherwise just throw later as an unknown algorithm would, but we may // as well catch this here if (!config.algorithm) { - console.log("Ignoring setRoomEncryption with no algorithm"); + logger.log("Ignoring setRoomEncryption with no algorithm"); return; } @@ -1854,17 +1908,15 @@ Crypto.prototype.exportRoomKeys = async function() { * @return {module:client.Promise} a promise which resolves once the keys have been imported */ Crypto.prototype.importRoomKeys = function(keys) { - return Promise.map( - keys, (key) => { - if (!key.room_id || !key.algorithm) { - logger.warn("ignoring room key entry with missing fields", key); - return null; - } + return Promise.all(keys.map((key) => { + if (!key.room_id || !key.algorithm) { + logger.warn("ignoring room key entry with missing fields", key); + return null; + } - const alg = this._getRoomDecryptor(key.room_id, key.algorithm); - return alg.importRoomKey(key); - }, - ); + const alg = this._getRoomDecryptor(key.room_id, key.algorithm); + return alg.importRoomKey(key); + })); }; /** @@ -1883,7 +1935,7 @@ Crypto.prototype.scheduleKeyBackupSend = async function(maxDelay = 10000) { // requests from different clients hitting the server all at // the same time when a new key is sent const delay = Math.random() * maxDelay; - await Promise.delay(delay); + await sleep(delay); let numFailures = 0; // number of consecutive failures while (1) { if (!this.backupKey) { @@ -1917,7 +1969,7 @@ Crypto.prototype.scheduleKeyBackupSend = async function(maxDelay = 10000) { } if (numFailures) { // exponential backoff if we have failures - await Promise.delay(1000 * Math.pow(2, Math.min(numFailures - 1, 4))); + await sleep(1000 * Math.pow(2, Math.min(numFailures - 1, 4))); } } } finally { @@ -2326,6 +2378,9 @@ Crypto.prototype._getTrackedE2eRooms = function() { Crypto.prototype._onToDeviceEvent = function(event) { try { + logger.log(`received to_device ${event.getType()} from: ` + + `${event.getSender()} id: ${event.getId()}`); + if (event.getType() == "m.room_key" || event.getType() == "m.forwarded_room_key") { this._onRoomKeyEvent(event); @@ -2392,8 +2447,8 @@ Crypto.prototype._onKeyVerificationRequest = function(event) { const content = event.getContent(); if (!("from_device" in content) || typeof content.from_device !== "string" - || !("transaction_id" in content) || typeof content.from_device !== "string" - || !("methods" in content) || !(content.methods instanceof Array) + || !("transaction_id" in content) + || !("methods" in content) || !Array.isArray(content.methods) || !("timestamp" in content) || typeof content.timestamp !== "number") { logger.warn("received invalid verification request from " + event.getSender()); // ignore event if malformed @@ -2469,6 +2524,7 @@ Crypto.prototype._onKeyVerificationRequest = function(event) { // notify the application of the verification request, so it can // decide what to do with it const request = { + timeout: VERIFICATION_REQUEST_TIMEOUT, event: event, methods: methods, beginKeyVerification: (method) => { @@ -2601,6 +2657,31 @@ Crypto.prototype._onKeyVerificationStart = function(event) { } } this._baseApis.emit("crypto.verification.start", verifier); + + // Riot does not implement `m.key.verification.request` while + // verifying over to_device messages, but the protocol is made to + // work when starting with a `m.key.verification.start` event straight + // away as well. Verification over DM *does* start with a request event first, + // and to expose a uniform api to monitor verification requests, we mock + // the request api here for to_device messages. + // "crypto.verification.start" is kept for backwards compatibility. + + // ahh, this will create 2 request notifications for clients that do support the request event + // so maybe we should remove emitting the request when actually receiving it *sigh* + const requestAdapter = { + event, + timeout: VERIFICATION_REQUEST_TIMEOUT, + methods: [content.method], + beginKeyVerification: (method) => { + if (content.method === method) { + return verifier; + } + }, + cancel: () => { + return verifier.cancel("User cancelled"); + }, + }; + this._baseApis.emit("crypto.verification.request", requestAdapter); } }; @@ -2633,6 +2714,53 @@ Crypto.prototype._onKeyVerificationMessage = function(event) { } }; +/** + * Handle key verification requests sent as timeline events + * + * @private + * @param {module:models/event.MatrixEvent} event the timeline event + */ +Crypto.prototype._onTimelineEvent = function(event) { + if (event.getType() !== "m.room.message") { + return; + } + const content = event.getContent(); + if (content.msgtype !== "m.key.verification.request") { + return; + } + // ignore event if malformed + if (!("from_device" in content) || typeof content.from_device !== "string" + || !("methods" in content) || !Array.isArray(content.methods) + || !("to" in content) || typeof content.to !== "string") { + logger.warn("received invalid verification request over DM from " + + event.getSender()); + return; + } + // check the request was directed to the syncing user + if (content.to !== this._baseApis.getUserId()) { + return; + } + + const timeout = VERIFICATION_REQUEST_TIMEOUT - VERIFICATION_REQUEST_MARGIN; + if (event.getLocalAge() >= timeout) { + return; + } + const request = { + event, + timeout: VERIFICATION_REQUEST_TIMEOUT, + methods: content.methods, + beginKeyVerification: (method) => { + const verifier = this.acceptVerificationDM(event, method); + return verifier; + }, + cancel: () => { + const verifier = this.acceptVerificationDM(event, content.methods[0]); + verifier.cancel("User declined"); + }, + }; + this._baseApis.emit("crypto.verification.request", request); +}; + /** * Handle a toDevice event that couldn't be decrypted * @@ -2815,14 +2943,10 @@ Crypto.prototype._processReceivedRoomKeyRequests = async function() { // cancellation (and end up with a cancelled request), rather than the // cancellation before the request (and end up with an outstanding // request which should have been cancelled.) - await Promise.map( - requests, (req) => - this._processReceivedRoomKeyRequest(req), - ); - await Promise.map( - cancellations, (cancellation) => - this._processReceivedRoomKeyRequestCancellation(cancellation), - ); + await Promise.all(requests.map((req) => + this._processReceivedRoomKeyRequest(req))); + await Promise.all(cancellations.map((cancellation) => + this._processReceivedRoomKeyRequestCancellation(cancellation))); } catch (e) { logger.error(`Error processing room key requsts: ${e}`); } finally { diff --git a/src/crypto/olmlib.js b/src/crypto/olmlib.js index 93a492e26..4af8e035d 100644 --- a/src/crypto/olmlib.js +++ b/src/crypto/olmlib.js @@ -287,12 +287,12 @@ async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceIn ); } catch (e) { // possibly a bad key - logger.error("Error starting session with device " + + logger.error("Error starting olm session with device " + userId + ":" + deviceId + ": " + e); return null; } - logger.log("Started new sessionid " + sid + + logger.log("Started new olm sessionid " + sid + " for device " + userId + ":" + deviceId); return sid; } diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 98c5d8945..ede785d00 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -58,35 +58,34 @@ export class Backend { getOrAddOutgoingRoomKeyRequest(request) { const requestBody = request.requestBody; - const deferred = Promise.defer(); - const txn = this._db.transaction("outgoingRoomKeyRequests", "readwrite"); - txn.onerror = deferred.reject; + return new Promise((resolve, reject) => { + const txn = this._db.transaction("outgoingRoomKeyRequests", "readwrite"); + txn.onerror = reject; - // first see if we already have an entry for this request. - this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { - if (existing) { - // this entry matches the request - return it. + // first see if we already have an entry for this request. + this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { + if (existing) { + // this entry matches the request - return it. + logger.log( + `already have key request outstanding for ` + + `${requestBody.room_id} / ${requestBody.session_id}: ` + + `not sending another`, + ); + resolve(existing); + return; + } + + // we got to the end of the list without finding a match + // - add the new request. logger.log( - `already have key request outstanding for ` + - `${requestBody.room_id} / ${requestBody.session_id}: ` + - `not sending another`, + `enqueueing key request for ${requestBody.room_id} / ` + + requestBody.session_id, ); - deferred.resolve(existing); - return; - } - - // we got to the end of the list without finding a match - // - add the new request. - logger.log( - `enqueueing key request for ${requestBody.room_id} / ` + - requestBody.session_id, - ); - txn.oncomplete = () => { deferred.resolve(request); }; - const store = txn.objectStore("outgoingRoomKeyRequests"); - store.add(request); + txn.oncomplete = () => { resolve(request); }; + const store = txn.objectStore("outgoingRoomKeyRequests"); + store.add(request); + }); }); - - return deferred.promise; } /** @@ -100,15 +99,14 @@ export class Backend { * not found */ getOutgoingRoomKeyRequest(requestBody) { - const deferred = Promise.defer(); + return new Promise((resolve, reject) => { + const txn = this._db.transaction("outgoingRoomKeyRequests", "readonly"); + txn.onerror = reject; - const txn = this._db.transaction("outgoingRoomKeyRequests", "readonly"); - txn.onerror = deferred.reject; - - this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { - deferred.resolve(existing); + this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { + resolve(existing); + }); }); - return deferred.promise; } /** diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index b2c2fe4d0..2919fb9ca 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -287,7 +287,9 @@ export default class IndexedDBCryptoStore { * @param {function(string)} func Called with the account pickle */ getAccount(txn, func) { - this._backendPromise.value().getAccount(txn, func); + this._backendPromise.then(backend => { + backend.getAccount(txn, func); + }); } /** @@ -298,7 +300,9 @@ export default class IndexedDBCryptoStore { * @param {string} newData The new account pickle to store. */ storeAccount(txn, newData) { - this._backendPromise.value().storeAccount(txn, newData); + this._backendPromise.then(backend => { + backend.storeAccount(txn, newData); + }); } /** @@ -310,7 +314,9 @@ export default class IndexedDBCryptoStore { * { key_type: base64 encoded seed } where key type = user_signing_key_seed or self_signing_key_seed */ getCrossSigningKeys(txn, func) { - this._backendPromise.value().getCrossSigningKeys(txn, func); + this._backendPromise.then(backend => { + backend.getCrossSigningKeys(txn, func); + }); } /** @@ -320,7 +326,9 @@ export default class IndexedDBCryptoStore { * @param {string} keys keys object as getCrossSigningKeys() */ storeCrossSigningKeys(txn, keys) { - this._backendPromise.value().storeCrossSigningKeys(txn, keys); + this._backendPromise.then(backend => { + backend.storeCrossSigningKeys(txn, keys); + }); } // Olm sessions @@ -331,7 +339,9 @@ export default class IndexedDBCryptoStore { * @param {function(int)} func Called with the count of sessions */ countEndToEndSessions(txn, func) { - this._backendPromise.value().countEndToEndSessions(txn, func); + this._backendPromise.then(backend => { + backend.countEndToEndSessions(txn, func); + }); } /** @@ -347,7 +357,9 @@ export default class IndexedDBCryptoStore { * a message. */ getEndToEndSession(deviceKey, sessionId, txn, func) { - this._backendPromise.value().getEndToEndSession(deviceKey, sessionId, txn, func); + this._backendPromise.then(backend => { + backend.getEndToEndSession(deviceKey, sessionId, txn, func); + }); } /** @@ -362,7 +374,9 @@ export default class IndexedDBCryptoStore { * a message. */ getEndToEndSessions(deviceKey, txn, func) { - this._backendPromise.value().getEndToEndSessions(deviceKey, txn, func); + this._backendPromise.then(backend => { + backend.getEndToEndSessions(deviceKey, txn, func); + }); } /** @@ -373,7 +387,9 @@ export default class IndexedDBCryptoStore { * and session keys. */ getAllEndToEndSessions(txn, func) { - this._backendPromise.value().getAllEndToEndSessions(txn, func); + this._backendPromise.then(backend => { + backend.getAllEndToEndSessions(txn, func); + }); } /** @@ -384,12 +400,14 @@ export default class IndexedDBCryptoStore { * @param {*} txn An active transaction. See doTxn(). */ storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { - this._backendPromise.value().storeEndToEndSession( - deviceKey, sessionId, sessionInfo, txn, - ); + this._backendPromise.then(backend => { + backend.storeEndToEndSession( + deviceKey, sessionId, sessionInfo, txn, + ); + }); } - // Inbound group saessions + // Inbound group sessions /** * Retrieve the end-to-end inbound group session for a given @@ -401,9 +419,11 @@ export default class IndexedDBCryptoStore { * to Base64 end-to-end session. */ getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { - this._backendPromise.value().getEndToEndInboundGroupSession( - senderCurve25519Key, sessionId, txn, func, - ); + this._backendPromise.then(backend => { + backend.getEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, txn, func, + ); + }); } /** @@ -414,7 +434,9 @@ export default class IndexedDBCryptoStore { * sessionData}, then once with null to indicate the end of the list. */ getAllEndToEndInboundGroupSessions(txn, func) { - this._backendPromise.value().getAllEndToEndInboundGroupSessions(txn, func); + this._backendPromise.then(backend => { + backend.getAllEndToEndInboundGroupSessions(txn, func); + }); } /** @@ -427,9 +449,11 @@ export default class IndexedDBCryptoStore { * @param {*} txn An active transaction. See doTxn(). */ addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this._backendPromise.value().addEndToEndInboundGroupSession( - senderCurve25519Key, sessionId, sessionData, txn, - ); + this._backendPromise.then(backend => { + backend.addEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, sessionData, txn, + ); + }); } /** @@ -442,9 +466,11 @@ export default class IndexedDBCryptoStore { * @param {*} txn An active transaction. See doTxn(). */ storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this._backendPromise.value().storeEndToEndInboundGroupSession( - senderCurve25519Key, sessionId, sessionData, txn, - ); + this._backendPromise.then(backend => { + backend.storeEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, sessionData, txn, + ); + }); } // End-to-end device tracking @@ -460,7 +486,9 @@ export default class IndexedDBCryptoStore { * @param {*} txn An active transaction. See doTxn(). */ storeEndToEndDeviceData(deviceData, txn) { - this._backendPromise.value().storeEndToEndDeviceData(deviceData, txn); + this._backendPromise.then(backend => { + backend.storeEndToEndDeviceData(deviceData, txn); + }); } /** @@ -471,7 +499,9 @@ export default class IndexedDBCryptoStore { * device data */ getEndToEndDeviceData(txn, func) { - this._backendPromise.value().getEndToEndDeviceData(txn, func); + this._backendPromise.then(backend => { + backend.getEndToEndDeviceData(txn, func); + }); } // End to End Rooms @@ -483,7 +513,9 @@ export default class IndexedDBCryptoStore { * @param {*} txn An active transaction. See doTxn(). */ storeEndToEndRoom(roomId, roomInfo, txn) { - this._backendPromise.value().storeEndToEndRoom(roomId, roomInfo, txn); + this._backendPromise.then(backend => { + backend.storeEndToEndRoom(roomId, roomInfo, txn); + }); } /** @@ -492,7 +524,9 @@ export default class IndexedDBCryptoStore { * @param {function(Object)} func Function called with the end to end encrypted rooms */ getEndToEndRooms(txn, func) { - this._backendPromise.value().getEndToEndRooms(txn, func); + this._backendPromise.then(backend => { + backend.getEndToEndRooms(txn, func); + }); } // session backups diff --git a/src/crypto/verification/Base.js b/src/crypto/verification/Base.js index 842c15eba..0026facaa 100644 --- a/src/crypto/verification/Base.js +++ b/src/crypto/verification/Base.js @@ -75,14 +75,15 @@ export default class VerificationBase extends EventEmitter { this._done = false; this._promise = null; this._transactionTimeoutTimer = null; + this._eventsSubscription = null; // At this point, the verification request was received so start the timeout timer. this._resetTimer(); if (this.roomId) { - this._send = this._sendMessage; + this._sendWithTxnId = this._sendMessage; } else { - this._send = this._sendToDevice; + this._sendWithTxnId = this._sendToDevice; } } @@ -106,13 +107,43 @@ export default class VerificationBase extends EventEmitter { } } + _contentFromEventWithTxnId(event) { + if (this.roomId) { // verification as timeline event + // ensure m.related_to is included in e2ee rooms + // as the field is excluded from encryption + const content = Object.assign({}, event.getContent()); + content["m.relates_to"] = event.getRelation(); + return content; + } else { // verification as to_device event + return event.getContent(); + } + } + + /* creates a content object with the transaction id added to it */ + _contentWithTxnId(content) { + const copy = Object.assign({}, content); + if (this.roomId) { // verification as timeline event + copy["m.relates_to"] = { + rel_type: "m.reference", + event_id: this.transactionId, + }; + } else { // verification as to_device event + copy.transaction_id = this.transactionId; + } + return copy; + } + + _send(type, contentWithoutTxnId) { + const content = this._contentWithTxnId(contentWithoutTxnId); + return this._sendWithTxnId(type, content); + } + /* send a message to the other participant, using to-device messages */ _sendToDevice(type, content) { if (this._done) { return Promise.reject(new Error("Verification is already done")); } - content.transaction_id = this.transactionId; return this._baseApis.sendToDevice(type, { [this.userId]: { [this.deviceId]: content }, }); @@ -124,12 +155,6 @@ export default class VerificationBase extends EventEmitter { if (this._done) { return Promise.reject(new Error("Verification is already done")); } - // FIXME: if MSC1849 decides to use m.relationship instead of - // m.relates_to, we should follow suit here - content["m.relates_to"] = { - rel_type: "m.reference", - event_id: this.transactionId, - }; return this._baseApis.sendEvent(this.roomId, type, content); } @@ -223,6 +248,10 @@ export default class VerificationBase extends EventEmitter { // but no reject function. If cancel is called again, we'd error. if (this._reject) this._reject(e); } else { + // unsubscribe from events, this happens in _reject usually but we don't have one here + if (this._eventsSubscription) { + this._eventsSubscription = this._eventsSubscription(); + } // FIXME: this causes an "Uncaught promise" console message // if nothing ends up chaining this promise. this._promise = Promise.reject(e); @@ -247,7 +276,10 @@ export default class VerificationBase extends EventEmitter { this._done = true; this._endTimer(); if (this.handler) { - this._baseApis.off("event", this.handler); + // these listeners are attached in Crypto.acceptVerificationDM + if (this._eventsSubscription) { + this._eventsSubscription = this._eventsSubscription(); + } } resolve(...args); }; @@ -255,7 +287,10 @@ export default class VerificationBase extends EventEmitter { this._done = true; this._endTimer(); if (this.handler) { - this._baseApis.off("event", this.handler); + // these listeners are attached in Crypto.acceptVerificationDM + if (this._eventsSubscription) { + this._eventsSubscription = this._eventsSubscription(); + } } reject(...args); }; @@ -309,4 +344,8 @@ export default class VerificationBase extends EventEmitter { await this._baseApis.setDeviceVerified(userId, deviceId); } } + + setEventsSubscription(subscription) { + this._eventsSubscription = subscription; + } } diff --git a/src/crypto/verification/SAS.js b/src/crypto/verification/SAS.js index d1cb0efe1..97dc8ffc6 100644 --- a/src/crypto/verification/SAS.js +++ b/src/crypto/verification/SAS.js @@ -205,7 +205,7 @@ export default class SAS extends Base { } async _doSendVerification() { - const initialMessage = { + const initialMessage = this._contentWithTxnId({ method: SAS.NAME, from_device: this._baseApis.deviceId, key_agreement_protocols: KEY_AGREEMENT_LIST, @@ -213,10 +213,10 @@ export default class SAS extends Base { message_authentication_codes: MAC_LIST, // FIXME: allow app to specify what SAS methods can be used short_authentication_string: SAS_LIST, - }; - // NOTE: this._send will modify initialMessage to include the - // transaction_id field, or the m.relationship/m.relates_to field - this._send("m.key.verification.start", initialMessage); + }); + // add the transaction id to the message beforehand because + // it needs to be included in the commitment hash later on + this._sendWithTxnId("m.key.verification.start", initialMessage); let e = await this._waitForEvent("m.key.verification.accept"); @@ -281,7 +281,10 @@ export default class SAS extends Base { } async _doRespondVerification() { - let content = this.startEvent.getContent(); + // as m.related_to is not included in the encrypted content in e2e rooms, + // we need to make sure it is added + let content = this._contentFromEventWithTxnId(this.startEvent); + // Note: we intersect using our pre-made lists, rather than the sets, // so that the result will be in our order of preference. Then // fetching the first element from the array will give our preferred diff --git a/src/models/event.js b/src/models/event.js index 6aa39c943..83ca20d2e 100644 --- a/src/models/event.js +++ b/src/models/event.js @@ -297,6 +297,15 @@ utils.extend(module.exports.MatrixEvent.prototype, { return this.getUnsigned().age || this.event.age; // v2 / v1 }, + /** + * Get the age of the event when this function was called. + * Relies on the local clock being in sync with the clock of the original homeserver. + * @return {Number} The age of this event in milliseconds. + */ + getLocalAge: function() { + return Date.now() - this.getTs(); + }, + /** * Get the event state_key if it has one. This will return undefined * for message events. diff --git a/src/models/relations.js b/src/models/relations.js index 2ea549667..81ead22de 100644 --- a/src/models/relations.js +++ b/src/models/relations.js @@ -15,7 +15,7 @@ limitations under the License. */ import EventEmitter from 'events'; -import { EventStatus } from '../../lib/models/event'; +import { EventStatus } from '../models/event'; /** * A container for relation events that supports easy access to common ways of diff --git a/src/models/room.js b/src/models/room.js index ce240a876..e6d24c5d5 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -895,7 +895,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline, * @return {RoomMember} The member or null. */ Room.prototype.getMember = function(userId) { - return this.currentState.getMember(userId); + return this.currentState.getMember(userId); }; /** @@ -903,7 +903,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline, * @return {RoomMember[]} A list of currently joined members. */ Room.prototype.getJoinedMembers = function() { - return this.getMembersWithMembership("join"); + return this.getMembersWithMembership("join"); }; /** diff --git a/src/pushprocessor.js b/src/pushprocessor.js index 0cf05cacf..3d0fc2db5 100644 --- a/src/pushprocessor.js +++ b/src/pushprocessor.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {escapeRegExp, globToRegexp} from "./utils"; +import {escapeRegExp, globToRegexp, isNullOrUndefined} from "./utils"; /** * @module pushprocessor @@ -268,7 +268,7 @@ function PushProcessor(client) { } const val = valueForDottedKey(cond.key, ev); - if (!val || typeof val != 'string') { + if (typeof val !== 'string') { return false; } @@ -304,10 +304,10 @@ function PushProcessor(client) { // special-case the first component to deal with encrypted messages const firstPart = parts[0]; - if (firstPart == 'content') { + if (firstPart === 'content') { val = ev.getContent(); parts.shift(); - } else if (firstPart == 'type') { + } else if (firstPart === 'type') { val = ev.getType(); parts.shift(); } else { @@ -316,11 +316,11 @@ function PushProcessor(client) { } while (parts.length > 0) { - const thispart = parts.shift(); - if (!val[thispart]) { + const thisPart = parts.shift(); + if (isNullOrUndefined(val[thisPart])) { return null; } - val = val[thispart]; + val = val[thisPart]; } return val; }; diff --git a/src/realtime-callbacks.js b/src/realtime-callbacks.js index ee2faa1e5..7a4401257 100644 --- a/src/realtime-callbacks.js +++ b/src/realtime-callbacks.js @@ -48,7 +48,7 @@ const debuglog = function() {}; * * Intended for use by the unit tests. * - * @param {function} f function which should return a millisecond counter + * @param {function} [f] function which should return a millisecond counter * * @internal */ diff --git a/src/store/indexeddb-remote-backend.js b/src/store/indexeddb-remote-backend.js index 920739b6e..270bef961 100644 --- a/src/store/indexeddb-remote-backend.js +++ b/src/store/indexeddb-remote-backend.js @@ -17,6 +17,7 @@ limitations under the License. import Promise from 'bluebird'; import logger from '../logger'; +import {defer} from '../utils'; /** * An IndexedDB store backend where the actual backend sits in a web @@ -152,7 +153,7 @@ RemoteIndexedDBStoreBackend.prototype = { // the promise automatically gets rejected return Promise.resolve().then(() => { const seq = this._nextSeq++; - const def = Promise.defer(); + const def = defer(); this._inFlight[seq] = def; diff --git a/src/timeline-window.js b/src/timeline-window.js index a3edabcd0..9d845dd61 100644 --- a/src/timeline-window.js +++ b/src/timeline-window.js @@ -132,14 +132,15 @@ TimelineWindow.prototype.load = function(initialEventId, initialWindowSize) { // feeling snappy. // if (initialEventId) { - const prom = this._client.getEventTimeline(this._timelineSet, initialEventId); - - if (prom.isFulfilled()) { - initFields(prom.value()); - return Promise.resolve(); - } else { - return prom.then(initFields); + const timeline = this._timelineSet.getTimelineForEvent(initialEventId); + if (timeline) { + // hot-path optimization to save a reactor tick by replicating the sync check getTimelineForEvent does. + initFields(timeline); + return Promise.resolve(timeline); } + + const prom = this._client.getEventTimeline(this._timelineSet, initialEventId); + return prom.then(initFields); } else { const tl = this._timelineSet.getLiveTimeline(); initFields(tl); diff --git a/src/utils.js b/src/utils.js index b752299f0..49863ecd8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -21,6 +21,7 @@ limitations under the License. */ const unhomoglyph = require('unhomoglyph'); +import Promise from 'bluebird'; /** * Encode a dictionary of query parameters. @@ -708,3 +709,25 @@ module.exports.ensureNoTrailingSlash = function(url) { return url; } }; + +// Returns a promise which resolves with a given value after the given number of ms +module.exports.sleep = (ms, value) => new Promise((resolve => { + setTimeout(resolve, ms, value); +})); + +module.exports.isNullOrUndefined = function(val) { + return val === null || val === undefined; +}; + +// Returns a Deferred +module.exports.defer = () => { + let resolve; + let reject; + + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + + return {resolve, reject, promise}; +}; diff --git a/yarn.lock b/yarn.lock index d708910ec..af06e632b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== @@ -89,6 +89,33 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@types/json-schema@^7.0.3": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" + integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== + +"@typescript-eslint/experimental-utils@^2.5.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.8.0.tgz#208b4164d175587e9b03ce6fea97d55f19c30ca9" + integrity sha512-jZ05E4SxCbbXseQGXOKf3ESKcsGxT8Ucpkp1jiVp55MGhOvZB2twmWKf894PAuVQTCgbPbJz9ZbRDqtUWzP8xA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.8.0" + eslint-scope "^5.0.0" + +"@typescript-eslint/typescript-estree@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.8.0.tgz#fcc3fe6532840085d29b75432c8a59895876aeca" + integrity sha512-ksvjBDTdbAQ04cR5JyFSDX113k66FxH1tAXmi+dj6hufsl/G0eMc/f1GgLjEVPkYClDbRKv+rnBFuE5EusomUw== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash.unescape "4.0.1" + semver "^6.3.0" + tsutils "^3.17.1" + JSONStream@^1.0.3: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -97,16 +124,16 @@ JSONStream@^1.0.3: jsonparse "^1.2.0" through ">=2.2.7 <3" +abab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= - accessory@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/accessory/-/accessory-1.1.0.tgz#7833e9839a32ded76d26021f36a41707a520f593" @@ -116,6 +143,14 @@ accessory@~1.1.0: balanced-match "~0.2.0" dot-parts "~1.0.0" +acorn-globals@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + acorn-jsx@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" @@ -130,17 +165,22 @@ acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1: acorn-walk "^7.0.0" xtend "^4.0.2" +acorn-walk@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + acorn-walk@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== -acorn@^5.2.1: +acorn@^5.2.1, acorn@^5.5.3: version "5.7.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.7: +acorn@^6.0.1, acorn@^6.0.7: version "6.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== @@ -170,12 +210,7 @@ another-json@^0.2.0: resolved "https://registry.yarnpkg.com/another-json/-/another-json-0.2.0.tgz#b5f4019c973b6dd5c6506a2d93469cb6d32aeedc" integrity sha1-tfQBnJc7bdXGUGotk0acttMq7tw= -ansi-colors@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== - -ansi-escapes@^3.2.0: +ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -228,6 +263,13 @@ ap@~0.2.0: resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110" integrity sha1-rglCYAspkS8NKxTsYMRejzMLYRA= +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + integrity sha1-126/jKlNJ24keja61EpLdKthGZE= + dependencies: + default-require-extensions "^1.0.0" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -270,6 +312,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" @@ -280,6 +327,11 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -324,10 +376,17 @@ async-each@^1.0.0, async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async@1.x: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.1.4: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" asynckit@^0.4.0: version "0.4.0" @@ -380,7 +439,7 @@ babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-core@^6.26.0: +babel-core@^6.0.0, babel-core@^6.26.0: version "6.26.3" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== @@ -417,7 +476,7 @@ babel-eslint@^10.0.1: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-generator@^6.26.0: +babel-generator@^6.18.0, babel-generator@^6.26.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== @@ -533,6 +592,14 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" +babel-jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1" + integrity sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew== + dependencies: + babel-plugin-istanbul "^4.1.6" + babel-preset-jest "^23.2.0" + babel-messages@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" @@ -547,6 +614,21 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-istanbul@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45" + integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ== + dependencies: + babel-plugin-syntax-object-rest-spread "^6.13.0" + find-up "^2.1.0" + istanbul-lib-instrument "^1.10.1" + test-exclude "^4.2.1" + +babel-plugin-jest-hoist@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" + integrity sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc= + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -562,6 +644,11 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= +babel-plugin-syntax-object-rest-spread@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= + babel-plugin-transform-async-to-bluebird@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-bluebird/-/babel-plugin-transform-async-to-bluebird-1.1.1.tgz#46ea3e7c5af629782ac9f1ed1b7cd38f8425afd4" @@ -849,6 +936,14 @@ babel-preset-es2016@^6.24.1: dependencies: babel-plugin-transform-exponentiation-operator "^6.24.1" +babel-preset-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" + integrity sha1-jsegOhOPABoaj7HoETZSvxpV2kY= + dependencies: + babel-plugin-jest-hoist "^23.2.0" + babel-plugin-syntax-object-rest-spread "^6.13.0" + babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" @@ -870,7 +965,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.9.0: +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.9.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= @@ -881,7 +976,7 @@ babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.9.0: babylon "^6.18.0" lodash "^4.17.4" -babel-traverse@^6.10.4, babel-traverse@^6.24.1, babel-traverse@^6.26.0: +babel-traverse@^6.0.0, babel-traverse@^6.10.4, babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= @@ -896,7 +991,7 @@ babel-traverse@^6.10.4, babel-traverse@^6.24.1, babel-traverse@^6.26.0: invariant "^2.2.2" lodash "^4.17.4" -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: +babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= @@ -1018,23 +1113,23 @@ browser-pack@^6.0.1: through2 "^2.0.0" umd "^3.0.0" +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + browser-request@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" integrity sha1-ns5bWsqJopkyJC4Yv5M975h2zBc= -browser-resolve@^1.11.0, browser-resolve@^1.7.0: +browser-resolve@^1.11.0, browser-resolve@^1.11.3, browser-resolve@^1.7.0: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== dependencies: resolve "1.1.7" -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -1166,6 +1261,13 @@ bs58@^4.0.1: dependencies: base-x "^3.0.2" +bser@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1209,15 +1311,27 @@ cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +capture-exit@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f" + integrity sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28= + dependencies: + rsvp "^3.3.3" caseless@~0.12.0: version "0.12.0" @@ -1296,6 +1410,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -1326,14 +1445,19 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" @@ -1431,6 +1555,13 @@ convert-source-map@^1.1.0, convert-source-map@^1.1.3, convert-source-map@^1.5.0, dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.4.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" @@ -1482,7 +1613,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1510,6 +1641,18 @@ crypto-browserify@^3.0.0: randombytes "^2.0.0" randomfill "^1.0.3" +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + dash-ast@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" @@ -1522,18 +1665,20 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-urls@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= -debug@3.2.6, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1541,14 +1686,21 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^4.0.1, debug@^4.1.0: +debug@^3.1.0, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" -decamelize@^1.2.0: +decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -1568,6 +1720,13 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + integrity sha1-836hXT4T/9m0N9M+GnW1+5eHTLg= + dependencies: + strip-bom "^2.0.0" + define-properties@^1.1.2, define-properties@^1.1.3, define-properties@~1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -1642,6 +1801,11 @@ detect-libc@^1.0.2: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + detective@^4.5.0: version "4.7.1" resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" @@ -1667,7 +1831,7 @@ detective@~3.1.0: escodegen "~1.1.0" esprima-fb "3001.1.0-dev-harmony-fb" -diff@3.5.0: +diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -1693,6 +1857,13 @@ domain-browser@^1.2.0: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + dot-parts@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/dot-parts/-/dot-parts-1.0.1.tgz#884bd7bcfc3082ffad2fe5db53e494d8f3e0743f" @@ -1731,11 +1902,25 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.12.0: version "1.14.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" @@ -1777,7 +1962,7 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -1787,17 +1972,17 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@1.8.x: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= +escodegen@^1.9.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" + integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" + esprima "^3.1.3" + estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: - source-map "~0.2.0" + source-map "~0.6.1" escodegen@~1.1.0: version "1.1.0" @@ -1822,6 +2007,13 @@ eslint-plugin-babel@^5.3.0: dependencies: eslint-rule-composer "^0.3.0" +eslint-plugin-jest@^23.0.4: + version "23.0.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.0.4.tgz#1ab81ffe3b16c5168efa72cbd4db14d335092aa0" + integrity sha512-OaP8hhT8chJNodUPvLJ6vl8gnalcsU/Ww1t9oR3HnGdEWjm/DdCCUXLOral+IPGAeWu/EwgVQCK/QtxALpH1Yw== + dependencies: + "@typescript-eslint/experimental-utils" "^2.5.0" + eslint-rule-composer@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" @@ -1835,6 +2027,14 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-utils@^1.3.1: version "1.4.2" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" @@ -1842,7 +2042,7 @@ eslint-utils@^1.3.1: dependencies: eslint-visitor-keys "^1.0.0" -eslint-visitor-keys@^1.0.0: +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== @@ -1903,10 +2103,10 @@ esprima-fb@3001.1.0-dev-harmony-fb: resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz#b77d37abcd38ea0b77426bb8bc2922ce6b426411" integrity sha1-t303q8046gt3Qmu4vCkizmtCZBE= -esprima@2.7.x, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= esprima@^4.0.0: version "4.0.1" @@ -1932,12 +2132,7 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" - integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -1970,6 +2165,31 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +exec-sh@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" + integrity sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw== + dependencies: + merge "^1.2.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + exorcist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/exorcist/-/exorcist-1.0.1.tgz#79316e3c4885845490f7bb405c0e5b5db1167c52" @@ -2020,6 +2240,18 @@ expect@^1.20.2: object-keys "^1.0.9" tmatch "^2.0.1" +expect@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98" + integrity sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w== + dependencies: + ansi-styles "^3.2.0" + jest-diff "^23.6.0" + jest-get-type "^22.1.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + exposify@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/exposify/-/exposify-0.5.0.tgz#f92d0094c265b3f553e1fa456a03a1883d1059cc" @@ -2106,6 +2338,13 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= + dependencies: + bser "^2.0.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -2125,6 +2364,14 @@ filename-regex@^2.0.0: resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + integrity sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA= + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + fill-range@^2.1.0: version "2.2.4" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" @@ -2151,12 +2398,20 @@ find-parent-dir@~0.3.0: resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ= -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: - locate-path "^3.0.0" + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" flat-cache@^2.0.1: version "2.0.1" @@ -2167,13 +2422,6 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" -flat@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" - integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== - dependencies: - is-buffer "~2.0.3" - flatted@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" @@ -2229,7 +2477,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.0.0, fsevents@^1.2.7: +fsevents@^1.0.0, fsevents@^1.2.3, fsevents@^1.2.7: version "1.2.9" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== @@ -2266,10 +2514,17 @@ get-assigned-identifiers@^1.2.0: resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" @@ -2306,10 +2561,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +glob@^7.0.3, glob@^7.1.1, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2318,17 +2573,6 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^7.1.0, glob@^7.1.2, glob@^7.1.3: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -2365,15 +2609,20 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.4, graceful-fs@^4.1.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +graceful-fs@^4.1.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -handlebars@^4.0.1: - version "4.3.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.3.3.tgz#56dd05fe33d6bd8a7d797351c39a0cdcfd576be5" - integrity sha512-VupOxR91xcGojfINrzMqrvlyYbBs39sXIrWa7YdaQWeBudOlvKEGvCczMfJPgnuwHE/zyH1M6J+IUP6cgDVyxg== +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +handlebars@^4.0.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" + integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -2482,11 +2731,6 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -2504,6 +2748,18 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +hosted-git-info@^2.1.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + htmlescape@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" @@ -2523,7 +2779,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -2555,6 +2811,14 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ== + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -2630,13 +2894,18 @@ insert-module-globals@^7.0.0: undeclared-identifiers "^1.1.2" xtend "^4.0.0" -invariant@^2.2.2: +invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2651,6 +2920,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + is-arrow-function@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-arrow-function/-/is-arrow-function-2.0.3.tgz#29be2c2d8d9450852b8bbafb635ba7b8d8e87ec2" @@ -2675,16 +2949,18 @@ is-buffer@^1.1.0, is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" - integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== - is-callable@^1.0.4, is-callable@^1.1.3, is-callable@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-ci@^1.0.10: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== + dependencies: + ci-info "^1.5.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -2797,6 +3073,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-generator-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-1.0.0.tgz#969d49e1bb3329f6bb7f09089be26578b2ddd46a" + integrity sha1-lp1J4bszKfa7fwkIm+JleLLd1Go= + is-generator-function@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" @@ -2816,7 +3097,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -2876,7 +3157,7 @@ is-regex@^1.0.3, is-regex@^1.0.4: dependencies: has "^1.0.1" -is-stream@~1.1.0: +is-stream@^1.1.0, is-stream@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -2898,11 +3179,21 @@ is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -2935,25 +3226,392 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" - integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs= +istanbul-api@^1.3.1: + version "1.3.7" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa" + integrity sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA== dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.2.1" + istanbul-lib-hook "^1.2.2" + istanbul-lib-instrument "^1.10.2" + istanbul-lib-report "^1.1.5" + istanbul-lib-source-maps "^1.2.6" + istanbul-reports "^1.5.1" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" + integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== + +istanbul-lib-hook@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86" + integrity sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw== + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca" + integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A== + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.1" + semver "^5.3.0" + +istanbul-lib-report@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" + integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw== + dependencies: + istanbul-lib-coverage "^1.2.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.2.4, istanbul-lib-source-maps@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f" + integrity sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg== + dependencies: + debug "^3.1.0" + istanbul-lib-coverage "^1.2.1" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" + integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw== + dependencies: + handlebars "^4.0.3" + +jest-changed-files@^23.4.2: + version "23.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83" + integrity sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA== + dependencies: + throat "^4.0.0" + +jest-cli@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.6.0.tgz#61ab917744338f443ef2baa282ddffdd658a5da4" + integrity sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.1.11" + import-local "^1.0.0" + is-ci "^1.0.10" + istanbul-api "^1.3.1" + istanbul-lib-coverage "^1.2.0" + istanbul-lib-instrument "^1.10.1" + istanbul-lib-source-maps "^1.2.4" + jest-changed-files "^23.4.2" + jest-config "^23.6.0" + jest-environment-jsdom "^23.4.0" + jest-get-type "^22.1.0" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve-dependencies "^23.6.0" + jest-runner "^23.6.0" + jest-runtime "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + jest-watcher "^23.4.0" + jest-worker "^23.2.0" + micromatch "^2.3.11" + node-notifier "^5.2.1" + prompts "^0.1.9" + realpath-native "^1.0.0" + rimraf "^2.5.4" + slash "^1.0.0" + string-length "^2.0.0" + strip-ansi "^4.0.0" + which "^1.2.12" + yargs "^11.0.0" + +jest-config@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d" + integrity sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ== + dependencies: + babel-core "^6.0.0" + babel-jest "^23.6.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^23.4.0" + jest-environment-node "^23.4.0" + jest-get-type "^22.1.0" + jest-jasmine2 "^23.6.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + micromatch "^2.3.11" + pretty-format "^23.6.0" + +jest-diff@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d" + integrity sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g== + dependencies: + chalk "^2.0.1" + diff "^3.2.0" + jest-get-type "^22.1.0" + pretty-format "^23.6.0" + +jest-docblock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7" + integrity sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c= + dependencies: + detect-newline "^2.1.0" + +jest-each@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.6.0.tgz#ba0c3a82a8054387016139c733a05242d3d71575" + integrity sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg== + dependencies: + chalk "^2.0.1" + pretty-format "^23.6.0" + +jest-environment-jsdom@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023" + integrity sha1-BWp5UrP+pROsYqFAosNox52eYCM= + dependencies: + jest-mock "^23.2.0" + jest-util "^23.4.0" + jsdom "^11.5.1" + +jest-environment-node@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10" + integrity sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA= + dependencies: + jest-mock "^23.2.0" + jest-util "^23.4.0" + +jest-get-type@^22.1.0: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== + +jest-haste-map@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16" + integrity sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg== + dependencies: + fb-watchman "^2.0.0" + graceful-fs "^4.1.11" + invariant "^2.2.4" + jest-docblock "^23.2.0" + jest-serializer "^23.0.1" + jest-worker "^23.2.0" + micromatch "^2.3.11" + sane "^2.0.0" + +jest-jasmine2@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz#840e937f848a6c8638df24360ab869cc718592e0" + integrity sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ== + dependencies: + babel-traverse "^6.0.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^23.6.0" + is-generator-fn "^1.0.0" + jest-diff "^23.6.0" + jest-each "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + pretty-format "^23.6.0" + +jest-leak-detector@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz#e4230fd42cf381a1a1971237ad56897de7e171de" + integrity sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg== + dependencies: + pretty-format "^23.6.0" + +jest-matcher-utils@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80" + integrity sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.1.0" + pretty-format "^23.6.0" + +jest-message-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" + integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8= + dependencies: + "@babel/code-frame" "^7.0.0-beta.35" + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + stack-utils "^1.0.1" + +jest-mock@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134" + integrity sha1-rRxg8p6HGdR8JuETgJi20YsmETQ= + +jest-regex-util@^23.3.0: + version "23.3.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5" + integrity sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U= + +jest-resolve-dependencies@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz#b4526af24c8540d9a3fab102c15081cf509b723d" + integrity sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA== + dependencies: + jest-regex-util "^23.3.0" + jest-snapshot "^23.6.0" + +jest-resolve@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae" + integrity sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA== + dependencies: + browser-resolve "^1.11.3" + chalk "^2.0.1" + realpath-native "^1.0.0" + +jest-runner@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.6.0.tgz#3894bd219ffc3f3cb94dc48a4170a2e6f23a5a38" + integrity sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA== + dependencies: + exit "^0.1.2" + graceful-fs "^4.1.11" + jest-config "^23.6.0" + jest-docblock "^23.2.0" + jest-haste-map "^23.6.0" + jest-jasmine2 "^23.6.0" + jest-leak-detector "^23.6.0" + jest-message-util "^23.4.0" + jest-runtime "^23.6.0" + jest-util "^23.4.0" + jest-worker "^23.2.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.6.0.tgz#059e58c8ab445917cd0e0d84ac2ba68de8f23082" + integrity sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw== + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^4.1.6" + chalk "^2.0.1" + convert-source-map "^1.4.0" + exit "^0.1.2" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.11" + jest-config "^23.6.0" + jest-haste-map "^23.6.0" + jest-message-util "^23.4.0" + jest-regex-util "^23.3.0" + jest-resolve "^23.6.0" + jest-snapshot "^23.6.0" + jest-util "^23.4.0" + jest-validate "^23.6.0" + micromatch "^2.3.11" + realpath-native "^1.0.0" + slash "^1.0.0" + strip-bom "3.0.0" + write-file-atomic "^2.1.0" + yargs "^11.0.0" + +jest-serializer@^23.0.1: + version "23.0.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165" + integrity sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU= + +jest-snapshot@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a" + integrity sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg== + dependencies: + babel-types "^6.0.0" + chalk "^2.0.1" + jest-diff "^23.6.0" + jest-matcher-utils "^23.6.0" + jest-message-util "^23.4.0" + jest-resolve "^23.6.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^23.6.0" + semver "^5.5.0" + +jest-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561" + integrity sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE= + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + jest-message-util "^23.4.0" + mkdirp "^0.5.1" + slash "^1.0.0" + source-map "^0.6.0" + +jest-validate@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474" + integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.1.0" + leven "^2.1.0" + pretty-format "^23.6.0" + +jest-watcher@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c" + integrity sha1-0uKM50+NrWxq/JIrksq+9u0FyRw= + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + string-length "^2.0.0" + +jest-worker@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9" + integrity sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk= + dependencies: + merge-stream "^1.0.1" + +jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" + integrity sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw== + dependencies: + import-local "^1.0.0" + jest-cli "^23.6.0" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -2965,7 +3623,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@3.13.1, js-yaml@3.x, js-yaml@^3.13.0: +js-yaml@^3.13.0, js-yaml@^3.7.0: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -3005,6 +3663,38 @@ jsdoc@^3.5.5: taffydb "2.6.2" underscore "~1.9.1" +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -3103,6 +3793,11 @@ klaw@^3.0.0: dependencies: graceful-fs "^4.1.9" +kleur@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300" + integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ== + labeled-stream-splicer@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" @@ -3111,6 +3806,23 @@ labeled-stream-splicer@^2.0.0: inherits "^2.0.1" stream-splicer "^2.0.0" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -3126,12 +3838,23 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: - p-locate "^3.0.0" + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" path-exists "^3.0.0" lodash.memoize@~3.0.3: @@ -3139,28 +3862,26 @@ lodash.memoize@~3.0.3: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= + lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - loglevel@^1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g== -lolex@^1.5.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" - integrity sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY= - loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -3168,6 +3889,20 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -3233,7 +3968,28 @@ mdurl@^1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= -micromatch@^2.1.5: +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +merge@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" + integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== + +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= @@ -3296,6 +4052,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -3306,7 +4067,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -3364,42 +4125,13 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mocha@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" - integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== - dependencies: - ansi-colors "3.2.3" - browser-stdout "1.3.1" - debug "3.2.6" - diff "3.5.0" - escape-string-regexp "1.0.5" - find-up "3.0.0" - glob "7.1.3" - growl "1.10.5" - he "1.2.0" - js-yaml "3.13.1" - log-symbols "2.2.0" - minimatch "3.0.4" - mkdirp "0.5.1" - ms "2.1.1" - node-environment-flags "1.0.5" - object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" - wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" - yargs-unparser "1.6.0" - module-deps@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.1.tgz#cfe558784060e926824f474b4e647287837cda50" @@ -3441,11 +4173,6 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -3502,13 +4229,21 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-notifier@^5.2.1: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" node-pre-gyp@^0.12.0: version "0.12.0" @@ -3526,13 +4261,6 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -nopt@3.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -3541,6 +4269,16 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -3566,6 +4304,13 @@ npm-packlist@^1.1.6: ignore-walk "^3.0.1" npm-bundled "^1.0.1" +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -3581,6 +4326,11 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +nwsapi@^2.0.7: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -3605,7 +4355,7 @@ object-inspect@^1.1.0, object-inspect@^1.6.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -3622,16 +4372,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - object.entries@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" @@ -3669,7 +4409,7 @@ object.pick@^1.3.0: version "3.1.4" resolved "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz#0f03128b7d3b2f614d2216409a1dfccca765fdb3" -once@1.x, once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -3713,6 +4453,15 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -3742,24 +4491,39 @@ output-file-sync@^1.1.2: mkdirp "^0.5.1" object-assign "^4.1.0" -p-limit@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - dependencies: - p-try "^2.0.0" +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= pako@~1.0.5: version "1.0.10" @@ -3802,6 +4566,18 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -3822,6 +4598,13 @@ path-dirname@^1.0.0: resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -3837,12 +4620,12 @@ path-is-inside@^1.0.2: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-key@^2.0.1: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.6: +path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -3852,6 +4635,15 @@ path-platform@~0.11.15: resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -3868,6 +4660,35 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -3883,6 +4704,14 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +pretty-format@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" + integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -3903,7 +4732,15 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -psl@^1.1.24: +prompts@^0.1.9: + version "0.1.14" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2" + integrity sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w== + dependencies: + kleur "^2.0.1" + sisteransi "^0.1.1" + +psl@^1.1.24, psl@^1.1.28: version "1.4.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== @@ -3920,6 +4757,14 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3930,7 +4775,7 @@ punycode@^1.3.2, punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -3996,7 +4841,24 @@ read-only-stream@^2.0.0: dependencies: readable-stream "^2.0.2" -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -4047,6 +4909,13 @@ readdirp@^2.0.0, readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +realpath-native@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -4151,7 +5020,23 @@ replace-requires@~1.0.3: patch-text "~1.0.2" xtend "~4.0.0" -request@^2.88.0: +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + dependencies: + lodash "^4.17.15" + +request-promise-native@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + dependencies: + request-promise-core "1.1.3" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -4182,10 +5067,10 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= requizzle@^0.2.3: version "0.2.3" @@ -4194,6 +5079,18 @@ requizzle@^0.2.3: dependencies: lodash "^4.17.14" +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -4204,12 +5101,12 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7, resolve@1.1.x: +resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.4, resolve@^1.12.0, resolve@^1.4.0: +resolve@^1.1.4, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.4.0: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== @@ -4241,7 +5138,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.6.1: +rimraf@^2.5.4, rimraf@^2.6.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -4263,6 +5160,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rsvp@^3.3.3: + version "3.6.2" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" + integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw== + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -4299,16 +5201,37 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sane@^2.0.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa" + integrity sha1-tNwYYcIbQn6SlQej51HiosuKs/o= + dependencies: + anymatch "^2.0.0" + capture-exit "^1.2.0" + exec-sh "^0.2.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.18.0" + optionalDependencies: + fsevents "^1.2.3" + sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -4357,6 +5280,11 @@ shell-quote@^1.4.2, shell-quote@^1.6.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -4367,6 +5295,11 @@ simple-concat@^1.0.0: resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= +sisteransi@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" + integrity sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g== + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -4437,12 +5370,20 @@ source-map-support@^0.5.13, source-map-support@~0.5.12: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.5.6: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -4459,13 +5400,6 @@ source-map@~0.1.30: dependencies: amdefine ">=0.0.4" -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= - dependencies: - amdefine ">=0.0.4" - sourceify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sourceify/-/sourceify-1.0.0.tgz#428da9a97fcbac6c250e03e58ea7aeb980c9e242" @@ -4474,6 +5408,32 @@ sourceify@^1.0.0: convert-source-map "^1.1.3" through2 "^2.0.0" +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -4501,6 +5461,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -4509,6 +5474,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + stream-browserify@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -4543,6 +5513,14 @@ stream-splicer@^2.0.0: inherits "^2.0.1" readable-stream "^2.0.2" +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4552,7 +5530,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -4560,7 +5538,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^3.0.0, string-width@^3.1.0: +string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== @@ -4618,14 +5596,31 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: +strip-ansi@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" -strip-json-comments@2.0.1, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-bom@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -4642,19 +5637,12 @@ subarg@^1.0.0: dependencies: minimist "^1.1.0" -supports-color@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" - integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== - dependencies: - has-flag "^3.0.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.0: +supports-color@^3.1.2: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= @@ -4668,6 +5656,11 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + syntax-error@^1.1.1: version "1.4.0" resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" @@ -4717,11 +5710,27 @@ terser@^4.3.8: source-map "~0.6.1" source-map-support "~0.5.12" +test-exclude@^4.2.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20" + integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA== + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -4767,6 +5776,11 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" @@ -4802,6 +5816,14 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +tough-cookie@^2.3.3, tough-cookie@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -4810,6 +5832,13 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + transformify@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/transformify/-/transformify-0.1.2.tgz#9a4f42a154433dd727b80575428a3c9e5489ebf1" @@ -4822,11 +5851,18 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" @@ -4953,6 +5989,14 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -4979,6 +6023,14 @@ v8flags@^2.1.1: dependencies: user-home "^1.1.1" +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -4993,6 +6045,28 @@ vm-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +watch@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" + integrity sha1-KAlUdsbffJDJYxOJkMClQj60uYY= + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + watchify@^3.11.1: version "3.11.1" resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.11.1.tgz#8e4665871fff1ef64c0430d1a2c9d084d9721881" @@ -5006,49 +6080,92 @@ watchify@^3.11.1: through2 "^2.0.0" xtend "^4.0.0" +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1.3.1, which@^1.1.1, which@^1.2.9: +which@^1.2.12, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -wide-align@1.1.3, wide-align@^1.1.0: +wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -wordwrap@^1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^2.1.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" @@ -5056,6 +6173,18 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + xmlcreate@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.1.tgz#2ec38bd7b708d213fd1a90e2431c4af9c09f6a52" @@ -5073,45 +6202,37 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= yallist@^3.0.0, yallist@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.0.tgz#906cc2100972dc2625ae78f566a2577230a1d6f7" integrity sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w== -yargs-parser@13.1.1, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" + camelcase "^4.1.0" -yargs-unparser@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" - integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== +yargs@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" + integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== dependencies: - flat "^4.1.0" - lodash "^4.17.15" - yargs "^13.3.0" - -yargs@13.3.0, yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^3.1.0" require-directory "^2.1.1" - require-main-filename "^2.0.0" + require-main-filename "^1.0.1" set-blocking "^2.0.0" - string-width "^3.0.0" + string-width "^2.0.0" which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" + y18n "^3.2.1" + yargs-parser "^9.0.2"