From 537062dca4ce8ecaa266318002788b750bc3dfea Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 1 Feb 2021 11:51:53 -0500 Subject: [PATCH 01/30] Add end parameter to PushQueue.from & pipeline. Prevents premature closing of pipeline. --- src/utils/PushQueue.js | 10 ++++++---- src/utils/iterators.js | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/utils/PushQueue.js b/src/utils/PushQueue.js index c2e11fcb4..391ed930d 100644 --- a/src/utils/PushQueue.js +++ b/src/utils/PushQueue.js @@ -72,9 +72,9 @@ export default class PushQueue { this.iterator = this.iterate() } - static from(iterable, opts) { + static from(iterable, { end, ...opts } = {}) { const queue = new PushQueue([], opts) - queue.from(iterable) + queue.from(iterable, { end }) return queue } @@ -101,12 +101,14 @@ export default class PushQueue { return buffer } - async from(iterable) { + async from(iterable, { end = true } = {}) { try { for await (const item of iterable) { this.push(item) } - this.end() + if (end) { + this.end() + } } catch (err) { await this.throw(err) } diff --git a/src/utils/iterators.js b/src/utils/iterators.js index 9ec70dd70..cbe206dea 100644 --- a/src/utils/iterators.js +++ b/src/utils/iterators.js @@ -292,7 +292,7 @@ const isPipeline = Symbol('isPipeline') const getIsStream = (item) => typeof item.pipe === 'function' -export function pipeline(iterables = [], onFinally = () => {}, opts = {}) { +export function pipeline(iterables = [], onFinally = () => {}, { end, ...opts } = {}) { const cancelFns = new Set() let cancelled = false let error @@ -371,7 +371,7 @@ export function pipeline(iterables = [], onFinally = () => {}, opts = {}) { } if (prev && nextStream) { - prev = getIsStream(prev) ? prev : PushQueue.from(prev) + prev = getIsStream(prev) ? prev : PushQueue.from(prev, { end }) prev.id = prev.id || 'inter-' + nextStream.id prev.pipe(nextStream) From 1d95da2c7ec0f6bd5ca671d8ccb3a4e9cc8e9516 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 1 Feb 2021 13:04:08 -0500 Subject: [PATCH 02/30] Add tests for PushQueue.from end parameter. PushQueue.from should buffer sync iterable immediately. --- src/utils/PushQueue.js | 27 ++++-- test/unit/PushQueue.test.js | 170 +++++++++++++++++++++++++++++++++++- 2 files changed, 190 insertions(+), 7 deletions(-) diff --git a/src/utils/PushQueue.js b/src/utils/PushQueue.js index 391ed930d..4db8a6616 100644 --- a/src/utils/PushQueue.js +++ b/src/utils/PushQueue.js @@ -103,15 +103,26 @@ export default class PushQueue { async from(iterable, { end = true } = {}) { try { - for await (const item of iterable) { - this.push(item) - } - if (end) { - this.end() + // detect sync/async iterable and iterate appropriately + if (!iterable[Symbol.asyncIterator]) { + // sync iterables push into buffer immediately + for (const item of iterable) { + this.push(item) + } + } else { + for await (const item of iterable) { + this.push(item) + } } } catch (err) { - await this.throw(err) + return this.throw(err) + } + + if (end) { + this.end() } + + return Promise.resolve() } onEnd(...args) { @@ -128,6 +139,10 @@ export default class PushQueue { */ end(v) { + if (this.ended) { + return + } + if (v != null) { this.push(v) } diff --git a/test/unit/PushQueue.test.js b/test/unit/PushQueue.test.js index 045d008a2..1404a13c3 100644 --- a/test/unit/PushQueue.test.js +++ b/test/unit/PushQueue.test.js @@ -73,8 +73,9 @@ describe('PushQueue', () => { }) describe('from', () => { - it('supports iterable', async () => { + it('supports sync iterable', async () => { const q = PushQueue.from(expected) + expect(q.length).toBe(expected.length) const msgs = [] // will end when source ends @@ -94,6 +95,7 @@ describe('PushQueue', () => { it('supports async iterable', async () => { const q = PushQueue.from(generate()) + expect(q.length).toBe(0) // can't tell length upfront with async iterable const msgs = [] // will end when source ends @@ -111,7 +113,119 @@ describe('PushQueue', () => { expect(q.length).toBe(0) }) + it('errors if source errors', async () => { + expect.assertions(5) + const err = new Error('expected') + const q = PushQueue.from(async function* GenerateError() { + yield* generate() + throw err + }()) + expect(q.length).toBe(0) // can't tell length upfront with async iterable + + const msgs = [] + await expect(async () => { + for await (const msg of q) { + msgs.push(msg) + } + }).rejects.toThrow('expected') + + expect(msgs).toEqual(expected) + + // buffer should have drained at end + expect(q.length).toBe(0) + + // these should have no effect + q.push('c') + expect(q.length).toBe(0) + }) + + it('errors if source errors immediately', async () => { + expect.assertions(5) + const err = new Error('expected') + // eslint-disable-next-line require-yield + const q = PushQueue.from(async function* GenerateError() { + throw err + }()) + + expect(q.length).toBe(0) + + const msgs = [] + await expect(async () => { + for await (const msg of q) { + msgs.push(msg) + } + }).rejects.toThrow('expected') + + expect(msgs).toEqual([]) + + // buffer should have drained at end + expect(q.length).toBe(0) + + // these should have no effect + q.push('c') + expect(q.length).toBe(0) + }) + + it('errors if sync source errors immediately', async () => { + expect.assertions(5) + const err = new Error('expected') + // eslint-disable-next-line require-yield + const q = PushQueue.from(function* GenerateError() { + throw err + }()) + + expect(q.length).toBe(0) + + const msgs = [] + await expect(async () => { + for await (const msg of q) { + msgs.push(msg) + } + }).rejects.toThrow('expected') + + expect(msgs).toEqual([]) + + // buffer should have drained at end + expect(q.length).toBe(0) + + // these should have no effect + q.push('c') + expect(q.length).toBe(0) + }) + + it('can require manually ending', async () => { + expect.assertions(6) + const q = PushQueue.from(generate(), { + end: false, + }) + + const msgs = [] + const callEnd = jest.fn(() => { + let error + try { + expect(q.isWritable()).toEqual(true) + expect(q.isReadable()).toEqual(true) + } catch (err) { + error = err + } + q.end(error) + }) + // will NOT end when source ends + for await (const msg of q) { + msgs.push(msg) + if (msgs.length === expected.length) { + setTimeout(callEnd, 100) + } + } + + expect(callEnd).toHaveBeenCalledTimes(1) + expect(msgs).toEqual(expected) + expect(q.isWritable()).toEqual(false) + expect(q.isReadable()).toEqual(false) + }) + it('can be aborted while waiting', async () => { + expect.assertions(3) const startedWaiting = jest.fn() const ac = new AbortController() let q @@ -259,6 +373,23 @@ describe('PushQueue', () => { expect(q.length).toBe(0) }) + it('ignores end after end', async () => { + const q = new PushQueue() // wouldn't end on its own + q.push(...expected) + q.end() + const err = new Error('expected error') + q.end(err) + + const msgs = [] + for await (const msg of q) { + msgs.push(msg) + } + + expect(msgs).toEqual(expected) + + expect(q.length).toBe(0) + }) + it('works with pending next', async () => { const q = new PushQueue() q.push(expected[0]) // preload first item @@ -282,6 +413,25 @@ describe('PushQueue', () => { // buffer should have drained at end expect(q.length).toBe(0) }) + + it('works with end in loop', async () => { + const q = new PushQueue(expected) + const msgs = [] + for await (const msg of q) { + msgs.push(msg) // gets rest of messages + if (msgs.length === 2) { + q.end() + } + } + + expect(msgs).toEqual(expected) + + // buffer should have drained at end + expect(q.length).toBe(0) + + // buffer should have drained at end + expect(q.length).toBe(0) + }) }) describe('onEnd', () => { @@ -356,6 +506,24 @@ describe('PushQueue', () => { expect(msgs).toEqual(expected.slice(0, 1)) expect(onEnd).toHaveBeenCalledTimes(1) }) + + it('fires only after all items consumed and source is closed', async () => { + const onEnd = jest.fn() + const q = PushQueue.from(expected, { + onEnd, + }) + + expect(q.length).toBe(expected.length) + expect(onEnd).toHaveBeenCalledTimes(0) + const msgs = [] + for await (const msg of q) { + msgs.push(msg) + await wait(0) + expect(onEnd).toHaveBeenCalledTimes(0) + } + expect(msgs).toEqual(expected) + expect(onEnd).toHaveBeenCalledTimes(1) + }) }) it('reduces length as items are consumed', async () => { From e5857ffaeb23030fdb0d8babd55cf3062ad0ea76 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 1 Feb 2021 14:10:22 -0500 Subject: [PATCH 03/30] Add autoEnd option to PushQueue. --- src/utils/PushQueue.js | 17 ++-- src/utils/iterators.js | 17 +--- test/unit/PushQueue.test.js | 36 +++++++- test/unit/iterators.test.js | 171 ++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 22 deletions(-) diff --git a/src/utils/PushQueue.js b/src/utils/PushQueue.js index 4db8a6616..e8350dd2d 100644 --- a/src/utils/PushQueue.js +++ b/src/utils/PushQueue.js @@ -42,7 +42,8 @@ export class AbortError extends Error { */ export default class PushQueue { - constructor(items = [], { signal, onEnd, timeout = 0 } = {}) { + constructor(items = [], { signal, onEnd, timeout = 0, autoEnd = true } = {}) { + this.autoEnd = autoEnd this.buffer = [...items] this.finished = false this.error = null // queued error @@ -72,9 +73,9 @@ export default class PushQueue { this.iterator = this.iterate() } - static from(iterable, { end, ...opts } = {}) { + static from(iterable, opts = {}) { const queue = new PushQueue([], opts) - queue.from(iterable, { end }) + queue.from(iterable) return queue } @@ -93,7 +94,9 @@ export default class PushQueue { tasks.push(task) } await Promise.all(tasks) - return buffer.end() + if (src.autoEnd) { + await buffer.end() + } })().catch((err) => { return buffer.throw(err) }) // no await @@ -101,7 +104,7 @@ export default class PushQueue { return buffer } - async from(iterable, { end = true } = {}) { + async from(iterable, { end = this.autoEnd } = {}) { try { // detect sync/async iterable and iterate appropriately if (!iterable[Symbol.asyncIterator]) { @@ -309,8 +312,8 @@ export default class PushQueue { }) } - pipe(next) { - return next.from(this) + pipe(next, opts) { + return next.from(this, opts) } async cancel(...args) { diff --git a/src/utils/iterators.js b/src/utils/iterators.js index cbe206dea..bfb70ffba 100644 --- a/src/utils/iterators.js +++ b/src/utils/iterators.js @@ -1,7 +1,5 @@ import pMemoize from 'p-memoize' -import PushQueue from './PushQueue' // eslint-disable-line import/no-cycle - import { Defer, pTimeout, allSettledValues, AggregatedError } from './index' /** @@ -290,7 +288,7 @@ export function CancelableGenerator(iterable, onFinally = () => {}, { timeout = const isPipeline = Symbol('isPipeline') -const getIsStream = (item) => typeof item.pipe === 'function' +const getIsStream = (item) => typeof item.from === 'function' export function pipeline(iterables = [], onFinally = () => {}, { end, ...opts } = {}) { const cancelFns = new Set() @@ -356,7 +354,6 @@ export function pipeline(iterables = [], onFinally = () => {}, { end, ...opts } prev = index === 0 ? firstSrc : _prev // take first "prev" from outer iterator, if one exists nextIterable = typeof next === 'function' ? next(prev) : next - let nextStream if (prev && nextIterable[isPipeline]) { nextIterable.setFirstSource(prev) @@ -366,15 +363,9 @@ export function pipeline(iterables = [], onFinally = () => {}, { end, ...opts } cancelFns.add(nextIterable) } - if (nextIterable && getIsStream(nextIterable)) { - nextStream = nextIterable - } - - if (prev && nextStream) { - prev = getIsStream(prev) ? prev : PushQueue.from(prev, { end }) - - prev.id = prev.id || 'inter-' + nextStream.id - prev.pipe(nextStream) + if (prev && nextIterable && getIsStream(nextIterable)) { + prev.id = prev.id || 'inter-' + nextIterable.id + nextIterable.from(prev, { end }) } yield* nextIterable diff --git a/test/unit/PushQueue.test.js b/test/unit/PushQueue.test.js index 1404a13c3..9245649de 100644 --- a/test/unit/PushQueue.test.js +++ b/test/unit/PushQueue.test.js @@ -193,9 +193,10 @@ describe('PushQueue', () => { expect(q.length).toBe(0) }) - it('can require manually ending', async () => { + it('can require manually ending with instance.from end: false', async () => { expect.assertions(6) - const q = PushQueue.from(generate(), { + const q = new PushQueue() + q.from(generate(), { end: false, }) @@ -224,6 +225,37 @@ describe('PushQueue', () => { expect(q.isReadable()).toEqual(false) }) + it('can require manually ending with autoEnd: false', async () => { + expect.assertions(6) + const q = PushQueue.from(generate(), { + autoEnd: false, + }) + + const msgs = [] + const callEnd = jest.fn(() => { + let error + try { + expect(q.isWritable()).toEqual(true) + expect(q.isReadable()).toEqual(true) + } catch (err) { + error = err + } + q.end(error) + }) + // will NOT end when source ends + for await (const msg of q) { + msgs.push(msg) + if (msgs.length === expected.length) { + setTimeout(callEnd, 100) + } + } + + expect(callEnd).toHaveBeenCalledTimes(1) + expect(msgs).toEqual(expected) + expect(q.isWritable()).toEqual(false) + expect(q.isReadable()).toEqual(false) + }) + it('can be aborted while waiting', async () => { expect.assertions(3) const startedWaiting = jest.fn() diff --git a/test/unit/iterators.test.js b/test/unit/iterators.test.js index 116e88f6d..268541443 100644 --- a/test/unit/iterators.test.js +++ b/test/unit/iterators.test.js @@ -1247,6 +1247,177 @@ describe('Iterator Utils', () => { expect(onInputStreamClose).toHaveBeenCalledTimes(1) }) + it('can have delayed yields', async () => { + const receivedStep1 = [] + const receivedStep2 = [] + const onThroughStreamClose = jest.fn() + const throughStream = PushQueue.from(generate(), { + onEnd: onThroughStreamClose, + }) + + const p = pipeline([ + throughStream, + async function* Step1(s) { + for await (const msg of s) { + receivedStep1.push(msg) + yield msg + } + await wait(10) + for (const msg of receivedStep1) { + yield msg * 2 + } + }, + async function* Step2(s) { + for await (const msg of s) { + receivedStep2.push(msg) + yield msg + } + }, + ], onFinally, { + timeout: WAIT, + }) + + const received = [] + for await (const msg of p) { + received.push(msg) + expect(onFinally).toHaveBeenCalledTimes(0) + } + + expect(received).toEqual([...expected, ...expected.map((v) => v * 2)]) + expect(receivedStep1).toEqual(expected) + + // all streams were closed + expect(onThroughStreamClose).toHaveBeenCalledTimes(1) + }) + + it('does not end internal non-autoEnd streams automatically', async () => { + const receivedStep1 = [] + const receivedStep2 = [] + const receivedStep3 = [] + const onThroughStreamClose = jest.fn() + const afterLoop1 = jest.fn() + const throughStream = new PushQueue([], { + autoEnd: false, + onEnd: onThroughStreamClose, + }) + + const p = pipeline([ + generate(), + // eslint-disable-next-line require-yield + async function* Step1(s) { + for await (const msg of s) { + receivedStep1.push(msg) + setTimeout(() => { + throughStream.push(msg) + }, 100) + } + afterLoop1() + }, + throughStream, + async function* Step2(s) { + for await (const msg of s) { + expect(afterLoop1).toHaveBeenCalledTimes(1) + receivedStep2.push(msg) + yield msg + if (receivedStep2.length === expected.length) { + // this should end pipeline + break + } + } + }, + async function* Step3(s) { + for await (const msg of s) { + receivedStep3.push(msg) + yield msg + // should close automatically + } + }, + ], onFinally, { + timeout: WAIT, + }) + + const received = [] + for await (const msg of p) { + received.push(msg) + expect(onFinally).toHaveBeenCalledTimes(0) + } + expect(received).toEqual(expected) + expect(receivedStep1).toEqual(expected) + expect(receivedStep2).toEqual(expected) + expect(receivedStep3).toEqual(expected) + + // all streams were closed + expect(onThroughStreamClose).toHaveBeenCalledTimes(1) + expect(afterLoop1).toHaveBeenCalledTimes(1) + }) + + it('does not end internal streams automatically if going through non-autoEnd: false PushQueue', async () => { + const receivedStep1 = [] + const receivedStep2 = [] + const receivedStep3 = [] + const onThroughStreamClose = jest.fn() + const onThroughStream2Close = jest.fn() + const afterLoop1 = jest.fn() + const throughStream = new PushQueue([], { + autoEnd: false, + onEnd: onThroughStreamClose, + }) + const throughStream2 = new PushQueue([], { + autoEnd: true, + onEnd: onThroughStream2Close, + }) + + const p = pipeline([ + generate(), + // eslint-disable-next-line require-yield + async function* Step1(s) { + for await (const msg of s) { + receivedStep1.push(msg) + setTimeout(() => { + throughStream.push(msg) + }, 100) + } + afterLoop1() + }, + throughStream, + async function* Step2(s) { + for await (const msg of s) { + expect(afterLoop1).toHaveBeenCalledTimes(1) + receivedStep2.push(msg) + yield msg + if (receivedStep2.length === expected.length) { + // end pipeline + break + } + } + }, + throughStream2, + async function* Step3(s) { + for await (const msg of s) { + receivedStep3.push(msg) + yield msg + // should close automatically + } + }, + ], onFinally, { + timeout: WAIT, + }) + + const received = [] + for await (const msg of p) { + received.push(msg) + expect(onFinally).toHaveBeenCalledTimes(0) + } + expect(received).toEqual(expected) + expect(receivedStep1).toEqual(expected) + expect(receivedStep2).toEqual(expected) + expect(receivedStep3).toEqual(expected) + + // all streams were closed + expect(onThroughStreamClose).toHaveBeenCalledTimes(1) + expect(onThroughStream2Close).toHaveBeenCalledTimes(1) + }) + it('works with nested pipelines', async () => { const onFinallyInnerAfter = jest.fn() const onFinallyInner = jest.fn(async () => { From 468eb5574085fbe7decd76615f105c417b98dbdb Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 1 Feb 2021 15:11:35 -0500 Subject: [PATCH 04/30] Use streamr-client-protocol@8.0.0-beta.1 --- package-lock.json | 45 +++++++++------------------------------------ package.json | 2 +- webpack.config.js | 2 +- 3 files changed, 11 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 59d71601e..81956a426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1207,15 +1207,6 @@ "regenerator-runtime": "^0.13.4" } }, - "@babel/runtime-corejs3": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", - "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", - "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - } - }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", @@ -3987,7 +3978,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -4662,7 +4652,8 @@ "core-js": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", - "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==" + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==", + "dev": true }, "core-js-compat": { "version": "3.8.2", @@ -4682,11 +4673,6 @@ } } }, - "core-js-pure": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.2.tgz", - "integrity": "sha512-v6zfIQqL/pzTVAbZvYUozsxNfxcFb6Ks3ZfEbuneJl3FW9Jb8F6vLWB6f+qTmAu72msUdyb84V8d/yBFf7FNnw==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -12581,22 +12567,11 @@ } }, "sha3": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.3.tgz", - "integrity": "sha512-Io53D4o9qOmf3Ow9p/DoGLQiQHhtuR0ulbyambvRSG+OX5yXExk2yYfvjHtb7AtOyk6K6+sPeK/qaowWc/E/GA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", "requires": { - "buffer": "5.6.0" - }, - "dependencies": { - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - } + "buffer": "6.0.3" } }, "shallow-clone": { @@ -13176,12 +13151,10 @@ "dev": true }, "streamr-client-protocol": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/streamr-client-protocol/-/streamr-client-protocol-7.1.2.tgz", - "integrity": "sha512-JibsGEEGaqKHKhTSfxuy41bARCQQEAVIeCUvEHry9aJk03QrM/0/d2LdT7d02Naxv2HX1RF1fQnJ2FxAMA46Yg==", + "version": "8.0.0-beta.1", + "resolved": "https://registry.npmjs.org/streamr-client-protocol/-/streamr-client-protocol-8.0.0-beta.1.tgz", + "integrity": "sha512-sxkn5ef/sqCo6hC/wy2f81hy7ONR/8aYW+H6TlQ2tltwT/q983QBmGtOW2Y4mT0I2QX10f6NDUIlfluemydsyA==", "requires": { - "@babel/runtime-corejs3": "^7.12.5", - "core-js": "^3.8.1", "debug": "^4.3.1", "ethers": "^5.0.24", "eventemitter3": "^4.0.7", diff --git a/package.json b/package.json index c86672f1a..408b53492 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "qs": "^6.9.6", "quick-lru": "^5.1.1", "readable-stream": "^3.6.0", - "streamr-client-protocol": "^7.1.2", + "streamr-client-protocol": "^8.0.0-beta.1", "typescript": "^4.1.4", "uuid": "^8.3.2", "webpack-node-externals": "^2.5.2", diff --git a/webpack.config.js b/webpack.config.js index 52b22578e..076d340ce 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -125,7 +125,7 @@ module.exports = (env, argv) => { buffer: path.resolve(__dirname, 'node_modules', 'buffer'), 'node-fetch': path.resolve(__dirname, './src/shim/node-fetch.js'), 'node-webcrypto-ossl': path.resolve(__dirname, 'src/shim/crypto.js'), - 'streamr-client-protocol': path.resolve(__dirname, 'node_modules/streamr-client-protocol/src'), + 'streamr-client-protocol': path.resolve(__dirname, 'node_modules/streamr-client-protocol/dist/src'), } }, plugins: [ From 08f928026aa8d3c2829db2d2276f7ec0d213bd2c Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 1 Feb 2021 16:09:31 -0500 Subject: [PATCH 05/30] Fix decryption transform. --- src/utils/PushQueue.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/PushQueue.js b/src/utils/PushQueue.js index e8350dd2d..9911cae81 100644 --- a/src/utils/PushQueue.js +++ b/src/utils/PushQueue.js @@ -79,8 +79,8 @@ export default class PushQueue { return queue } - static transform(src, fn) { - const buffer = new PushQueue() + static transform(src, fn, opts = {}) { + const buffer = new PushQueue([], opts) const orderedFn = pOrderedResolve(fn) // push must be run in sequence ;(async () => { // eslint-disable-line semi-style const tasks = [] @@ -94,8 +94,8 @@ export default class PushQueue { tasks.push(task) } await Promise.all(tasks) - if (src.autoEnd) { - await buffer.end() + if (buffer.autoEnd) { + buffer.end() } })().catch((err) => { return buffer.throw(err) From 9f13e02b8ca130e09ea84c5416faa544b41c4d34 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Mon, 8 Feb 2021 11:28:44 -0500 Subject: [PATCH 06/30] Add resend gapfill test. WIP. --- src/subscribe/OrderMessages.js | 27 +++++++++++++++++++++++- src/subscribe/messageStream.js | 2 +- test/integration/GapFill.test.js | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index fb141e809..05b091e83 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -1,6 +1,7 @@ import { Utils } from 'streamr-client-protocol' import { pipeline } from '../utils/iterators' +import { Defer } from '../utils' import PushQueue from '../utils/PushQueue' import { validateOptions } from '../stream/utils' @@ -17,9 +18,20 @@ export default function OrderMessages(client, options = {}) { const { gapFillTimeout, retryResendAfter, gapFill = true } = client.options const { streamId, streamPartition } = validateOptions(options) - const outStream = new PushQueue() // output buffer + // output buffer + const outStream = new PushQueue([], { + // we can end when: + // input has closed (i.e. all messages sent) + // AND + // no gaps are pending + // AND + // gaps have been filled or failed + autoEnd: false, + }) let done = false + const inputDone = Defer() + const allHandled = Defer() const resendStreams = new Set() // holds outstanding resends for cleanup const orderingUtil = new OrderingUtil(streamId, streamPartition, (orderedMessage) => { @@ -71,6 +83,17 @@ export default function OrderMessages(client, options = {}) { } }, outStream, // consumer gets outStream + async function* IsDone(src) { + for await (const msg of src) { + if (!gapFill) { + orderingUtil.markMessageExplicitly(msg) + } + + orderingUtil.add(msg) + // note no yield + // orderingUtil writes to outStream itself + } + }, ], async (err) => { done = true orderingUtil.clearGaps() @@ -78,6 +101,8 @@ export default function OrderMessages(client, options = {}) { resendStreams.clear() await outStream.cancel(err) orderingUtil.clearGaps() + }, { + end: false, }), { markMessageExplicitly, }) diff --git a/src/subscribe/messageStream.js b/src/subscribe/messageStream.js index 36b90f355..85dbcb082 100644 --- a/src/subscribe/messageStream.js +++ b/src/subscribe/messageStream.js @@ -19,7 +19,7 @@ function getIsMatchingStreamMessage({ streamId, streamPartition = 0 }) { * Returns a PushQueue that will fill with messages. */ -export default function messageStream(connection, { streamId, streamPartition, isUnicast, type }, onFinally = () => {}) { +export default function messageStream(connection, { streamId, streamPartition, isUnicast, type }, onFinally = async () => {}) { if (!type) { // eslint-disable-next-line no-param-reassign type = isUnicast ? ControlMessage.TYPES.UnicastMessage : ControlMessage.TYPES.BroadcastMessage diff --git a/test/integration/GapFill.test.js b/test/integration/GapFill.test.js index af2b7e7ec..8a7c90980 100644 --- a/test/integration/GapFill.test.js +++ b/test/integration/GapFill.test.js @@ -202,6 +202,41 @@ describeRepeats('GapFill', () => { expect(client.connection.getState()).toBe('connected') }, 15000) + it('can fill gaps in resends', async () => { + const { parse } = client.connection + let count = 0 + client.connection.parse = (...args) => { + const msg = parse.call(client.connection, ...args) + if (!msg.streamMessage) { + return msg + } + + count += 1 + if (count === 3 || count === 4 || count === 7) { + return null + } + + return msg + } + + const published = await publishTestMessages(MAX_MESSAGES, { + timestamp: 111111, + waitForLast: true, + }) + + const sub = await client.resend({ + stream, + last: MAX_MESSAGES, + }) + const received = [] + for await (const m of sub) { + received.push(m.getParsedContent()) + // should not need to explicitly end + } + expect(received).toEqual(published) + expect(client.connection.getState()).toBe('connected') + }, 60000) + it('can fill gaps between resend and realtime', async () => { // publish 5 messages into storage const published = await publishTestMessages(5, { From d334d58f53a948cae48c077db5db15efb120f4de Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Wed, 10 Feb 2021 13:57:34 -0500 Subject: [PATCH 07/30] Add runtime-corejs3 to dependencies, update babel runtime deps. --- package-lock.json | 1386 ++++++++++++++++++++++----------------------- package.json | 5 +- 2 files changed, 691 insertions(+), 700 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81956a426..4005cb40c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.12.10.tgz", - "integrity": "sha512-+y4ZnePpvWs1fc/LhZRTHkTesbXkyBYuOB+5CyodZqrEuETXi3zOVfpAQIdgC3lXbHLTDG9dQosxR9BhvLKDLQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.12.13.tgz", + "integrity": "sha512-Zto3HPeE0GRmaxobUl7NvFTo97NKe1zdAuWqTO8oka7nE0IIqZ4CFvuRZe1qf+ZMd7eHMhwqrecjwc10mjXo/g==", "dev": true, "requires": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", @@ -23,34 +23,34 @@ } }, "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.13.tgz", + "integrity": "sha512-U/hshG5R+SIoW7HVWIdmy1cB7s3ki+r3FpyEZiCgpi4tFgPnX/vynY80ZGSASOIrUM6O7VxOgCZgdt7h97bUGg==", "dev": true }, "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", + "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helpers": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -61,199 +61,188 @@ } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", - "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.13.tgz", + "integrity": "sha512-dXof20y/6wB5HnLOGyLh/gobsMvDNoekcC+8MCV2iaTd5JemhFkPD73QB+tK3iFC9P0xJC73B6MvKkyUfS9cCw==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", + "@babel/compat-data": "^7.12.13", + "@babel/helper-validator-option": "^7.12.11", "browserslist": "^4.14.5", "semver": "^5.5.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.13.tgz", + "integrity": "sha512-Vs/e9wv7rakKYeywsmEBSRC9KtmE7Px+YBlESekLeJOF0zbGUicGfXSNi3o+tfXSNS48U/7K9mIOOCR79Cl3+Q==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.13.tgz", + "integrity": "sha512-XC+kiA0J3at6E85dL5UnCYfVOcIZ834QcAY0TIpgUVnz0zDzg+0TtvZTnJ4g9L1dPRGe30Qi03XCIS4tYCLtqw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", "regexpu-core": "^4.7.1" } }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz", + "integrity": "sha512-5loeRNvMo9mx1dA/d6yNi+YiKziJZFylZnCo1nmFF4qPU4yJ14abhWESuSMQSlQxWdxdOFzxXjk/PpfudTtYyw==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.12.13.tgz", + "integrity": "sha512-KSC5XSj5HreRhYQtZ3cnSnQwDzgnbdUDEFsxkN0m6Q3WrCRt72xrnZ8+h+pX7YxM7hr87zIO3a/v5p/H3TrnVw==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", + "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.13.tgz", + "integrity": "sha512-Qa6PU9vNcj1NZacZZI1Mvwt+gXDH6CTfgAkSjeRMLE8HxtDK76+YDId6NQR+z7Rgd5arhD2cIbS74r0SxD6PDA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-skip-transparent-expression-wrappers": { @@ -266,12 +255,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -287,64 +276,64 @@ "dev": true }, "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.13.tgz", + "integrity": "sha512-t0aZFEmBJ1LojdtJnhOaQEVejnzYhyjWHSsNSNo8vOYRbAJNh6r6GQF7pd36SqG7OKGbn+AewVQ/0IfYfIuGdw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", + "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.13.tgz", + "integrity": "sha512-1KH46Hx4WqP77f978+5Ye/VUbuwQld2hph70yaw2hXS2v7ER2f3nlpNMu909HO2rbvP0NKLlMVDPh9KXklVMhA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-remap-async-to-generator": "^7.12.13", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.13.tgz", + "integrity": "sha512-8SCJ0Ddrpwv4T7Gwb33EmW1V9PY5lggTO+A8WjyIwxrSHDUyBw4MtF96ifn1n8H806YlxbVCoKXbbmzD6RD+cA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-proposal-dynamic-import": { @@ -358,105 +347,105 @@ } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz", + "integrity": "sha512-v9eEi4GiORDg8x+Dmi5r8ibOe0VXoKDeNPYcTTxdGN4eOWikrJfDJCJrr1l5gKGvsNyGJbrfMftC2dTL6oz7pg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.13.tgz", + "integrity": "sha512-fqmiD3Lz7jVdK6kabeSr1PZlWSUVqSitmHEe3Z00dtGTKieWnX9beafvavc32kjORa5Bai4QNHgFDwWJP+WtSQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.13.tgz", + "integrity": "sha512-Qoxpy+OxhDBI5kRqliJFAl4uWXk3Bn24WeFstPH0iLymFehSAUR8MHpqU7njyXv/qbo7oN6yTy5bfCmXdKpo1Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.13.tgz", + "integrity": "sha512-WvA1okB/0OS/N3Ldb3sziSrXg6sRphsBgqiccfcQq7woEn5wQLNX82Oc4PlaFcdwcWHuQXAtb8ftbS8Fbsg/sg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@babel/plugin-transform-parameters": "^7.12.13" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz", + "integrity": "sha512-9+MIm6msl9sHWg58NvqpNpLtuFbmpFYk37x8kgnGzAHvX35E1FyAwSUt5hIkSoWJFSAH+iwU8bJ4fcD1zKXOzg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.13.tgz", + "integrity": "sha512-0ZwjGfTcnZqyV3y9DSD1Yk3ebp+sIUpT2YDqP8hovzaNZnQq2Kd7PEqa6iOIUDBXBt7Jl3P7YAcEIL5Pz8u09Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.13.tgz", + "integrity": "sha512-sV0V57uUwpauixvR7s2o75LmwJI6JECwm5oPUY5beZB1nBl2i37hc7CJGqB5G+58fur5Y6ugvl3LRONk5x34rg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-async-generators": { @@ -478,12 +467,12 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-dynamic-import": { @@ -577,12 +566,12 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-typescript": { @@ -603,308 +592,307 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz", + "integrity": "sha512-tBtuN6qtCTd+iHzVZVOMNp+L04iIJBpqkdY42tWbmjIT5wvR2kx7gxMBsyhQtFzHwBbyGi9h8J8r9HgnOpQHxg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.13.tgz", + "integrity": "sha512-psM9QHcHaDr+HZpRuJcE1PXESuGWSCcbiGFFhhwfzdbTxaGDVzuVtdNYliAwcRo3GFg0Bc8MmI+AvIGYIJG04A==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-remap-async-to-generator": "^7.12.13" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", + "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.13.tgz", + "integrity": "sha512-cqZlMlhCC1rVnxE5ZGMtIb896ijL90xppMiuWXcwcOAuFczynpd3KYemb91XFFPi3wJSe/OcrX9lXoowatkkxA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.13.tgz", + "integrity": "sha512-dDfuROUPGK1mTtLKyDPUavmj2b6kFu82SmgpztBFEO974KMjJT+Ytj3/oWsTUMBmgPcp9J5Pc1SlcAYRpJ2hRA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.13.tgz", + "integrity": "sha512-Dn83KykIFzjhA3FDPA1z4N+yfF3btDGhjnJwxIj0T43tP0flCujnU8fKgEkf0C1biIpSv9NZegPBQ1J6jYkwvQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.13.tgz", + "integrity": "sha512-xCbdgSzXYmHGyVX3+BsQjcd4hv4vA/FDy7Kc8eOpzKmBBPEOTurt0w5fCRQaGl+GSBORKgJdstQ1rHl4jbNseQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.13.tgz", + "integrity": "sha512-JHLOU0o81m5UqG0Ulz/fPC68/v+UTuGTWaZBUwpEk1fYQ1D9LfKV6MPn4ttJKqRo5Lm460fkzjLTL4EHvCprvA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.13.tgz", + "integrity": "sha512-OGQoeVXVi1259HjuoDnsQMlMkT9UkZT9TpXAsqWplS/M0N1g3TJAn/ByOCeQu7mfjc5WpSsRU+jV1Hd89ts0kQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz", + "integrity": "sha512-aHfVjhZ8QekaNF/5aNdStCGzwTbU7SI5hUybBKlMzqIMC7w7Ho8hx5a4R/DkTHfRfLwHGGxSpFt9BfxKCoXKoA==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-hoist-variables": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.13.tgz", + "integrity": "sha512-BgZndyABRML4z6ibpi7Z98m4EVLFI9tVsZDADC14AElFaNHHBcJIovflJ6wtCqFxwy2YJ1tJhGRsr0yLPKoN+w==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" + "@babel/helper-create-regexp-features-plugin": "^7.12.13" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" } }, "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.13.tgz", + "integrity": "sha512-e7QqwZalNiBRHCpJg/P8s/VJeSRYgmtWySs1JwvfwPqhBbiWfOcHDKdeAi6oAyIimoKWBlwc8oTgbZHdhCoVZA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz", + "integrity": "sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-runtime": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.10.tgz", - "integrity": "sha512-xOrUfzPxw7+WDm9igMgQCbO3cJKymX7dFdsgRr1eu9n3KjjyU4pptIXbXPseQDquw+W+RuJEJMHKHNsPNNm3CA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.15.tgz", + "integrity": "sha512-OwptMSRnRWJo+tJ9v9wgAf72ydXWfYSXWhnQjZing8nGZSDFqU1MBleKM3+DriKkcbv7RagA8gVeB0A1PNlNow==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", "semver": "^5.5.1" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.13.tgz", + "integrity": "sha512-dUCrqPIowjqk5pXsx1zPftSq4sT0aCeZVAxhdgs3AMgyaDmoUT0G+5h3Dzja27t76aUEIJWlFgPJqJ/d4dbTtg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.13.tgz", + "integrity": "sha512-arIKlWYUgmNsF28EyfmiQHJLJFlAJNYkuQO10jL46ggjBpeb2re1P9K9YGxNJB45BqTbaslVysXDYm/g3sN/Qg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", - "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-typescript": { @@ -1075,50 +1063,50 @@ } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/preset-env": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.11.tgz", - "integrity": "sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.13.tgz", + "integrity": "sha512-JUVlizG8SoFTz4LmVUL8++aVwzwxcvey3N0j1tRbMAXVEy95uQ/cnEkmEKHN00Bwq4voAV3imQGnQvpkLAxsrw==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.7", - "@babel/helper-compilation-targets": "^7.12.5", - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/compat-data": "^7.12.13", + "@babel/helper-compilation-targets": "^7.12.13", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/helper-validator-option": "^7.12.11", - "@babel/plugin-proposal-async-generator-functions": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.13", + "@babel/plugin-proposal-class-properties": "^7.12.13", "@babel/plugin-proposal-dynamic-import": "^7.12.1", - "@babel/plugin-proposal-export-namespace-from": "^7.12.1", - "@babel/plugin-proposal-json-strings": "^7.12.1", - "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.7", - "@babel/plugin-proposal-object-rest-spread": "^7.12.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.7", - "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.12.13", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.13", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.13", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.12.13", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.13", + "@babel/plugin-proposal-optional-chaining": "^7.12.13", + "@babel/plugin-proposal-private-methods": "^7.12.13", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.12.1", + "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.0", @@ -1128,41 +1116,41 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.12.1", - "@babel/plugin-transform-arrow-functions": "^7.12.1", - "@babel/plugin-transform-async-to-generator": "^7.12.1", - "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.11", - "@babel/plugin-transform-classes": "^7.12.1", - "@babel/plugin-transform-computed-properties": "^7.12.1", - "@babel/plugin-transform-destructuring": "^7.12.1", - "@babel/plugin-transform-dotall-regex": "^7.12.1", - "@babel/plugin-transform-duplicate-keys": "^7.12.1", - "@babel/plugin-transform-exponentiation-operator": "^7.12.1", - "@babel/plugin-transform-for-of": "^7.12.1", - "@babel/plugin-transform-function-name": "^7.12.1", - "@babel/plugin-transform-literals": "^7.12.1", - "@babel/plugin-transform-member-expression-literals": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-modules-systemjs": "^7.12.1", - "@babel/plugin-transform-modules-umd": "^7.12.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", - "@babel/plugin-transform-new-target": "^7.12.1", - "@babel/plugin-transform-object-super": "^7.12.1", - "@babel/plugin-transform-parameters": "^7.12.1", - "@babel/plugin-transform-property-literals": "^7.12.1", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-reserved-words": "^7.12.1", - "@babel/plugin-transform-shorthand-properties": "^7.12.1", - "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.7", - "@babel/plugin-transform-template-literals": "^7.12.1", - "@babel/plugin-transform-typeof-symbol": "^7.12.10", - "@babel/plugin-transform-unicode-escapes": "^7.12.1", - "@babel/plugin-transform-unicode-regex": "^7.12.1", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.12.13", + "@babel/plugin-transform-async-to-generator": "^7.12.13", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.12.13", + "@babel/plugin-transform-computed-properties": "^7.12.13", + "@babel/plugin-transform-destructuring": "^7.12.13", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.12.13", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.12.13", + "@babel/plugin-transform-modules-commonjs": "^7.12.13", + "@babel/plugin-transform-modules-systemjs": "^7.12.13", + "@babel/plugin-transform-modules-umd": "^7.12.13", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.12.13", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.12.13", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.12.13", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "core-js-compat": "^3.8.0", "semver": "^5.5.0" } @@ -1200,36 +1188,45 @@ } }, "@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", + "integrity": "sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==", "requires": { "regenerator-runtime": "^0.13.4" } }, + "@babel/runtime-corejs3": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.13.tgz", + "integrity": "sha512-8fSpqYRETHATtNitsCXq8QQbKJP31/KnDl2Wz2Vtui9nKzjss2ysuZtyVsWjBtvkeEFo346gkwjYPab1hvrXkQ==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1304,9 +1301,9 @@ } }, "@ethersproject/abi": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.10.tgz", - "integrity": "sha512-cfC3lGgotfxX3SMri4+CisOPwignoj/QGHW9J29spC4R4Qqcnk/SYuVkPFBMdLbvBp3f/pGiVqPNwont0TSXhg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.12.tgz", + "integrity": "sha512-Ujr/3bwyYYjXLDQfebeiiTuvOw9XtUKM8av6YkoBeMXyGQM9GkjrQlwJMNwGTmqjATH/ZNbRgCh98GjOLiIB1Q==", "requires": { "@ethersproject/address": "^5.0.9", "@ethersproject/bignumber": "^5.0.13", @@ -1320,9 +1317,9 @@ } }, "@ethersproject/abstract-provider": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.0.8.tgz", - "integrity": "sha512-fqJXkewcGdi8LogKMgRyzc/Ls2js07yor7+g9KfPs09uPOcQLg7cc34JN+lk34HH9gg2HU0DIA5797ZR8znkfw==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.0.9.tgz", + "integrity": "sha512-X9fMkqpeu9ayC3JyBkeeZhn35P4xQkpGX/l+FrxDtEW9tybf/UWXSMi8bGThpPtfJ6q6U2LDetXSpSwK4TfYQQ==", "requires": { "@ethersproject/bignumber": "^5.0.13", "@ethersproject/bytes": "^5.0.9", @@ -1334,9 +1331,9 @@ } }, "@ethersproject/abstract-signer": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.11.tgz", - "integrity": "sha512-RKOgPSEYafknA62SrD3OCK42AllHE4YBfKYXyQeM+sBP7Nq3X5FpzeoY4uzC43P4wIhmNoTHCKQuwnX7fBqb6Q==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.12.tgz", + "integrity": "sha512-qt4jAEzQGPZ31My1gFGPzzJHJveYhVycW7RHkuX0W8fvMdg7wr0uvP7mQEptMVrb+jYwsVktCf6gBGwWDpFiTA==", "requires": { "@ethersproject/abstract-provider": "^5.0.8", "@ethersproject/bignumber": "^5.0.13", @@ -1346,9 +1343,9 @@ } }, "@ethersproject/address": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.9.tgz", - "integrity": "sha512-gKkmbZDMyGbVjr8nA5P0md1GgESqSGH7ILIrDidPdNXBl4adqbuA3OAuZx/O2oGpL6PtJ9BDa0kHheZ1ToHU3w==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.10.tgz", + "integrity": "sha512-70vqESmW5Srua1kMDIN6uVfdneZMaMyRYH4qPvkAXGkbicrCOsA9m01vIloA4wYiiF+HLEfL1ENKdn5jb9xiAw==", "requires": { "@ethersproject/bignumber": "^5.0.13", "@ethersproject/bytes": "^5.0.9", @@ -1358,26 +1355,26 @@ } }, "@ethersproject/base64": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.0.7.tgz", - "integrity": "sha512-S5oh5DVfCo06xwJXT8fQC68mvJfgScTl2AXvbYMsHNfIBTDb084Wx4iA9MNlEReOv6HulkS+gyrUM/j3514rSw==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.0.8.tgz", + "integrity": "sha512-PNbpHOMgZpZ1skvQl119pV2YkCPXmZTxw+T92qX0z7zaMFPypXWTZBzim+hUceb//zx4DFjeGT4aSjZRTOYThg==", "requires": { "@ethersproject/bytes": "^5.0.9" } }, "@ethersproject/basex": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.0.7.tgz", - "integrity": "sha512-OsXnRsujGmYD9LYyJlX+cVe5KfwgLUbUJrJMWdzRWogrygXd5HvGd7ygX1AYjlu1z8W/+t2FoQnczDR/H2iBjA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.0.8.tgz", + "integrity": "sha512-PCVKZIShBQUqAXjJSvaCidThPvL0jaaQZcewJc0sf8Xx05BizaOS8r3jdPdpNdY+/qZtRDqwHTSKjvR/xssyLQ==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/properties": "^5.0.7" } }, "@ethersproject/bignumber": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.13.tgz", - "integrity": "sha512-b89bX5li6aK492yuPP5mPgRVgIxxBP7ksaBtKX5QQBsrZTpNOjf/MR4CjcUrAw8g+RQuD6kap9lPjFgY4U1/5A==", + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.14.tgz", + "integrity": "sha512-Q4TjMq9Gg3Xzj0aeJWqJgI3tdEiPiET7Y5OtNtjTAODZ2kp4y9jMNg97zVcvPedFvGROdpGDyCI77JDFodUzOw==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/logger": "^5.0.8", @@ -1385,25 +1382,25 @@ } }, "@ethersproject/bytes": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.9.tgz", - "integrity": "sha512-k+17ZViDtAugC0s7HM6rdsTWEdIYII4RPCDkPEuxKc6i40Bs+m6tjRAtCECX06wKZnrEoR9pjOJRXHJ/VLoOcA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.10.tgz", + "integrity": "sha512-vpu0v1LZ1j1s9kERQIMnVU69MyHEzUff7nqK9XuCU4vx+AM8n9lU2gj7jtJIvGSt9HzatK/6I6bWusI5nyuaTA==", "requires": { "@ethersproject/logger": "^5.0.8" } }, "@ethersproject/constants": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.8.tgz", - "integrity": "sha512-sCc73pFBsl59eDfoQR5OCEZCRv5b0iywadunti6MQIr5lt3XpwxK1Iuzd8XSFO02N9jUifvuZRrt0cY0+NBgTg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.9.tgz", + "integrity": "sha512-2uAKH89UcaJP/Sc+54u92BtJtZ4cPgcS1p0YbB1L3tlkavwNvth+kNCUplIB1Becqs7BOZr0B/3dMNjhJDy4Dg==", "requires": { "@ethersproject/bignumber": "^5.0.13" } }, "@ethersproject/contracts": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.0.9.tgz", - "integrity": "sha512-CCTxVeDh6sjdSEbjzONhtwPjECvaHE62oGkY8M7kP0CHmgLD2SEGel0HZib8e5oQKRKGly9AKcUFW4g3rQ0AQw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.0.10.tgz", + "integrity": "sha512-h9kdvllwT6B1LyUXeNQIb7Y6u6ZprP5LUiQIjSqvOehhm1sFZcaVtydsSa0LIg3SBC5QF0M7zH5p7EtI2VD0rQ==", "requires": { "@ethersproject/abi": "^5.0.10", "@ethersproject/abstract-provider": "^5.0.8", @@ -1417,9 +1414,9 @@ } }, "@ethersproject/hash": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.10.tgz", - "integrity": "sha512-Tf0bvs6YFhw28LuHnhlDWyr0xfcDxSXdwM4TcskeBbmXVSKLv3bJQEEEBFUcRX0fJuslR3gCVySEaSh7vuMx5w==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.11.tgz", + "integrity": "sha512-H3KJ9fk33XWJ2djAW03IL7fg3DsDMYjO1XijiUb1hJ85vYfhvxu0OmsU7d3tg2Uv1H1kFSo8ghr3WFQ8c+NL3g==", "requires": { "@ethersproject/abstract-signer": "^5.0.10", "@ethersproject/address": "^5.0.9", @@ -1432,9 +1429,9 @@ } }, "@ethersproject/hdnode": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.0.8.tgz", - "integrity": "sha512-Mscpjd7BBjxYSWghaNMwV0xrBBkOoCq6YEPRm9MgE24CiBlzzbfEB5DGq6hiZqhQaxPkdCUtKKqZi3nt9hx43g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.0.9.tgz", + "integrity": "sha512-S5UMmIC6XfFtqhUK4uTjD8GPNzSbE+sZ/0VMqFnA3zAJ+cEFZuEyhZDYnl2ItGJzjT4jsy+uEy1SIl3baYK1PQ==", "requires": { "@ethersproject/abstract-signer": "^5.0.10", "@ethersproject/basex": "^5.0.7", @@ -1451,9 +1448,9 @@ } }, "@ethersproject/json-wallets": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.0.10.tgz", - "integrity": "sha512-Ux36u+d7Dm0M5AQ+mWuHdvfGPMN8K1aaLQgwzrsD4ELTWlwRuHuQbmn7/GqeOpbfaV6POLwdYcBk2TXjlGp/IQ==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.0.11.tgz", + "integrity": "sha512-0GhWScWUlXXb4qJNp0wmkU95QS3YdN9UMOfMSEl76CRANWWrmyzxcBVSXSBu5iQ0/W8wO+xGlJJ3tpA6v3mbIw==", "requires": { "@ethersproject/abstract-signer": "^5.0.10", "@ethersproject/address": "^5.0.9", @@ -1471,48 +1468,48 @@ } }, "@ethersproject/keccak256": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.7.tgz", - "integrity": "sha512-zpUBmofWvx9PGfc7IICobgFQSgNmTOGTGLUxSYqZzY/T+b4y/2o5eqf/GGmD7qnTGzKQ42YlLNo+LeDP2qe55g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.8.tgz", + "integrity": "sha512-zoGbwXcWWs9MX4NOAZ7N0hhgIRl4Q/IO/u9c/RHRY4WqDy3Ywm0OLamEV53QDwhjwn3YiiVwU1Ve5j7yJ0a/KQ==", "requires": { "@ethersproject/bytes": "^5.0.9", "js-sha3": "0.5.7" } }, "@ethersproject/logger": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.8.tgz", - "integrity": "sha512-SkJCTaVTnaZ3/ieLF5pVftxGEFX56pTH+f2Slrpv7cU0TNpUZNib84QQdukd++sWUp/S7j5t5NW+WegbXd4U/A==" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.9.tgz", + "integrity": "sha512-kV3Uamv3XOH99Xf3kpIG3ZkS7mBNYcLDM00JSDtNgNB4BihuyxpQzIZPRIDmRi+95Z/R1Bb0X2kUNHa/kJoVrw==" }, "@ethersproject/networks": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.0.7.tgz", - "integrity": "sha512-dI14QATndIcUgcCBL1c5vUr/YsI5cCHLN81rF7PU+yS7Xgp2/Rzbr9+YqpC6NBXHFUASjh6GpKqsVMpufAL0BQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.0.8.tgz", + "integrity": "sha512-PYpptlO2Tu5f/JEBI5hdlMds5k1DY1QwVbh3LKPb3un9dQA2bC51vd2/gRWAgSBpF3kkmZOj4FhD7ATLX4H+DA==", "requires": { "@ethersproject/logger": "^5.0.8" } }, "@ethersproject/pbkdf2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.0.7.tgz", - "integrity": "sha512-0SNLNixPMqnosH6pyc4yPiUu/C9/Jbu+f6I8GJW9U2qNpMBddmRJviwseoha5Zw1V+Aw0Z/yvYyzIIE8yPXqLA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.0.8.tgz", + "integrity": "sha512-UlmAMGbIPaS2xXsI38FbePVTfJMuU9jnwcqVn3p88HxPF4kD897ha+l3TNsBqJqf32UbQL5GImnf1oJkSKq4vQ==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/sha2": "^5.0.7" } }, "@ethersproject/properties": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.7.tgz", - "integrity": "sha512-812H1Rus2vjw0zbasfDI1GLNPDsoyX1pYqiCgaR1BuyKxUTbwcH1B+214l6VGe1v+F6iEVb7WjIwMjKhb4EUsg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.8.tgz", + "integrity": "sha512-zEnLMze2Eu2VDPj/05QwCwMKHh506gpT9PP9KPVd4dDB+5d6AcROUYVLoIIQgBYK7X/Gw0UJmG3oVtnxOQafAw==", "requires": { "@ethersproject/logger": "^5.0.8" } }, "@ethersproject/providers": { - "version": "5.0.19", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.0.19.tgz", - "integrity": "sha512-G+flo1jK1y/rvQy6b71+Nu7qOlkOKz+XqpgqFMZslkCzGuzQRmk9Qp7Ln4soK8RSyP1e5TCujaRf1H+EZahoaw==", + "version": "5.0.22", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.0.22.tgz", + "integrity": "sha512-6C6agQsz/7FRFfZFok+m1HVUS6G+IU1grLwBx9+W11SF3UeP8vquXRMVDfOiKWyvy+g4bJT1+9C/QuC8lG08DQ==", "requires": { "@ethersproject/abstract-provider": "^5.0.8", "@ethersproject/abstract-signer": "^5.0.10", @@ -1543,27 +1540,27 @@ } }, "@ethersproject/random": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.0.7.tgz", - "integrity": "sha512-PxSRWwN3s+FH9AWMZU6AcWJsNQ9KzqKV6NgdeKPtxahdDjCuXxTAuzTZNXNRK+qj+Il351UnweAGd+VuZcOAlQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.0.8.tgz", + "integrity": "sha512-4rHtotmd9NjklW0eDvByicEkL+qareIyFSbG1ShC8tPJJSAC0g55oQWzw+3nfdRCgBHRuEE7S8EcPcTVPvZ9cA==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/logger": "^5.0.8" } }, "@ethersproject/rlp": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.7.tgz", - "integrity": "sha512-ulUTVEuV7PT4jJTPpfhRHK57tkLEDEY9XSYJtrSNHOqdwMvH0z7BM2AKIMq4LVDlnu4YZASdKrkFGEIO712V9w==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.8.tgz", + "integrity": "sha512-E4wdFs8xRNJfzNHmnkC8w5fPeT4Wd1U2cust3YeT16/46iSkLT8nn8ilidC6KhR7hfuSZE4UqSPzyk76p7cdZg==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/logger": "^5.0.8" } }, "@ethersproject/sha2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.0.7.tgz", - "integrity": "sha512-MbUqz68hhp5RsaZdqi1eg1rrtiqt5wmhRYqdA7MX8swBkzW2KiLgK+Oh25UcWhUhdi1ImU9qrV6if5j0cC7Bxg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.0.8.tgz", + "integrity": "sha512-ILP1ZgyvDj4rrdE+AXrTv9V88m7x87uga2VZ/FeULKPumOEw/4bGnJz/oQ8zDnDvVYRCJ+48VaQBS2CFLbk1ww==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/logger": "^5.0.8", @@ -1582,20 +1579,20 @@ } }, "@ethersproject/signing-key": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.8.tgz", - "integrity": "sha512-YKxQM45eDa6WAD+s3QZPdm1uW1MutzVuyoepdRRVmMJ8qkk7iOiIhUkZwqKLNxKzEJijt/82ycuOREc9WBNAKg==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.10.tgz", + "integrity": "sha512-w5it3GbFOvN6e0mTd5gDNj+bwSe6L9jqqYjU+uaYS8/hAEp4qYLk5p8ZjbJJkNn7u1p0iwocp8X9oH/OdK8apA==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/logger": "^5.0.8", "@ethersproject/properties": "^5.0.7", - "elliptic": "6.5.3" + "elliptic": "6.5.4" } }, "@ethersproject/solidity": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.0.8.tgz", - "integrity": "sha512-OJkyBq9KaoGsi8E8mYn6LX+vKyCURvxSp0yuGBcOqEFM3vkn9PsCiXsHdOXdNBvlHG5evJXwAYC2UR0TzgJeKA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.0.9.tgz", + "integrity": "sha512-LIxSAYEQgLRXE3mRPCq39ou61kqP8fDrGqEeNcaNJS3aLbmAOS8MZp56uK++WsdI9hj8sNsFh78hrAa6zR9Jag==", "requires": { "@ethersproject/bignumber": "^5.0.13", "@ethersproject/bytes": "^5.0.9", @@ -1605,9 +1602,9 @@ } }, "@ethersproject/strings": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.8.tgz", - "integrity": "sha512-5IsdXf8tMY8QuHl8vTLnk9ehXDDm6x9FB9S9Og5IA1GYhLe5ZewydXSjlJlsqU2t9HRbfv97OJZV/pX8DVA/Hw==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.9.tgz", + "integrity": "sha512-ogxBpcUpdO524CYs841MoJHgHxEPUy0bJFDS4Ezg8My+WYVMfVAOlZSLss0Rurbeeam8CpUVDzM4zUn09SU66Q==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/constants": "^5.0.8", @@ -1615,9 +1612,9 @@ } }, "@ethersproject/transactions": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.9.tgz", - "integrity": "sha512-0Fu1yhdFBkrbMjenEr+39tmDxuHmaw0pe9Jb18XuKoItj7Z3p7+UzdHLr2S/okvHDHYPbZE5gtANDdQ3ZL1nBA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.10.tgz", + "integrity": "sha512-Tqpp+vKYQyQdJQQk4M73tDzO7ODf2D42/sJOcKlDAAbdSni13v6a+31hUdo02qYXhVYwIs+ZjHnO4zKv5BNk8w==", "requires": { "@ethersproject/address": "^5.0.9", "@ethersproject/bignumber": "^5.0.13", @@ -1631,9 +1628,9 @@ } }, "@ethersproject/units": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.0.9.tgz", - "integrity": "sha512-4jIkcMVrJ3lCgXMO4M/2ww0/T/IN08vJTZld7FIAwa6aoBDTAy71+sby3sShl1SG3HEeKYbI3fBWauCUgPRUpQ==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.0.10.tgz", + "integrity": "sha512-eaiHi9ham5lbC7qpqxpae7OY/nHJUnRUnFFuEwi2VB5Nwe3Np468OAV+e+HR+jAK4fHXQE6PFBTxWGtnZuO37g==", "requires": { "@ethersproject/bignumber": "^5.0.13", "@ethersproject/constants": "^5.0.8", @@ -1641,9 +1638,9 @@ } }, "@ethersproject/wallet": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.0.10.tgz", - "integrity": "sha512-5siYr38NhqZKH6DUr6u4PdhgOKur8Q6sw+JID2TitEUmW0tOl8f6rpxAe77tw6SJT60D2UcvgsyLtl32+Nl+ig==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.0.11.tgz", + "integrity": "sha512-2Fg/DOvUltR7aZTOyWWlQhru+SKvq2UE3uEhXSyCFgMqDQNuc2nHXh1SHJtN65jsEbjVIppOe1Q7EQMvhmeeRw==", "requires": { "@ethersproject/abstract-provider": "^5.0.8", "@ethersproject/abstract-signer": "^5.0.10", @@ -1663,9 +1660,9 @@ } }, "@ethersproject/web": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.0.12.tgz", - "integrity": "sha512-gVxS5iW0bgidZ76kr7LsTxj4uzN5XpCLzvZrLp8TP+4YgxHfCeetFyQkRPgBEAJdNrexdSBayvyJvzGvOq0O8g==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.0.13.tgz", + "integrity": "sha512-G3x/Ns7pQm21ALnWLbdBI5XkW/jrsbXXffI9hKNPHqf59mTxHYtlNiSwxdoTSwCef3Hn7uvGZpaSgTyxs7IufQ==", "requires": { "@ethersproject/base64": "^5.0.7", "@ethersproject/bytes": "^5.0.9", @@ -1675,9 +1672,9 @@ } }, "@ethersproject/wordlists": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.0.8.tgz", - "integrity": "sha512-px2mloc1wAcdTbzv0ZotTx+Uh/dfnDO22D9Rx8xr7+/PUwAhZQjoJ9t7Hn72nsaN83rWBXsLvFcIRZju4GIaEQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.0.9.tgz", + "integrity": "sha512-Sn6MTjZkfbriod6GG6+p43W09HOXT4gwcDVNj0YoPYlo4Zq2Fk6b1CU9KUX3c6aI17PrgYb4qwZm5BMuORyqyQ==", "requires": { "@ethersproject/bytes": "^5.0.9", "@ethersproject/hash": "^5.0.10", @@ -2352,24 +2349,13 @@ } }, "@npmcli/move-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.0.tgz", - "integrity": "sha512-Iv2iq0JuyYjKeFkSR4LPaCdDZwlGK9X2cP/01nJcp3yMJ1FjNd9vpiEYvLUgzBxKPg2SFmaOhizoQsPc0LWeOQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.1.tgz", + "integrity": "sha512-LtWTicuF2wp7PNTuyCwABx7nNG+DnzSE8gN0iWxkC6mpgm/iOPu0ZMTkXuCxmJxtWFsDxUaixM9COSNJEMUfuQ==", "dev": true, "requires": { "mkdirp": "^1.0.4", - "rimraf": "^2.7.1" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "rimraf": "^3.0.2" } }, "@peculiar/asn1-schema": { @@ -2524,9 +2510,9 @@ } }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, "@types/json5": { @@ -2536,9 +2522,9 @@ "dev": true }, "@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", + "version": "14.14.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", + "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==", "dev": true }, "@types/normalize-package-data": { @@ -2548,9 +2534,9 @@ "dev": true }, "@types/prettier": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", - "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.0.tgz", + "integrity": "sha512-O3SQC6+6AySHwrspYn2UvC6tjo6jCTMMmylxZUFhE1CulVu5l3AxU6ca9lrJDTQDVllF62LIxVSx5fuYL6LiZg==", "dev": true }, "@types/qs": { @@ -2566,9 +2552,9 @@ "dev": true }, "@types/yargs": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", - "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2871,24 +2857,24 @@ } }, "@webpack-cli/configtest": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.0.tgz", - "integrity": "sha512-Un0SdBoN1h4ACnIO7EiCjWuyhNI0Jl96JC+63q6xi4HDUYRZn8Auluea9D+v9NWKc5J4sICVEltdBaVjLX39xw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.1.tgz", + "integrity": "sha512-B+4uBUYhpzDXmwuo3V9yBH6cISwxEI4J+NO5ggDaGEEHb0osY/R7MzeKc0bHURXQuZjMM4qD+bSJCKIuI3eNBQ==", "dev": true }, "@webpack-cli/info": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.1.tgz", - "integrity": "sha512-fLnDML5HZ5AEKzHul8xLAksoKN2cibu6MgonkUj8R9V7bbeVRkd1XbGEGWrAUNYHbX1jcqCsDEpBviE5StPMzQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.2.tgz", + "integrity": "sha512-5U9kUJHnwU+FhKH4PWGZuBC1hTEPYyxGSL5jjoBI96Gx8qcYJGOikpiIpFoTq8mmgX3im2zAo2wanv/alD74KQ==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.2.2.tgz", - "integrity": "sha512-03GkWxcgFfm8+WIwcsqJb9agrSDNDDoxaNnexPnCCexP5SCE4IgFd9lNpSy+K2nFqVMpgTFw6SwbmVAVTndVew==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.0.tgz", + "integrity": "sha512-k2p2VrONcYVX1wRRrf0f3X2VGltLWcv+JzXRBDmvCxGlCeESx4OXw91TsWeKOkp784uNoVQo313vxJFHXPPwfw==", "dev": true }, "@xtuc/ieee754": { @@ -3953,16 +3939,16 @@ } }, "browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001173", + "caniuse-lite": "^1.0.30001181", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", + "electron-to-chromium": "^1.3.649", "escalade": "^3.1.1", - "node-releases": "^1.1.69" + "node-releases": "^1.1.70" } }, "bser": { @@ -4045,17 +4031,6 @@ "ssri": "^8.0.0", "tar": "^6.0.2", "unique-filename": "^1.1.1" - }, - "dependencies": { - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - } } }, "cache-base": { @@ -4098,9 +4073,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001178", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz", - "integrity": "sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ==", + "version": "1.0.30001185", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz", + "integrity": "sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg==", "dev": true }, "capture-exit": { @@ -4160,9 +4135,9 @@ "dev": true }, "chokidar": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", - "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "optional": true, "requires": { @@ -4656,12 +4631,12 @@ "dev": true }, "core-js-compat": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", - "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", + "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", "dev": true, "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.16.1", "semver": "7.0.0" }, "dependencies": { @@ -4673,6 +4648,11 @@ } } }, + "core-js-pure": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.3.tgz", + "integrity": "sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -5191,23 +5171,23 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.641", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.641.tgz", - "integrity": "sha512-b0DLhsHSHESC1I+Nx6n4w4Lr61chMd3m/av1rZQhS2IXTzaS5BMM5N+ldWdMIlni9CITMRM09m8He4+YV/92TA==", + "version": "1.3.661", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.661.tgz", + "integrity": "sha512-INNzKoL9ceOpPCpF5J+Fp9AOHY1RegwKViohAyTzV3XbkuRUx04r4v8edsDbevsog8UuL0GvD/Qerr2HwVTlSA==", "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, "emittery": { @@ -5306,9 +5286,9 @@ } }, "envinfo": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", - "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.4.tgz", + "integrity": "sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ==", "dev": true }, "errno": { @@ -5501,6 +5481,15 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -5931,40 +5920,40 @@ "dev": true }, "ethers": { - "version": "5.0.26", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.0.26.tgz", - "integrity": "sha512-MqA8Fvutn3qEW0yBJOHeV6KZmRpF2rqlL2B5058AGkUFsuu6j5Ns/FRlMsbGeQwBz801IB23jQp7vjRfFsKSkg==", - "requires": { - "@ethersproject/abi": "5.0.10", - "@ethersproject/abstract-provider": "5.0.8", - "@ethersproject/abstract-signer": "5.0.11", - "@ethersproject/address": "5.0.9", - "@ethersproject/base64": "5.0.7", - "@ethersproject/basex": "5.0.7", - "@ethersproject/bignumber": "5.0.13", - "@ethersproject/bytes": "5.0.9", - "@ethersproject/constants": "5.0.8", - "@ethersproject/contracts": "5.0.9", - "@ethersproject/hash": "5.0.10", - "@ethersproject/hdnode": "5.0.8", - "@ethersproject/json-wallets": "5.0.10", - "@ethersproject/keccak256": "5.0.7", - "@ethersproject/logger": "5.0.8", - "@ethersproject/networks": "5.0.7", - "@ethersproject/pbkdf2": "5.0.7", - "@ethersproject/properties": "5.0.7", - "@ethersproject/providers": "5.0.19", - "@ethersproject/random": "5.0.7", - "@ethersproject/rlp": "5.0.7", - "@ethersproject/sha2": "5.0.7", - "@ethersproject/signing-key": "5.0.8", - "@ethersproject/solidity": "5.0.8", - "@ethersproject/strings": "5.0.8", - "@ethersproject/transactions": "5.0.9", - "@ethersproject/units": "5.0.9", - "@ethersproject/wallet": "5.0.10", - "@ethersproject/web": "5.0.12", - "@ethersproject/wordlists": "5.0.8" + "version": "5.0.30", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.0.30.tgz", + "integrity": "sha512-CdY/zb8d0uEBaWNmDkAVWXO8FLs2plAPOjgukgYC95L5VKIZzZaCav7PAeG2IqGico4vtNu8l3ibXdXd6FqjrQ==", + "requires": { + "@ethersproject/abi": "5.0.12", + "@ethersproject/abstract-provider": "5.0.9", + "@ethersproject/abstract-signer": "5.0.12", + "@ethersproject/address": "5.0.10", + "@ethersproject/base64": "5.0.8", + "@ethersproject/basex": "5.0.8", + "@ethersproject/bignumber": "5.0.14", + "@ethersproject/bytes": "5.0.10", + "@ethersproject/constants": "5.0.9", + "@ethersproject/contracts": "5.0.10", + "@ethersproject/hash": "5.0.11", + "@ethersproject/hdnode": "5.0.9", + "@ethersproject/json-wallets": "5.0.11", + "@ethersproject/keccak256": "5.0.8", + "@ethersproject/logger": "5.0.9", + "@ethersproject/networks": "5.0.8", + "@ethersproject/pbkdf2": "5.0.8", + "@ethersproject/properties": "5.0.8", + "@ethersproject/providers": "5.0.22", + "@ethersproject/random": "5.0.8", + "@ethersproject/rlp": "5.0.8", + "@ethersproject/sha2": "5.0.8", + "@ethersproject/signing-key": "5.0.10", + "@ethersproject/solidity": "5.0.9", + "@ethersproject/strings": "5.0.9", + "@ethersproject/transactions": "5.0.10", + "@ethersproject/units": "5.0.10", + "@ethersproject/wallet": "5.0.11", + "@ethersproject/web": "5.0.13", + "@ethersproject/wordlists": "5.0.9" } }, "eventemitter3": { @@ -6813,9 +6802,9 @@ "dev": true }, "fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -6893,9 +6882,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -7122,9 +7111,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "growl": { @@ -7534,9 +7523,9 @@ "dev": true }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, "is-ci": { @@ -7714,11 +7703,12 @@ "dev": true }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "has-symbols": "^1.0.1" } }, @@ -9550,9 +9540,9 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -10741,9 +10731,9 @@ } }, "node-releases": { - "version": "1.1.69", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", - "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==", + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", "dev": true }, "node-status-codes": { @@ -11760,11 +11750,11 @@ "dev": true }, "pvtsutils": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.1.1.tgz", - "integrity": "sha512-Evbhe6L4Sxwu4SPLQ4LQZhgfWDQO3qa1lju9jM5cxsQp8vE10VipcSmo7hiJW48TmiHgVLgDtC2TL6/+ND+IVg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.1.2.tgz", + "integrity": "sha512-Yfm9Dsk1zfEpOWCaJaHfqtNXAFWNNHMFSCLN6jTnhuCCBCC2nqge4sAgo7UrkRBoAAYIL8TN/6LlLoNfZD/b5A==", "requires": { - "tslib": "^2.0.3" + "tslib": "^2.1.0" } }, "pvutils": { @@ -12106,9 +12096,9 @@ "dev": true }, "regjsparser": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.6.tgz", - "integrity": "sha512-jjyuCp+IEMIm3N1H1LLTJW1EISEJV9+5oHdEyrt43Pg9cDSb6rrLZei2cVWpl0xTjmmlpec/lEQGYgM7xfpGCQ==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.7.tgz", + "integrity": "sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -12649,9 +12639,9 @@ } }, "sirv": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.10.tgz", - "integrity": "sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz", + "integrity": "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==", "dev": true, "requires": { "@polka/url": "^1.0.0-next.9", @@ -12919,9 +12909,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, "spdx-correct": { @@ -12989,9 +12979,9 @@ } }, "ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, "requires": { "minipass": "^3.1.1" @@ -13341,9 +13331,9 @@ }, "dependencies": { "ajv": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.0.tgz", - "integrity": "sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", + "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -14139,15 +14129,15 @@ } }, "webcrypto-core": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.1.8.tgz", - "integrity": "sha512-hKnFXsqh0VloojNeTfrwFoRM4MnaWzH6vtXcaFcGjPEu+8HmBdQZnps3/2ikOFqS8bJN1RYr6mI2P/FJzyZnXg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.2.0.tgz", + "integrity": "sha512-p76Z/YLuE4CHCRdc49FB/ETaM4bzM3roqWNJeGs+QNY1fOTzKTOVnhmudW1fuO+5EZg6/4LG9NJ6gaAyxTk9XQ==", "requires": { - "@peculiar/asn1-schema": "^2.0.12", + "@peculiar/asn1-schema": "^2.0.27", "@peculiar/json-schema": "^1.1.12", "asn1js": "^2.0.26", - "pvtsutils": "^1.0.11", - "tslib": "^2.0.1" + "pvtsutils": "^1.1.2", + "tslib": "^2.1.0" } }, "webidl-conversions": { @@ -14492,17 +14482,17 @@ } }, "webpack-cli": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.4.0.tgz", - "integrity": "sha512-/Qh07CXfXEkMu5S8wEpjuaw2Zj/CC0hf/qbTDp6N8N7JjdGuaOjZ7kttz+zhuJO/J5m7alQEhNk9lsc4rC6xgQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.5.0.tgz", + "integrity": "sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.0.0", - "@webpack-cli/info": "^1.2.1", - "@webpack-cli/serve": "^1.2.2", + "@webpack-cli/configtest": "^1.0.1", + "@webpack-cli/info": "^1.2.2", + "@webpack-cli/serve": "^1.3.0", "colorette": "^1.2.1", - "commander": "^6.2.0", + "commander": "^7.0.0", "enquirer": "^2.3.6", "execa": "^5.0.0", "fastest-levenshtein": "^1.0.12", @@ -14514,9 +14504,9 @@ }, "dependencies": { "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", + "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", "dev": true }, "execa": { @@ -14772,9 +14762,9 @@ } }, "ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", + "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==" }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 408b53492..1ec726854 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-transform-classes": "^7.12.1", "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", + "@babel/plugin-transform-runtime": "^7.12.15", "@babel/preset-env": "^7.12.11", "@babel/preset-typescript": "^7.12.13", "@types/debug": "^4.1.5", @@ -87,7 +87,8 @@ }, "#IMPORTANT": "babel-runtime must be in dependencies, not devDependencies", "dependencies": { - "@babel/runtime": "^7.12.5", + "@babel/runtime": "^7.12.13", + "@babel/runtime-corejs3": "^7.12.13", "@ethersproject/address": "^5.0.9", "@ethersproject/bignumber": "^5.0.13", "@ethersproject/bytes": "^5.0.9", From 942f7c496f4a400a173ccd445246bd36a41c1c3d Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 13:35:00 -0500 Subject: [PATCH 08/30] Fix jest module resolution with npm linked deps. --- jest.config.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jest.config.js b/jest.config.js index 1086a1106..f273f3d05 100644 --- a/jest.config.js +++ b/jest.config.js @@ -60,9 +60,10 @@ module.exports = { // globals: {}, // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], + moduleDirectories: [ + 'node_modules', + path.resolve('./node_modules'), // makes npm link work. + ], // An array of file extensions your modules use // moduleFileExtensions: [ From 5be869d1bbe8c1290f8b6a5b136ff94e702f06df Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 13:36:13 -0500 Subject: [PATCH 09/30] Default onFinally is async for type hinting. --- src/subscribe/index.js | 2 +- src/subscribe/pipeline.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/subscribe/index.js b/src/subscribe/index.js index 456ec49ef..db4af5fe1 100644 --- a/src/subscribe/index.js +++ b/src/subscribe/index.js @@ -319,7 +319,7 @@ class Subscriptions { this.subSessions = new Map() } - async add(opts, onFinally = () => {}) { + async add(opts, onFinally = async () => {}) { const options = validateOptions(opts) const { key } = options diff --git a/src/subscribe/pipeline.js b/src/subscribe/pipeline.js index 577c50d5e..9c1182c42 100644 --- a/src/subscribe/pipeline.js +++ b/src/subscribe/pipeline.js @@ -22,7 +22,7 @@ async function collect(src) { * Subscription message processing pipeline */ -export default function MessagePipeline(client, opts = {}, onFinally = () => {}) { +export default function MessagePipeline(client, opts = {}, onFinally = async () => {}) { const options = validateOptions(opts) const { key, afterSteps = [], beforeSteps = [], onError = (err) => { throw err } } = options const id = counterId('MessagePipeline') + key From 28980048fdd727670ae56b75fccce29a113df0e2 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 14:08:37 -0500 Subject: [PATCH 10/30] Fix broken OrderMessages output. --- src/subscribe/OrderMessages.js | 11 ----------- test/integration/GapFill.test.js | 6 ++++-- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index 05b091e83..ea72fa02b 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -83,17 +83,6 @@ export default function OrderMessages(client, options = {}) { } }, outStream, // consumer gets outStream - async function* IsDone(src) { - for await (const msg of src) { - if (!gapFill) { - orderingUtil.markMessageExplicitly(msg) - } - - orderingUtil.add(msg) - // note no yield - // orderingUtil writes to outStream itself - } - }, ], async (err) => { done = true orderingUtil.clearGaps() diff --git a/test/integration/GapFill.test.js b/test/integration/GapFill.test.js index 8a7c90980..10e1aad57 100644 --- a/test/integration/GapFill.test.js +++ b/test/integration/GapFill.test.js @@ -123,7 +123,9 @@ describeRepeats('GapFill', () => { expect(subscriber.count(stream.id)).toBe(1) - const published = await publishTestMessages(MAX_MESSAGES) + const published = await publishTestMessages(MAX_MESSAGES, { + timestamp: 111111, + }) const received = [] for await (const m of sub) { @@ -202,7 +204,7 @@ describeRepeats('GapFill', () => { expect(client.connection.getState()).toBe('connected') }, 15000) - it('can fill gaps in resends', async () => { + it.skip('can fill gaps in resends', async () => { const { parse } = client.connection let count = 0 client.connection.parse = (...args) => { From 7d1a91f99bd3ef7b450ec594c5c341d3a5a7ad47 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 14:15:13 -0500 Subject: [PATCH 11/30] Convert jest.setup to ESM. --- jest.config.js | 3 +-- jest.setup.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/jest.config.js b/jest.config.js index f273f3d05..4be81d064 100644 --- a/jest.config.js +++ b/jest.config.js @@ -62,9 +62,8 @@ module.exports = { // An array of directory names to be searched recursively up from the requiring module's location moduleDirectories: [ 'node_modules', - path.resolve('./node_modules'), // makes npm link work. + path.resolve('./node_modules'), // makes npm link work. ], - // An array of file extensions your modules use // moduleFileExtensions: [ // "js", diff --git a/jest.setup.js b/jest.setup.js index 81b9b01b6..5592139d6 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,16 +1,16 @@ -const Debug = require('debug') -const GitRevisionPlugin = require('git-revision-webpack-plugin') +import GitRevisionPlugin from 'git-revision-webpack-plugin' +import Debug from 'debug' const pkg = require('./package.json') -if (process.env.DEBUG_CONSOLE) { - // Use debug as console log - // This prevents jest messing with console output - // Ensuring debug messages are printed alongside console messages, in the correct order - console.log = Debug('Streamr::CONSOLE') // eslint-disable-line no-console -} +export default async () => { + if (process.env.DEBUG_CONSOLE) { + // Use debug as console log + // This prevents jest messing with console output + // Ensuring debug messages are printed alongside console messages, in the correct order + console.log = Debug('Streamr::CONSOLE') // eslint-disable-line no-console + } -module.exports = async () => { if (!process.env.GIT_VERSION) { const gitRevisionPlugin = new GitRevisionPlugin() const [GIT_VERSION, GIT_COMMITHASH, GIT_BRANCH] = await Promise.all([ From 1147ee56e4d45cf3e2ac0d04ef402a0e0f3262d2 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 15:13:54 -0500 Subject: [PATCH 12/30] Refresh package-lock. --- package-lock.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4005cb40c..9506d4fc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2349,9 +2349,9 @@ } }, "@npmcli/move-file": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.1.tgz", - "integrity": "sha512-LtWTicuF2wp7PNTuyCwABx7nNG+DnzSE8gN0iWxkC6mpgm/iOPu0ZMTkXuCxmJxtWFsDxUaixM9COSNJEMUfuQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "dev": true, "requires": { "mkdirp": "^1.0.4", @@ -5171,9 +5171,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.661", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.661.tgz", - "integrity": "sha512-INNzKoL9ceOpPCpF5J+Fp9AOHY1RegwKViohAyTzV3XbkuRUx04r4v8edsDbevsog8UuL0GvD/Qerr2HwVTlSA==", + "version": "1.3.663", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.663.tgz", + "integrity": "sha512-xkVkzHj6k3oRRGlmdgUCCLSLhtFYHDCTH7SeK+LJdJjnsLcrdbpr8EYmfMQhez3V/KPO5UScSpzQ0feYX6Qoyw==", "dev": true }, "elliptic": { @@ -5804,9 +5804,9 @@ } }, "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz", + "integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==", "dev": true }, "eslint-scope": { @@ -12257,12 +12257,12 @@ "dev": true }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -13155,9 +13155,9 @@ } }, "streamr-test-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/streamr-test-utils/-/streamr-test-utils-1.3.0.tgz", - "integrity": "sha512-mSjtHIUyoVksv6OXVDMj5BecbEXCKfXRmSmiHav1kJLEHj4oE9cF1N0Ml1I3KL3ARw51+RloRnPtRSexg71m/Q==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/streamr-test-utils/-/streamr-test-utils-1.3.1.tgz", + "integrity": "sha512-3ol+bRQOSUz6Qjso0/VDBNzTxD9msizCOtsHgx7YqGYkxtIUSlGbsVtZh3JhBnnm53BNyaNHS74+N7Mjoa+w5Q==", "dev": true }, "string-length": { @@ -13331,9 +13331,9 @@ }, "dependencies": { "ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.0.tgz", + "integrity": "sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", From 3ac9c576e107d0883554207740781dffc7f197c7 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 15:17:48 -0500 Subject: [PATCH 13/30] Fix issues with gaps in resends. Test gap failure mode. Only ~Close OrderMessages stream once messages sent & all gaps filled or failed. --- src/subscribe/OrderMessages.js | 52 +++++++++++++++++++------------- test/integration/GapFill.test.js | 44 ++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index ea72fa02b..5d787b9a4 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -1,7 +1,6 @@ import { Utils } from 'streamr-client-protocol' import { pipeline } from '../utils/iterators' -import { Defer } from '../utils' import PushQueue from '../utils/PushQueue' import { validateOptions } from '../stream/utils' @@ -13,36 +12,29 @@ const { OrderingUtil } = Utils * Wraps OrderingUtil into a pipeline. * Implements gap filling */ - +let ID = 0 export default function OrderMessages(client, options = {}) { - const { gapFillTimeout, retryResendAfter, gapFill = true } = client.options - const { streamId, streamPartition } = validateOptions(options) + const { gapFillTimeout, retryResendAfter } = client.options + const { streamId, streamPartition, gapFill = true } = validateOptions(options) + const debug = client.debug.extend(`OrderMessages::${ID}`) + ID += 1 // output buffer const outStream = new PushQueue([], { - // we can end when: - // input has closed (i.e. all messages sent) - // AND - // no gaps are pending - // AND - // gaps have been filled or failed autoEnd: false, }) let done = false - const inputDone = Defer() - const allHandled = Defer() const resendStreams = new Set() // holds outstanding resends for cleanup const orderingUtil = new OrderingUtil(streamId, streamPartition, (orderedMessage) => { if (!outStream.isWritable() || done) { return } - outStream.push(orderedMessage) }, async (from, to, publisherId, msgChainId) => { if (done || !gapFill) { return } - client.debug('gap %o', { + debug('%d gap %o', { streamId, streamPartition, publisherId, msgChainId, from, to, }) @@ -65,22 +57,42 @@ export default function OrderMessages(client, options = {}) { resendStreams.delete(resendMessageStream) await resendMessageStream.cancel() } - }, gapFillTimeout, retryResendAfter) + }, gapFillTimeout, retryResendAfter, gapFill ? 5 : 0) const markMessageExplicitly = orderingUtil.markMessageExplicitly.bind(orderingUtil) + let inputClosed = false + + function maybeClose() { + // we can close when: + // input has closed (i.e. all messages sent) + // AND + // no gaps are pending + // AND + // gaps have been filled or failed + if (inputClosed && orderingUtil.isEmpty()) { + outStream.end() + } + } + + orderingUtil.on('drain', () => { + maybeClose() + }) + + orderingUtil.on('error', (err) => { + outStream.push(err) + }) + return Object.assign(pipeline([ // eslint-disable-next-line require-yield async function* WriteToOrderingUtil(src) { for await (const msg of src) { - if (!gapFill) { - orderingUtil.markMessageExplicitly(msg) - } - orderingUtil.add(msg) // note no yield // orderingUtil writes to outStream itself } + inputClosed = true + maybeClose() }, outStream, // consumer gets outStream ], async (err) => { @@ -90,8 +102,6 @@ export default function OrderMessages(client, options = {}) { resendStreams.clear() await outStream.cancel(err) orderingUtil.clearGaps() - }, { - end: false, }), { markMessageExplicitly, }) diff --git a/test/integration/GapFill.test.js b/test/integration/GapFill.test.js index 10e1aad57..d0eabcead 100644 --- a/test/integration/GapFill.test.js +++ b/test/integration/GapFill.test.js @@ -204,7 +204,7 @@ describeRepeats('GapFill', () => { expect(client.connection.getState()).toBe('connected') }, 15000) - it.skip('can fill gaps in resends', async () => { + it('can fill gaps in resends', async () => { const { parse } = client.connection let count = 0 client.connection.parse = (...args) => { @@ -239,6 +239,48 @@ describeRepeats('GapFill', () => { expect(client.connection.getState()).toBe('connected') }, 60000) + it('can fill gaps in resends even if gap cannot be filled', async () => { + const { parse } = client.connection + let count = 0 + let droppedMsgRef + client.connection.parse = (...args) => { + const msg = parse.call(client.connection, ...args) + if (!msg.streamMessage) { + return msg + } + + count += 1 + if (count === 3) { + if (!droppedMsgRef) { + droppedMsgRef = msg.streamMessage.getMessageRef() + } + return null + } + + if (droppedMsgRef && msg.streamMessage.getMessageRef().compareTo(droppedMsgRef) === 0) { + return null + } + + return msg + } + + const published = await publishTestMessages(MAX_MESSAGES, { + waitForLast: true, + }) + + const sub = await client.resend({ + stream, + last: MAX_MESSAGES, + }) + const received = [] + for await (const m of sub) { + received.push(m.getParsedContent()) + // should not need to explicitly end + } + expect(received).toEqual(published.filter((_value, index) => index !== 2)) + expect(client.connection.getState()).toBe('connected') + }, 60000) + it('can fill gaps between resend and realtime', async () => { // publish 5 messages into storage const published = await publishTestMessages(5, { From 2ec29639869dc278cbbc0384e545ef2ddfcd86be Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 11 Feb 2021 16:20:22 -0500 Subject: [PATCH 14/30] Ignore failed messages explicitly rather than treat them as unfillable gaps in ordering util. --- src/stream/Encryption.js | 2 +- src/subscribe/Decrypt.js | 17 ++++++-- src/subscribe/OrderMessages.js | 2 +- src/subscribe/pipeline.js | 25 ++++++++++-- test/integration/Encryption.test.js | 61 +++++++++++++++++++++++++++++ test/integration/Validation.test.js | 13 ++++-- 6 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/stream/Encryption.js b/src/stream/Encryption.js index 0726c880c..f682f0c58 100644 --- a/src/stream/Encryption.js +++ b/src/stream/Encryption.js @@ -8,7 +8,7 @@ import { MessageLayer } from 'streamr-client-protocol' import { uuid } from '../utils' -class UnableToDecryptError extends Error { +export class UnableToDecryptError extends Error { constructor(message = '', streamMessage) { super(`Unable to decrypt. ${message} ${util.inspect(streamMessage)}`) this.streamMessage = streamMessage diff --git a/src/subscribe/Decrypt.js b/src/subscribe/Decrypt.js index 6d67c4308..c2c794927 100644 --- a/src/subscribe/Decrypt.js +++ b/src/subscribe/Decrypt.js @@ -1,7 +1,7 @@ import { MessageLayer } from 'streamr-client-protocol' import PushQueue from '../utils/PushQueue' -import EncryptionUtil from '../stream/Encryption' +import EncryptionUtil, { UnableToDecryptError } from '../stream/Encryption' import { SubscriberKeyExchange } from '../stream/KeyExchange' const { StreamMessage } = MessageLayer @@ -26,7 +26,7 @@ export default function Decrypt(client, options = {}) { } }) - async function* decrypt(src) { + async function* decrypt(src, onError = async (err) => { throw err }) { yield* PushQueue.transform(src, async (streamMessage) => { if (!streamMessage.groupKeyId) { return streamMessage @@ -36,8 +36,17 @@ export default function Decrypt(client, options = {}) { return streamMessage } - const groupKey = await requestKey(streamMessage) - await EncryptionUtil.decryptStreamMessage(streamMessage, groupKey) + try { + const groupKey = await requestKey(streamMessage) + if (!groupKey) { + throw new UnableToDecryptError(`Group key not found: ${streamMessage.groupKeyId}`, streamMessage) + } + await EncryptionUtil.decryptStreamMessage(streamMessage, groupKey) + return streamMessage + } catch (err) { + await onError(err, streamMessage) + } + return streamMessage }) } diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index 5d787b9a4..2c1da8e83 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -34,7 +34,7 @@ export default function OrderMessages(client, options = {}) { outStream.push(orderedMessage) }, async (from, to, publisherId, msgChainId) => { if (done || !gapFill) { return } - debug('%d gap %o', { + debug('gap %o', { streamId, streamPartition, publisherId, msgChainId, from, to, }) diff --git a/src/subscribe/pipeline.js b/src/subscribe/pipeline.js index 9c1182c42..de6b8d2b7 100644 --- a/src/subscribe/pipeline.js +++ b/src/subscribe/pipeline.js @@ -42,6 +42,11 @@ export default function MessagePipeline(client, opts = {}, onFinally = async () gapFill: false, }) + // collect messages that fail validation/parsing, do not push out of pipeline + // NOTE: we let failed messages be processed and only removed at end so they don't + // end up acting as gaps that we repeatedly try to fill. + const ignoreMessages = new WeakSet() + const p = pipeline([ // take messages msgStream, @@ -61,21 +66,26 @@ export default function MessagePipeline(client, opts = {}, onFinally = async () try { await validate(streamMessage) } catch (err) { - internalOrderingUtil.markMessageExplicitly(streamMessage) + ignoreMessages.add(streamMessage) await onError(err) } yield streamMessage } }, // decrypt - decrypt, + async function* Parse(src) { + yield* decrypt(src, async (err, streamMessage) => { + ignoreMessages.add(streamMessage) + await onError(err) + }) + }, // parse content async function* Parse(src) { for await (const streamMessage of src) { try { streamMessage.getParsedContent() } catch (err) { - internalOrderingUtil.markMessageExplicitly(streamMessage) + ignoreMessages.add(streamMessage) await onError(err) } yield streamMessage @@ -83,6 +93,15 @@ export default function MessagePipeline(client, opts = {}, onFinally = async () }, // re-order messages (ignore gaps) internalOrderingUtil, + // ignore any failed messages + async function* IgnoreMessages(src) { + for await (const streamMessage of src) { + if (ignoreMessages.has(streamMessage)) { + continue + } + yield streamMessage + } + }, // special handling for bye message async function* ByeMessageSpecialHandling(src) { for await (const orderedMessage of src) { diff --git a/test/integration/Encryption.test.js b/test/integration/Encryption.test.js index 3ac930bd1..7f5d05a49 100644 --- a/test/integration/Encryption.test.js +++ b/test/integration/Encryption.test.js @@ -483,5 +483,66 @@ describe('decryption', () => { expect(received).toEqual(published.slice(-2)) await client.unsubscribe(sub) }, 2 * TIMEOUT) + + it('fails gracefully if cannot decrypt', async () => { + const MAX_MESSAGES = 10 + const groupKey = GroupKey.generate() + const keys = { + [stream.id]: { + [groupKey.id]: groupKey, + } + } + + await client.setNextGroupKey(stream.id, groupKey) + + const BAD_INDEX = 6 + let count = 0 + const { parse } = client.connection + client.connection.parse = (...args) => { + const msg = parse.call(client.connection, ...args) + if (!msg.streamMessage) { + return msg + } + + if (count === BAD_INDEX) { + msg.streamMessage.groupKeyId = 'badgroupkey' + } + + count += 1 + return msg + } + + const sub = await client.subscribe({ + stream: stream.id, + groupKeys: keys, + }) + + const onSubError = jest.fn((err) => { + expect(err).toBeInstanceOf(Error) + expect(err.message).toMatch('decrypt') + }) + + sub.on('error', onSubError) + + // Publish after subscribed + const published = await publishTestMessages(MAX_MESSAGES, { + timestamp: 1111111, + }) + + const received = [] + for await (const m of sub) { + received.push(m.getParsedContent()) + if (received.length === published.length - 1) { + break + } + } + + expect(received).toEqual([ + ...published.slice(0, BAD_INDEX), + ...published.slice(BAD_INDEX + 1, MAX_MESSAGES) + ]) + + expect(onSubError).toHaveBeenCalledTimes(1) + }) }) diff --git a/test/integration/Validation.test.js b/test/integration/Validation.test.js index 54e072497..9951fb4c2 100644 --- a/test/integration/Validation.test.js +++ b/test/integration/Validation.test.js @@ -127,7 +127,9 @@ describeRepeats('Validation', () => { return msg } - const published = await publishTestMessages(MAX_MESSAGES) + const published = await publishTestMessages(MAX_MESSAGES, { + timestamp: 111111, + }) const received = [] for await (const m of sub) { @@ -136,11 +138,13 @@ describeRepeats('Validation', () => { break } } - - expect(received).toEqual([ + const expectedMessages = [ + // remove bad message ...published.slice(0, BAD_INDEX), ...published.slice(BAD_INDEX + 1, MAX_MESSAGES) - ]) + ] + + expect(received).toEqual(expectedMessages) expect(client.connection.getState()).toBe('connected') expect(onSubError).toHaveBeenCalledTimes(1) }, 10000) @@ -184,6 +188,7 @@ describeRepeats('Validation', () => { const published = await publishTestMessages(MAX_MESSAGES, { stream, + timestamp: 1111111, }) const received = [] From 02c4d032901445b1e15254313fe03b4605cb147f Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 11:21:26 -0500 Subject: [PATCH 15/30] Fix handling for failed gapfills. --- src/subscribe/OrderMessages.js | 10 +++++++--- src/subscribe/index.js | 3 ++- src/subscribe/resendStream.js | 2 +- src/utils/PushQueue.js | 5 ++++- test/integration/GapFill.test.js | 4 ++-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index 2c1da8e83..fe06a8ecd 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -8,11 +8,13 @@ import resendStream from './resendStream' const { OrderingUtil } = Utils +let ID = 0 + /** * Wraps OrderingUtil into a pipeline. * Implements gap filling */ -let ID = 0 + export default function OrderMessages(client, options = {}) { const { gapFillTimeout, retryResendAfter } = client.options const { streamId, streamPartition, gapFill = true } = validateOptions(options) @@ -70,6 +72,7 @@ export default function OrderMessages(client, options = {}) { // no gaps are pending // AND // gaps have been filled or failed + // NOTE ordering util cannot have gaps if queue is empty if (inputClosed && orderingUtil.isEmpty()) { outStream.end() } @@ -79,8 +82,9 @@ export default function OrderMessages(client, options = {}) { maybeClose() }) - orderingUtil.on('error', (err) => { - outStream.push(err) + orderingUtil.on('error', () => { + // TODO: handle gapfill errors without closing stream or logging + maybeClose() // probably noop }) return Object.assign(pipeline([ diff --git a/src/subscribe/index.js b/src/subscribe/index.js index db4af5fe1..7ca0385a5 100644 --- a/src/subscribe/index.js +++ b/src/subscribe/index.js @@ -21,6 +21,7 @@ export class Subscription extends Emitter { this.streamPartition = this.options.streamPartition this._onDone = Defer() + this._onDone.catch(() => {}) // prevent unhandledrejection this._onFinally = onFinally const validate = opts.validate || Validator(client, this.options) @@ -157,7 +158,7 @@ class SubscriptionSession extends Emitter { await this.step() } } catch (err) { - this.emit(err) + this.emit('error', err) } } diff --git a/src/subscribe/resendStream.js b/src/subscribe/resendStream.js index 48271b80f..51c51439a 100644 --- a/src/subscribe/resendStream.js +++ b/src/subscribe/resendStream.js @@ -13,7 +13,7 @@ const { ControlMessage } = ControlLayer * Sends resend request, handles responses. */ -export default function resendStream(client, opts = {}, onFinally = () => {}) { +export default function resendStream(client, opts = {}, onFinally = async () => {}) { const options = validateOptions(opts) const { connection } = client const requestId = counterId(`${options.key}-resend`) diff --git a/src/utils/PushQueue.js b/src/utils/PushQueue.js index 9911cae81..adab5e0a8 100644 --- a/src/utils/PushQueue.js +++ b/src/utils/PushQueue.js @@ -279,7 +279,7 @@ export default class PushQueue { continue // eslint-disable-line no-continue } - const value = await new Promise((resolve, reject) => { + const deferred = new Promise((resolve, reject) => { // wait for next push this.nextQueue.push({ resolve, @@ -287,6 +287,9 @@ export default class PushQueue { }) }) + deferred.catch(() => {}) // prevent unhandledrejection + const value = await deferred + // ignore value if finished if (this.finished) { return diff --git a/test/integration/GapFill.test.js b/test/integration/GapFill.test.js index d0eabcead..7cce47c7d 100644 --- a/test/integration/GapFill.test.js +++ b/test/integration/GapFill.test.js @@ -97,8 +97,8 @@ describeRepeats('GapFill', () => { describe('filling gaps', () => { beforeEach(async () => { await setupClient({ - gapFillTimeout: 1000, - retryResendAfter: 1000, + gapFillTimeout: 200, + retryResendAfter: 200, }) await client.connect() }) From 45d6174a523ffb2c3f705129970e301f8bdffb4b Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 14:26:37 -0500 Subject: [PATCH 16/30] Add onError option to Scaffold. --- src/utils/Scaffold.js | 25 +++++++---- test/unit/Scaffold.test.js | 87 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/src/utils/Scaffold.js b/src/utils/Scaffold.js index 67ac3a47a..bf40f8b84 100644 --- a/src/utils/Scaffold.js +++ b/src/utils/Scaffold.js @@ -9,11 +9,13 @@ import AggregatedError from './AggregatedError' * If/when check fails (or there's an error) cleanup functions will be executed in order. * onChange fires when up/down direction changes. * onDone fires when no more up/down steps to execute. + * onError fires when something errors. Rethrow in onError to keep error, don't rethrow to suppress. * returns a function which should be called whenever something changes that could affect the check. */ -export default function Scaffold(sequence = [], _checkFn, { onDone, onChange } = {}) { +export default function Scaffold(sequence = [], _checkFn, { onError, onDone, onChange } = {}) { let error + // ignore error if check fails const nextSteps = sequence.slice().reverse() const prevSteps = [] @@ -23,16 +25,23 @@ export default function Scaffold(sequence = [], _checkFn, { onDone, onChange } = let isDone = false let didStart = false - function onError(err) { - // eslint-disable-next-line require-atomic-updates - error = AggregatedError.from(error, err) + function collectErrors(err) { + try { + if (typeof onError === 'function') { + onError(err) // give option to suppress error + } else { + throw err // rethrow + } + } catch (newErr) { + error = AggregatedError.from(error, newErr) + } } const checkFn = async (...args) => { try { return await _checkFn(...args) } catch (err) { - onError(err, 'in check') + collectErrors(err, 'in check') } return false } @@ -51,7 +60,7 @@ export default function Scaffold(sequence = [], _checkFn, { onDone, onChange } = try { await onChange(shouldUp) } catch (err) { - onError(err) + collectErrors(err) } return next(...args) } @@ -66,7 +75,7 @@ export default function Scaffold(sequence = [], _checkFn, { onDone, onChange } = try { onDownStep = await stepFn(...args) } catch (err) { - onError(err) + collectErrors(err) } onDownSteps.push(onDownStep || (() => {})) return next(...args) @@ -78,7 +87,7 @@ export default function Scaffold(sequence = [], _checkFn, { onDone, onChange } = try { await stepFn() } catch (err) { - onError(err) + collectErrors(err) } nextSteps.push(prevSteps.pop()) return next(...args) diff --git a/test/unit/Scaffold.test.js b/test/unit/Scaffold.test.js index 9e54789e0..24a78b243 100644 --- a/test/unit/Scaffold.test.js +++ b/test/unit/Scaffold.test.js @@ -291,6 +291,93 @@ describe('Scaffold', () => { ]) }) + it('does not error if onError suppresses', async () => { + expect.assertions(2) + const shouldUp = true + + const err = new Error('expected') + const onErrorNoop = jest.fn() + const next = Scaffold([ + async () => { + await up('a') + return () => down('a') // this should throw due to on('next' above + }, + async () => { + await up('b') + return async () => { + await down('b') + } + }, + async () => { + throw err + } + ], () => shouldUp, { + onDone, + onChange, + onError: onErrorNoop, + }) + + await next() + + expect(order).toEqual([ + 'change up', + 'up start a', + 'up end a', + 'up start b', + 'up end b', + 'done up', + ]) + expect(onErrorNoop).toHaveBeenCalledWith(err) + }) + + it('does not error if onError rethrows', async () => { + expect.assertions(3) + const shouldUp = true + + const err = new Error('expected') + const onErrorRethrow = jest.fn((error) => { + throw error + }) + const next = Scaffold([ + async () => { + await up('a') + return () => down('a') // this should throw due to on('next' above + }, + async () => { + await up('b') + return async () => { + await down('b') + } + }, + async () => { + throw err + } + ], () => shouldUp, { + onDone, + onChange, + onError: onErrorRethrow, + }) + + await expect(async () => { + await next() + }).rejects.toThrow(err) + + expect(order).toEqual([ + 'change up', + 'up start a', + 'up end a', + 'up start b', + 'up end b', + 'change down', + 'down start b', + 'down end b', + 'down start a', + 'down end a', + 'done down', + ]) + expect(onErrorRethrow).toHaveBeenCalledWith(err) + }) + it('does nothing if check fails', async () => { const shouldUp = false const next = Scaffold([ From 3e047536e025ed06b839a6755ab05d80566f8b1e Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:15:26 -0500 Subject: [PATCH 17/30] Give each test message a count out of total so it's clear what it is. --- test/utils.js | 97 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/test/utils.js b/test/utils.js index c764cc53f..e467fef87 100644 --- a/test/utils.js +++ b/test/utils.js @@ -137,6 +137,8 @@ export function getPublishTestMessages(client, defaultOpts = {}) { } const publishTestMessagesRaw = async (n = 4, opts = {}) => { + const id = uid('test') + let msgCount = 0 const { streamId, streamPartition = 0, @@ -149,47 +151,76 @@ export function getPublishTestMessages(client, defaultOpts = {}) { afterEach = () => {}, timestamp, partitionKey, - createMessage = Msg, + createMessage = () => { + msgCount += 1 + return { + test: id, + value: `${msgCount} of ${n}` + } + }, } = validateOptions({ ...defaultOpts, ...opts, }) - const published = [] - for (let i = 0; i < n; i++) { - const message = createMessage() - // eslint-disable-next-line no-await-in-loop, no-loop-func - await beforeEach(message) - // eslint-disable-next-line no-await-in-loop, no-loop-func - const request = await pTimeout(client.publish({ - streamId, - streamPartition, - }, message, timestamp, partitionKey), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) - published.push([ - message, - request, - ]) - - // eslint-disable-next-line no-await-in-loop, no-loop-func - await afterEach(message, request) - // eslint-disable-next-line no-await-in-loop, no-loop-func - await wait(delay) // ensure timestamp increments for reliable resend response in test. + let connectionDone = false + function checkDone() { + if (connectionDone) { + throw new Error('Connection done before finished publishing') + } } - - if (waitForLast) { - const msg = published[published.length - 1][1] - await getWaitForStorage(client)(msg, { - streamId, - streamPartition, - timeout: waitForLastTimeout, - count: waitForLastCount, - messageMatchFn(m, b) { - return m.streamMessage.signature === b.signature - } - }) + const onDone = () => { + connectionDone = true } + try { + client.connection.once('done', onDone) + + const published = [] + for (let i = 0; i < n; i++) { + checkDone() + const message = createMessage() + // eslint-disable-next-line no-await-in-loop, no-loop-func + await beforeEach(message) + checkDone() + // eslint-disable-next-line no-await-in-loop, no-loop-func + const request = await pTimeout(client.publish({ + streamId, + streamPartition, + }, message, timestamp, partitionKey), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) + checkDone() + published.push([ + message, + request, + ]) + + // eslint-disable-next-line no-await-in-loop, no-loop-func + await afterEach(message, request) + checkDone() + // eslint-disable-next-line no-await-in-loop, no-loop-func + await wait(delay) // ensure timestamp increments for reliable resend response in test. + checkDone() + } - return published + checkDone() + + if (waitForLast) { + const msg = published[published.length - 1][1] + await getWaitForStorage(client)(msg, { + streamId, + streamPartition, + timeout: waitForLastTimeout, + count: waitForLastCount, + messageMatchFn(m, b) { + checkDone() + return m.streamMessage.signature === b.signature + } + }) + } + + return published + } finally { + client.connection.off('done', onDone) + } } const publishTestMessages = async (...args) => { From d1772051660b119e5063061ed9ce58f3939334c1 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:16:58 -0500 Subject: [PATCH 18/30] Exclude gap related tests from 'npm run test-integration-no-resend' --- package.json | 2 +- test/integration/GapFill.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1ec726854..13f445871 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "test-unit": "jest test/unit --detectOpenHandles", "coverage": "jest --coverage", "test-integration": "jest --forceExit test/integration", - "test-integration-no-resend": "jest --testTimeout=10000 --testPathIgnorePatterns='resend|Resend' --testNamePattern='^((?!(resend|Resend|resent|Resent)).)*$' test/integration/*.test.js", + "test-integration-no-resend": "jest --testTimeout=10000 --testPathIgnorePatterns='resend|Resend' --testNamePattern='^((?!(resend|Resend|resent|Resent|gap|Gap)).)*$' test/integration/*.test.js", "test-integration-resend": "jest --testTimeout=15000 --testNamePattern='(resend|Resend|resent|Resent)' test/integration/*.test.js", "test-integration-dataunions": "jest --testTimeout=15000 --runInBand test/integration/DataUnionEndpoints", "test-flakey": "jest --forceExit test/flakey/*", diff --git a/test/integration/GapFill.test.js b/test/integration/GapFill.test.js index 7cce47c7d..4271b30c8 100644 --- a/test/integration/GapFill.test.js +++ b/test/integration/GapFill.test.js @@ -8,7 +8,7 @@ import config from './config' const MAX_MESSAGES = 10 -describeRepeats('GapFill', () => { +describeRepeats('GapFill with resends', () => { let expectErrors = 0 // check no errors by default let publishTestMessages let onError = jest.fn() From a6f10554a5ea93d907a0c2cc549f149546de63cf Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:17:41 -0500 Subject: [PATCH 19/30] Concatenate error messages with passed-in message in AggregatedError. --- src/utils/AggregatedError.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/AggregatedError.js b/src/utils/AggregatedError.js index d95a2baf6..3da55c1c8 100644 --- a/src/utils/AggregatedError.js +++ b/src/utils/AggregatedError.js @@ -37,14 +37,13 @@ export default class AggregatedError extends Error { */ static from(oldErr, newErr, msg) { + if (newErr && msg) { + // copy message + newErr.message = `${msg}: ${newErr.message}` // eslint-disable-line no-param-reassign + } switch (true) { // When no oldErr, just return newErr case !oldErr: { - if (msg) { - // copy message - newErr.message = msg // eslint-disable-line no-param-reassign - } - return newErr } // When oldErr is an AggregatedError, extend it From 9b2cf3955f3b67ec5baf00abdeca31d1277f2aa0 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:18:12 -0500 Subject: [PATCH 20/30] Tune collecting of subscription errors. --- src/subscribe/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/subscribe/index.js b/src/subscribe/index.js index 7ca0385a5..879640092 100644 --- a/src/subscribe/index.js +++ b/src/subscribe/index.js @@ -107,17 +107,17 @@ export class Subscription extends Emitter { */ function multiEmit(emitters, ...args) { - const errs = [] + let error emitters.forEach((s) => { try { s.emit(...args) } catch (err) { - errs.push(err) + AggregatedError.from(error, err, `Error emitting event: ${args[0]}`) } }) - if (errs.length) { - throw new AggregatedError(errs, `Error emitting event: ${args[0]}`) + if (error) { + throw error } } From f523e57f0c8bc234177291ad8719e407d45ec68f Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:20:05 -0500 Subject: [PATCH 21/30] Ignore connection errors if should be disconnected during subscription scaffold. --- src/Connection.js | 2 +- src/subscribe/index.js | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Connection.js b/src/Connection.js index b84da2817..d804ec041 100644 --- a/src/Connection.js +++ b/src/Connection.js @@ -11,7 +11,7 @@ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) // add global support for pretty millisecond formatting with %n Debug.formatters.n = (v) => Debug.humanize(v) -class ConnectionError extends Error { +export class ConnectionError extends Error { constructor(err, ...args) { if (err instanceof ConnectionError) { return err diff --git a/src/subscribe/index.js b/src/subscribe/index.js index 879640092..93cab50e5 100644 --- a/src/subscribe/index.js +++ b/src/subscribe/index.js @@ -3,6 +3,7 @@ import Emitter from 'events' import { allSettledValues, AggregatedError, Scaffold, Defer, counterId } from '../utils' import { pipeline } from '../utils/iterators' import { validateOptions } from '../stream/utils' +import { ConnectionError } from '../Connection' import { subscribe, unsubscribe } from './api' import MessagePipeline from './pipeline' @@ -163,6 +164,14 @@ class SubscriptionSession extends Emitter { } let deleted = new Set() + const check = () => { + return ( + connection.isConnectionValid() + && !needsReset + // has some active subscription + && this.count() + ) + } this.step = Scaffold([ () => { @@ -220,12 +229,16 @@ class SubscriptionSession extends Emitter { await unsubscribe(this.client, this.options) } } - ], () => ( - connection.isConnectionValid() - && !needsReset - // has some active subscription - && this.count() - )) + ], check, { + onError(err) { + if (err instanceof ConnectionError && !check()) { + // ignore error if state changed + needsReset = true + return + } + throw err + } + }) } has(sub) { From 5354b9e3c3d79b35f6ae97ab9427d9cab502eec4 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Fri, 12 Feb 2021 15:25:37 -0500 Subject: [PATCH 22/30] Add another variant of flakey disconnect/reconnect subscribe test. --- test/integration/MultipleClients.test.js | 140 ++++++++++++++++++++++- test/integration/StreamrClient.test.js | 17 +-- 2 files changed, 145 insertions(+), 12 deletions(-) diff --git a/test/integration/MultipleClients.test.js b/test/integration/MultipleClients.test.js index 56bce9cad..c54fa7898 100644 --- a/test/integration/MultipleClients.test.js +++ b/test/integration/MultipleClients.test.js @@ -1,12 +1,15 @@ import { wait } from 'streamr-test-utils' +import { ControlLayer } from 'streamr-client-protocol' import { describeRepeats, uid, fakePrivateKey, getWaitForStorage, getPublishTestMessages, addAfterFn } from '../utils' import StreamrClient from '../../src/StreamrClient' -import { counterId } from '../../src/utils' +import { counterId, Defer, pLimitFn } from '../../src/utils' import Connection from '../../src/Connection' import config from './config' +const { ControlMessage } = ControlLayer + const createClient = (opts = {}) => new StreamrClient({ ...config.clientOptions, auth: { @@ -14,9 +17,13 @@ const createClient = (opts = {}) => new StreamrClient({ }, autoConnect: false, autoDisconnect: false, + // disconnectDelay: 1, + // publishAutoDisconnectDelay: 1, ...opts, }) +const MAX_MESSAGES = 10 + describeRepeats('PubSub with multiple clients', () => { let stream let mainClient @@ -101,9 +108,134 @@ describeRepeats('PubSub with multiple clients', () => { expect(receivedMessagesOther).toEqual([message]) }, 30000) - describe('multiple publishers', () => { - const MAX_MESSAGES = 10 + test('can get messages published from other client if subscriber disconnects', async () => { + otherClient = createClient({ + auth: { + privateKey + } + }) + otherClient.on('error', getOnError(errors)) + await otherClient.connect() + await mainClient.connect() + + const receivedMessagesOther = [] + const msgs = receivedMessagesOther + const otherDone = Defer() + // subscribe to stream from other client instance + await otherClient.subscribe({ + stream: stream.id, + }, (msg) => { + otherClient.debug('other', msg.value) + receivedMessagesOther.push(msg) + + if (receivedMessagesOther.length === MAX_MESSAGES) { + otherDone.resolve() + } + }) + + const disconnect = pLimitFn(async () => { + if (msgs.length === MAX_MESSAGES) { return } + otherClient.debug('disconnecting...', msgs.length) + otherClient.connection.socket.close() + // wait for reconnection before possibly disconnecting again + await otherClient.nextConnection() + otherClient.debug('reconnected...', msgs.length) + }) + + const onConnectionMessage = jest.fn(() => { + // disconnect after every message + disconnect() + }) + + otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) + otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) + + const publishTestMessages = getPublishTestMessages(mainClient, { + stream, + delay: 600, + waitForLast: true, + waitForLastTimeout: 10000, + waitForLastCount: MAX_MESSAGES, + }) + + const published = await publishTestMessages(MAX_MESSAGES) + await otherDone + + expect(receivedMessagesOther).toEqual(published) + }, 30000) + + test('can get messages published from other client if subscriber disconnects & publisher is also subscribed', async () => { + otherClient = createClient({ + auth: { + privateKey + } + }) + otherClient.on('error', getOnError(errors)) + await otherClient.connect() + await mainClient.connect() + + const receivedMessagesOther = [] + const msgs = receivedMessagesOther + const receivedMessagesMain = [] + const mainDone = Defer() + const otherDone = Defer() + // subscribe to stream from other client instance + await otherClient.subscribe({ + stream: stream.id, + }, (msg) => { + otherClient.debug('other', msg.value) + receivedMessagesOther.push(msg) + + if (receivedMessagesOther.length === MAX_MESSAGES) { + otherDone.resolve() + } + }) + + const disconnect = pLimitFn(async () => { + if (msgs.length === MAX_MESSAGES) { return } + otherClient.debug('disconnecting...', msgs.length) + otherClient.connection.socket.close() + // wait for reconnection before possibly disconnecting again + await otherClient.nextConnection() + otherClient.debug('reconnected...', msgs.length) + }) + const onConnectionMessage = jest.fn(() => { + // disconnect after every message + disconnect() + }) + + otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) + otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) + // subscribe to stream from main client instance + await mainClient.subscribe({ + stream: stream.id, + }, (msg) => { + mainClient.debug('main', msg.value) + receivedMessagesMain.push(msg) + if (receivedMessagesMain.length === MAX_MESSAGES) { + mainDone.resolve() + } + }) + + const publishTestMessages = getPublishTestMessages(mainClient, { + stream, + delay: 600, + waitForLast: true, + waitForLastTimeout: 10000, + waitForLastCount: MAX_MESSAGES, + }) + const published = await publishTestMessages(MAX_MESSAGES) + + await otherDone + await mainDone + + // messages should arrive on both clients? + expect(receivedMessagesMain).toEqual(published) + expect(receivedMessagesOther).toEqual(published) + }, 30000) + + describe('multiple publishers', () => { async function createPublisher() { const pubClient = createClient({ auth: { @@ -271,7 +403,7 @@ describeRepeats('PubSub with multiple clients', () => { }) published[publisherId] = await publishTestMessages(MAX_MESSAGES, { - async afterEach(pubMsg, req) { + async afterEach(_pubMsg, req) { counter += 1 if (counter === 3) { // late subscribe to stream from other client instance diff --git a/test/integration/StreamrClient.test.js b/test/integration/StreamrClient.test.js index 407977ad7..9934cd321 100644 --- a/test/integration/StreamrClient.test.js +++ b/test/integration/StreamrClient.test.js @@ -7,7 +7,7 @@ import { wait, waitForEvent } from 'streamr-test-utils' import { describeRepeats, uid, fakePrivateKey, getWaitForStorage, getPublishTestMessages, Msg } from '../utils' import StreamrClient from '../../src/StreamrClient' -import { Defer, pLimitFn } from '../../src/utils' +import { Defer, pLimitFn, pTimeout } from '../../src/utils' import Connection from '../../src/Connection' import config from './config' @@ -17,7 +17,7 @@ const WebSocket = require('ws') const { StreamMessage } = MessageLayer const { SubscribeRequest, UnsubscribeRequest, ResendLastRequest, ControlMessage } = ControlLayer -const MAX_MESSAGES = 5 +const MAX_MESSAGES = 20 describeRepeats('StreamrClient', () => { let expectErrors = 0 // check no errors by default @@ -38,8 +38,8 @@ describeRepeats('StreamrClient', () => { }, autoConnect: false, autoDisconnect: false, - disconnectDelay: 1, - publishAutoDisconnectDelay: 50, + // disconnectDelay: 500, + // publishAutoDisconnectDelay: 250, maxRetries: 2, ...opts, }) @@ -914,6 +914,7 @@ describeRepeats('StreamrClient', () => { }) afterEach(async () => { + otherClient.debug('disconnecting after test') const tasks = [ otherClient.disconnect(), client.disconnect(), @@ -924,12 +925,12 @@ describeRepeats('StreamrClient', () => { it('should work', async () => { const done = Defer() - const msgs = [] await otherClient.subscribe(stream, (msg) => { msgs.push(msg) + otherClient.debug('got msg %d of %d', msgs.length, MAX_MESSAGES) if (msgs.length === MAX_MESSAGES) { // should eventually get here done.resolve() @@ -938,9 +939,11 @@ describeRepeats('StreamrClient', () => { const disconnect = pLimitFn(async () => { if (msgs.length === MAX_MESSAGES) { return } + otherClient.debug('disconnecting...', msgs.length) otherClient.connection.socket.close() // wait for reconnection before possibly disconnecting again await otherClient.nextConnection() + otherClient.debug('reconnected...', msgs.length) }) const onConnectionMessage = jest.fn(() => { @@ -955,11 +958,9 @@ describeRepeats('StreamrClient', () => { const onDisconnected = jest.fn() otherClient.connection.on('connected', onConnected) otherClient.connection.on('disconnected', onDisconnected) - const published = await publishTestMessages(MAX_MESSAGES, { - delay: 1000, + delay: 600, }) - await done // wait for final re-connection after final message await otherClient.connection.nextConnection() From 46c3a3520516830796bc5d858633c71a427f76df Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Tue, 16 Feb 2021 11:10:15 -0500 Subject: [PATCH 23/30] Make test/utils getPublishTestMessages timestamp option into optional function. --- test/utils.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/utils.js b/test/utils.js index e467fef87..b8f6fe981 100644 --- a/test/utils.js +++ b/test/utils.js @@ -176,30 +176,28 @@ export function getPublishTestMessages(client, defaultOpts = {}) { client.connection.once('done', onDone) const published = [] + /* eslint-disable no-await-in-loop, no-loop-func */ for (let i = 0; i < n; i++) { checkDone() const message = createMessage() - // eslint-disable-next-line no-await-in-loop, no-loop-func await beforeEach(message) checkDone() - // eslint-disable-next-line no-await-in-loop, no-loop-func const request = await pTimeout(client.publish({ streamId, streamPartition, - }, message, timestamp, partitionKey), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) + }, message, typeof timestamp === 'function' ? timestamp() : timestamp, partitionKey), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) checkDone() published.push([ message, request, ]) - // eslint-disable-next-line no-await-in-loop, no-loop-func await afterEach(message, request) checkDone() - // eslint-disable-next-line no-await-in-loop, no-loop-func await wait(delay) // ensure timestamp increments for reliable resend response in test. checkDone() } + /* eslint-enable no-await-in-loop, no-loop-func */ checkDone() From 106d57bce39adc69731ce2dd846e1c97f8fef683 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Tue, 16 Feb 2021 11:10:36 -0500 Subject: [PATCH 24/30] Rename pipeline steps. --- src/subscribe/pipeline.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/subscribe/pipeline.js b/src/subscribe/pipeline.js index de6b8d2b7..3e45d7c4b 100644 --- a/src/subscribe/pipeline.js +++ b/src/subscribe/pipeline.js @@ -61,7 +61,7 @@ export default function MessagePipeline(client, opts = {}, onFinally = async () // order messages (fill gaps) orderingUtil, // validate - async function* Validate(src) { + async function* ValidateMessages(src) { for await (const streamMessage of src) { try { await validate(streamMessage) @@ -73,14 +73,14 @@ export default function MessagePipeline(client, opts = {}, onFinally = async () } }, // decrypt - async function* Parse(src) { + async function* DecryptMessages(src) { yield* decrypt(src, async (err, streamMessage) => { ignoreMessages.add(streamMessage) await onError(err) }) }, // parse content - async function* Parse(src) { + async function* ParseMessages(src) { for await (const streamMessage of src) { try { streamMessage.getParsedContent() From 3517ef0f72a96f6dc798c194a4b8b241447a7cca Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Tue, 16 Feb 2021 11:13:47 -0500 Subject: [PATCH 25/30] Update flakey disconnect/reconnect subscribe test. --- test/integration/MultipleClients.test.js | 318 ++++++++++++----------- 1 file changed, 171 insertions(+), 147 deletions(-) diff --git a/test/integration/MultipleClients.test.js b/test/integration/MultipleClients.test.js index c54fa7898..68d32f682 100644 --- a/test/integration/MultipleClients.test.js +++ b/test/integration/MultipleClients.test.js @@ -72,168 +72,192 @@ describeRepeats('PubSub with multiple clients', () => { } }) - test('can get messages published from other client', async () => { - otherClient = createClient({ - auth: { - privateKey - } - }) - otherClient.on('error', getOnError(errors)) - await otherClient.connect() - await mainClient.connect() - - const receivedMessagesOther = [] - const receivedMessagesMain = [] - // subscribe to stream from other client instance - await otherClient.subscribe({ - stream: stream.id, - }, (msg) => { - receivedMessagesOther.push(msg) - }) - // subscribe to stream from main client instance - await mainClient.subscribe({ - stream: stream.id, - }, (msg) => { - receivedMessagesMain.push(msg) - }) - const message = { - msg: uid('message'), - } - await wait(5000) - // publish message on main client - await mainClient.publish(stream, message) - await wait(5000) - // messages should arrive on both clients? - expect(receivedMessagesMain).toEqual([message]) - expect(receivedMessagesOther).toEqual([message]) - }, 30000) - - test('can get messages published from other client if subscriber disconnects', async () => { - otherClient = createClient({ - auth: { - privateKey - } - }) - otherClient.on('error', getOnError(errors)) - await otherClient.connect() - await mainClient.connect() + describe('can get messages published from other client', () => { + test('it works', async () => { + otherClient = createClient({ + auth: { + privateKey + } + }) + otherClient.on('error', getOnError(errors)) + await otherClient.connect() + await mainClient.connect() - const receivedMessagesOther = [] - const msgs = receivedMessagesOther - const otherDone = Defer() - // subscribe to stream from other client instance - await otherClient.subscribe({ - stream: stream.id, - }, (msg) => { - otherClient.debug('other', msg.value) - receivedMessagesOther.push(msg) - - if (receivedMessagesOther.length === MAX_MESSAGES) { - otherDone.resolve() + const receivedMessagesOther = [] + const receivedMessagesMain = [] + // subscribe to stream from other client instance + await otherClient.subscribe({ + stream: stream.id, + }, (msg) => { + receivedMessagesOther.push(msg) + }) + // subscribe to stream from main client instance + await mainClient.subscribe({ + stream: stream.id, + }, (msg) => { + receivedMessagesMain.push(msg) + }) + const message = { + msg: uid('message'), } - }) + await wait(5000) + // publish message on main client + await mainClient.publish(stream, message) + await wait(5000) + // messages should arrive on both clients? + expect(receivedMessagesMain).toEqual([message]) + expect(receivedMessagesOther).toEqual([message]) + }, 30000) + + describe('subscriber disconnects after each message', () => { + test('single subscriber', async () => { + const maxMessages = MAX_MESSAGES + Math.floor(Math.random() * MAX_MESSAGES * 0.25) + otherClient = createClient({ + auth: { + privateKey + } + }) + otherClient.on('error', getOnError(errors)) + await otherClient.connect() + await mainClient.connect() + + const receivedMessagesOther = [] + const msgs = receivedMessagesOther + const otherDone = Defer() + // subscribe to stream from other client instance + await otherClient.subscribe({ + stream: stream.id, + }, (msg) => { + otherClient.debug('other', msg.value) + receivedMessagesOther.push(msg) + + if (receivedMessagesOther.length === maxMessages) { + otherDone.resolve() + } + }) + let disconnecting = false + const disconnect = async () => { + if (msgs.length === maxMessages) { return } + + if (disconnecting) { return } + disconnecting = true + otherClient.debug('disconnecting...', msgs.length) + otherClient.connection.socket.close() + // wait for reconnection before possibly disconnecting again + try { + await otherClient.nextConnection() + otherClient.debug('reconnected...', msgs.length) + } finally { + // eslint-disable-next-line require-atomic-updates + disconnecting = false + } + } - const disconnect = pLimitFn(async () => { - if (msgs.length === MAX_MESSAGES) { return } - otherClient.debug('disconnecting...', msgs.length) - otherClient.connection.socket.close() - // wait for reconnection before possibly disconnecting again - await otherClient.nextConnection() - otherClient.debug('reconnected...', msgs.length) - }) + const onConnectionMessage = jest.fn(() => { + // disconnect after every message + disconnect() + }) - const onConnectionMessage = jest.fn(() => { - // disconnect after every message - disconnect() - }) + otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) + otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) + let t = 0 + const publishTestMessages = getPublishTestMessages(mainClient, { + stream, + delay: 600, + timestamp: () => { + t += 1 + return t + }, + waitForLast: true, + waitForLastTimeout: 10000, + waitForLastCount: maxMessages, + }) - otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) - otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) + const published = await publishTestMessages(maxMessages) + await otherDone - const publishTestMessages = getPublishTestMessages(mainClient, { - stream, - delay: 600, - waitForLast: true, - waitForLastTimeout: 10000, - waitForLastCount: MAX_MESSAGES, - }) + expect(receivedMessagesOther).toEqual(published) + }, 30000) - const published = await publishTestMessages(MAX_MESSAGES) - await otherDone + test('publisher also subscriber', async () => { + const maxMessages = MAX_MESSAGES + Math.floor(Math.random() * MAX_MESSAGES * 0.25) + otherClient = createClient({ + auth: { + privateKey + } + }) + otherClient.on('error', getOnError(errors)) + await otherClient.connect() + await mainClient.connect() + + const receivedMessagesOther = [] + const msgs = receivedMessagesOther + const receivedMessagesMain = [] + const mainDone = Defer() + const otherDone = Defer() + // subscribe to stream from other client instance + await otherClient.subscribe({ + stream: stream.id, + }, (msg) => { + otherClient.debug('other', msg.value) + receivedMessagesOther.push(msg) + + if (receivedMessagesOther.length === maxMessages) { + otherDone.resolve() + } + }) - expect(receivedMessagesOther).toEqual(published) - }, 30000) + const disconnect = pLimitFn(async () => { + if (msgs.length === maxMessages) { return } + otherClient.debug('disconnecting...', msgs.length) + otherClient.connection.socket.close() + // wait for reconnection before possibly disconnecting again + await otherClient.nextConnection() + otherClient.debug('reconnected...', msgs.length) + }) - test('can get messages published from other client if subscriber disconnects & publisher is also subscribed', async () => { - otherClient = createClient({ - auth: { - privateKey - } - }) - otherClient.on('error', getOnError(errors)) - await otherClient.connect() - await mainClient.connect() + const onConnectionMessage = jest.fn(() => { + // disconnect after every message + disconnect() + }) - const receivedMessagesOther = [] - const msgs = receivedMessagesOther - const receivedMessagesMain = [] - const mainDone = Defer() - const otherDone = Defer() - // subscribe to stream from other client instance - await otherClient.subscribe({ - stream: stream.id, - }, (msg) => { - otherClient.debug('other', msg.value) - receivedMessagesOther.push(msg) - - if (receivedMessagesOther.length === MAX_MESSAGES) { - otherDone.resolve() - } - }) + otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) + otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) + // subscribe to stream from main client instance + await mainClient.subscribe({ + stream: stream.id, + }, (msg) => { + mainClient.debug('main', msg.value) + receivedMessagesMain.push(msg) + if (receivedMessagesMain.length === maxMessages) { + mainDone.resolve() + } + }) - const disconnect = pLimitFn(async () => { - if (msgs.length === MAX_MESSAGES) { return } - otherClient.debug('disconnecting...', msgs.length) - otherClient.connection.socket.close() - // wait for reconnection before possibly disconnecting again - await otherClient.nextConnection() - otherClient.debug('reconnected...', msgs.length) - }) + let t = 0 - const onConnectionMessage = jest.fn(() => { - // disconnect after every message - disconnect() - }) + const publishTestMessages = getPublishTestMessages(mainClient, { + stream, + delay: 600, + waitForLast: true, + waitForLastTimeout: 10000, + waitForLastCount: maxMessages, + timestamp: () => { + t += 1 + return t + }, + }) + const published = await publishTestMessages(maxMessages) - otherClient.connection.on(ControlMessage.TYPES.BroadcastMessage, onConnectionMessage) - otherClient.connection.on(ControlMessage.TYPES.UnicastMessage, onConnectionMessage) - // subscribe to stream from main client instance - await mainClient.subscribe({ - stream: stream.id, - }, (msg) => { - mainClient.debug('main', msg.value) - receivedMessagesMain.push(msg) - if (receivedMessagesMain.length === MAX_MESSAGES) { - mainDone.resolve() - } - }) + await otherDone + await mainDone - const publishTestMessages = getPublishTestMessages(mainClient, { - stream, - delay: 600, - waitForLast: true, - waitForLastTimeout: 10000, - waitForLastCount: MAX_MESSAGES, + // messages should arrive on both clients? + expect(receivedMessagesMain).toEqual(published) + expect(receivedMessagesOther).toEqual(published) + }, 30000) }) - const published = await publishTestMessages(MAX_MESSAGES) - - await otherDone - await mainDone - - // messages should arrive on both clients? - expect(receivedMessagesMain).toEqual(published) - expect(receivedMessagesOther).toEqual(published) - }, 30000) + }) describe('multiple publishers', () => { async function createPublisher() { From 55c2b5135e24286b6da1ef0db247d1be1ea672ba Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Tue, 16 Feb 2021 16:12:40 -0500 Subject: [PATCH 26/30] Use streamr-client-protocol@8.0.0-beta.2. --- package-lock.json | 15 +++++++++++---- package.json | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9506d4fc3..c3aaca2f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13141,17 +13141,19 @@ "dev": true }, "streamr-client-protocol": { - "version": "8.0.0-beta.1", - "resolved": "https://registry.npmjs.org/streamr-client-protocol/-/streamr-client-protocol-8.0.0-beta.1.tgz", - "integrity": "sha512-sxkn5ef/sqCo6hC/wy2f81hy7ONR/8aYW+H6TlQ2tltwT/q983QBmGtOW2Y4mT0I2QX10f6NDUIlfluemydsyA==", + "version": "8.0.0-beta.2", + "resolved": "https://registry.npmjs.org/streamr-client-protocol/-/streamr-client-protocol-8.0.0-beta.2.tgz", + "integrity": "sha512-TwNkaBIQCyarkkTxpQuMBCu7Uqs0sH5CUK+/g6751J3ZL7VenZgv/dPRuQaatFDP5LboyRTo/qJxD+75TBfZBw==", "requires": { + "@babel/runtime-corejs3": "^7.12.13", "debug": "^4.3.1", "ethers": "^5.0.24", "eventemitter3": "^4.0.7", "heap": "^0.2.6", "promise-memoize": "^1.2.1", "secp256k1": "^4.0.2", - "sha3": "^2.1.3" + "sha3": "^2.1.3", + "strict-event-emitter-types": "^2.0.0" } }, "streamr-test-utils": { @@ -13160,6 +13162,11 @@ "integrity": "sha512-3ol+bRQOSUz6Qjso0/VDBNzTxD9msizCOtsHgx7YqGYkxtIUSlGbsVtZh3JhBnnm53BNyaNHS74+N7Mjoa+w5Q==", "dev": true }, + "strict-event-emitter-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", + "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" + }, "string-length": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", diff --git a/package.json b/package.json index 13f445871..c7ce34140 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "qs": "^6.9.6", "quick-lru": "^5.1.1", "readable-stream": "^3.6.0", - "streamr-client-protocol": "^8.0.0-beta.1", + "streamr-client-protocol": "^8.0.0-beta.2", "typescript": "^4.1.4", "uuid": "^8.3.2", "webpack-node-externals": "^2.5.2", From cb878f743c455280b296eb5654f72a828ce28ef9 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Tue, 16 Feb 2021 16:28:25 -0500 Subject: [PATCH 27/30] Linting --- src/StreamrClient.ts | 7 +++---- test/integration/StreamrClient.test.js | 2 +- test/utils.js | 10 ++++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/StreamrClient.ts b/src/StreamrClient.ts index 30d521ec0..f6060c3a8 100644 --- a/src/StreamrClient.ts +++ b/src/StreamrClient.ts @@ -1,5 +1,4 @@ import EventEmitter from 'eventemitter3' -// @ts-expect-error import { ControlLayer } from 'streamr-client-protocol' import Debug from 'debug' @@ -8,7 +7,7 @@ import { validateOptions } from './stream/utils' import Config from './Config' import StreamrEthereum from './Ethereum' import Session from './Session' -import Connection from './Connection' +import Connection, { ConnectionError } from './Connection' import Publisher from './publish' import Subscriber from './subscribe' import { getUserId } from './user' @@ -237,12 +236,12 @@ export default class StreamrClient extends EventEmitter { } onConnectionError(err: Todo) { - this.emit('error', new Connection.ConnectionError(err)) + this.emit('error', new ConnectionError(err)) } getErrorEmitter(source: Todo) { return (err: Todo) => { - if (!(err instanceof Connection.ConnectionError || err.reason instanceof Connection.ConnectionError)) { + if (!(err instanceof ConnectionError || err.reason instanceof ConnectionError)) { // emit non-connection errors this.emit('error', err) } else { diff --git a/test/integration/StreamrClient.test.js b/test/integration/StreamrClient.test.js index 9934cd321..77e3017ee 100644 --- a/test/integration/StreamrClient.test.js +++ b/test/integration/StreamrClient.test.js @@ -7,7 +7,7 @@ import { wait, waitForEvent } from 'streamr-test-utils' import { describeRepeats, uid, fakePrivateKey, getWaitForStorage, getPublishTestMessages, Msg } from '../utils' import StreamrClient from '../../src/StreamrClient' -import { Defer, pLimitFn, pTimeout } from '../../src/utils' +import { Defer, pLimitFn } from '../../src/utils' import Connection from '../../src/Connection' import config from './config' diff --git a/test/utils.js b/test/utils.js index b8f6fe981..15dbd7632 100644 --- a/test/utils.js +++ b/test/utils.js @@ -182,10 +182,12 @@ export function getPublishTestMessages(client, defaultOpts = {}) { const message = createMessage() await beforeEach(message) checkDone() - const request = await pTimeout(client.publish({ - streamId, - streamPartition, - }, message, typeof timestamp === 'function' ? timestamp() : timestamp, partitionKey), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) + const request = await pTimeout(client.publish( + { streamId, streamPartition }, + message, + typeof timestamp === 'function' ? timestamp() : timestamp, + partitionKey + ), timeout, `publish timeout ${streamId}: ${i} ${inspect(message)}`) checkDone() published.push([ message, From cd45f51c9fd08032e1888b64becdf46f2c0dad81 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Wed, 17 Feb 2021 10:07:04 -0500 Subject: [PATCH 28/30] Line-length linting. --- src/Session.ts | 16 ++++++++++++---- src/rest/DataUnionEndpoints.ts | 7 ++++++- src/rest/StreamEndpoints.ts | 9 +++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Session.ts b/src/Session.ts index f9522f131..c3cd2397f 100644 --- a/src/Session.ts +++ b/src/Session.ts @@ -44,15 +44,23 @@ export default class Session extends EventEmitter { // TODO: move loginFunction to StreamrClient constructor where "auth type" is checked if (typeof this.options.privateKey !== 'undefined') { const wallet = new Wallet(this.options.privateKey) - this.loginFunction = async () => this._client.loginEndpoints.loginWithChallengeResponse((d: string) => wallet.signMessage(d), wallet.address) + this.loginFunction = async () => ( + this._client.loginEndpoints.loginWithChallengeResponse((d: string) => wallet.signMessage(d), wallet.address) + ) } else if (typeof this.options.ethereum !== 'undefined') { const provider = new Web3Provider(this.options.ethereum) const signer = provider.getSigner() - this.loginFunction = async () => this._client.loginEndpoints.loginWithChallengeResponse((d: string) => signer.signMessage(d), await signer.getAddress()) + this.loginFunction = async () => ( + this._client.loginEndpoints.loginWithChallengeResponse((d: string) => signer.signMessage(d), await signer.getAddress()) + ) } else if (typeof this.options.apiKey !== 'undefined') { - this.loginFunction = async () => this._client.loginEndpoints.loginWithApiKey(this.options.apiKey!) + this.loginFunction = async () => ( + this._client.loginEndpoints.loginWithApiKey(this.options.apiKey!) + ) } else if (typeof this.options.username !== 'undefined' && typeof this.options.password !== 'undefined') { - this.loginFunction = async () => this._client.loginEndpoints.loginWithUsernamePassword(this.options.username!, this.options.password!) + this.loginFunction = async () => ( + this._client.loginEndpoints.loginWithUsernamePassword(this.options.username!, this.options.password!) + ) } else { if (!this.options.sessionToken) { this.options.unauthenticated = true diff --git a/src/rest/DataUnionEndpoints.ts b/src/rest/DataUnionEndpoints.ts index 697a2347c..73c3bfa72 100644 --- a/src/rest/DataUnionEndpoints.ts +++ b/src/rest/DataUnionEndpoints.ts @@ -433,7 +433,12 @@ async function transportSignatures(client: StreamrClient, messageHash: Todo, opt // template for withdraw functions // client could be replaced with AMB (mainnet and sidechain) -async function untilWithdrawIsComplete(client: StreamrClient, getWithdrawTxFunc: (options: DataUnionOptions) => Todo, getBalanceFunc: (options: DataUnionOptions) => Todo, options: DataUnionOptions = {}) { +async function untilWithdrawIsComplete( + client: StreamrClient, + getWithdrawTxFunc: (options: DataUnionOptions) => Todo, + getBalanceFunc: (options: DataUnionOptions) => Todo, + options: DataUnionOptions = {} +) { const { pollingIntervalMs = 1000, retryTimeoutMs = 60000, diff --git a/src/rest/StreamEndpoints.ts b/src/rest/StreamEndpoints.ts index 95a8071f8..f56e6349c 100644 --- a/src/rest/StreamEndpoints.ts +++ b/src/rest/StreamEndpoints.ts @@ -217,11 +217,12 @@ export class StreamEndpoints { streamPartition, count, }) - const query = { - count, - } - const url = getEndpointUrl(this.client.options.restUrl, 'streams', streamId, 'data', 'partitions', streamPartition, 'last') + `?${qs.stringify(query)}` + const url = ( + getEndpointUrl(this.client.options.restUrl, 'streams', streamId, 'data', 'partitions', streamPartition, 'last') + + `?${qs.stringify({ count })}` + ) + const json = await authFetch(url, this.client.session) return json } From 421d3ee4b0053933ad4e2be5b09ec2a21e03de2e Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Wed, 17 Feb 2021 14:53:39 -0500 Subject: [PATCH 29/30] Remove superfluous @ts-expect-error directives preventing build. --- src/Config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Config.ts b/src/Config.ts index 65442c4c5..a6599842f 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -1,5 +1,4 @@ import qs from 'qs' -// @ts-expect-error import { ControlLayer, MessageLayer } from 'streamr-client-protocol' import Debug from 'debug' From f3f986a4e4a17e17070d33a3f082aafdfb034923 Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Thu, 18 Feb 2021 11:18:31 -0500 Subject: [PATCH 30/30] Add client.options.maxGapRequests = 5 to client config, use to configure OrderMessages. --- src/Config.ts | 1 + src/subscribe/OrderMessages.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Config.ts b/src/Config.ts index a6599842f..f0e43c183 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -27,6 +27,7 @@ export default function ClientConfig(opts: StreamrClientOptions = {}) { orderMessages: true, retryResendAfter: 5000, gapFillTimeout: 5000, + maxGapRequests: 5, maxPublishQueueSize: 10000, // Encryption options diff --git a/src/subscribe/OrderMessages.js b/src/subscribe/OrderMessages.js index fe06a8ecd..4da6226f0 100644 --- a/src/subscribe/OrderMessages.js +++ b/src/subscribe/OrderMessages.js @@ -16,7 +16,7 @@ let ID = 0 */ export default function OrderMessages(client, options = {}) { - const { gapFillTimeout, retryResendAfter } = client.options + const { gapFillTimeout, retryResendAfter, maxGapRequests } = client.options const { streamId, streamPartition, gapFill = true } = validateOptions(options) const debug = client.debug.extend(`OrderMessages::${ID}`) ID += 1 @@ -59,7 +59,7 @@ export default function OrderMessages(client, options = {}) { resendStreams.delete(resendMessageStream) await resendMessageStream.cancel() } - }, gapFillTimeout, retryResendAfter, gapFill ? 5 : 0) + }, gapFillTimeout, retryResendAfter, gapFill ? maxGapRequests : 0) const markMessageExplicitly = orderingUtil.markMessageExplicitly.bind(orderingUtil)