diff --git a/.env.example b/.env.example index 733cb27ccf..b17e7096df 100644 --- a/.env.example +++ b/.env.example @@ -165,7 +165,7 @@ GOOGLE_ENABLED=false # Queues # ########## -# Queue management system (REDIS|SQS|PUBSUB) +# Queue management system (REDIS|SQS|PUBSUB|SERVICE_BUS) QUEUE_PROVIDER=REDIS # Namespace for queues @@ -181,6 +181,9 @@ QUEUE_NAMESPACE=DEV #PUBSUB_GOOGLE_CLOUD_PROJECT_ID= #PUBSUB_GOOGLE_CLOUD_SUBSCRIPTION_NAME=ll +# Azure service bus endpoint, required if QUEUE_PROVIDER=SERVICE_BUS +# SERVICE_BUS_ENDPOINT='Endpoint=sb://[namespace].servicebus.windows.net/;SharedAccessKeyName=[keyName];SharedAccessKey=[key]' + ################ # File storage # ################ diff --git a/lib/services/queue/index.js b/lib/services/queue/index.js index 34f5c73b3f..43d4585fb1 100644 --- a/lib/services/queue/index.js +++ b/lib/services/queue/index.js @@ -4,6 +4,7 @@ import * as bullProvider from './bull'; import * as sqsProvider from './sqs'; import * as localProvider from './local'; import * as pubsubProvider from './pubsub'; +import * as serviceBusProvider from './serviceBus'; const defaultCallback = (err) => { if (err) logger.error('QUEUE DEFAULT ERROR', err); @@ -14,6 +15,7 @@ const getProvider = ({ queueProvider = process.env.QUEUE_PROVIDER }, done) => { switch (queueProvider) { case 'PUBSUB': return done(null, pubsubProvider); case 'SQS': return done(null, sqsProvider); + case 'SERVICE_BUS': return done(null, serviceBusProvider); case 'REDIS': case 'BULL': return done(null, bullProvider); diff --git a/lib/services/queue/serviceBus/index-test.js b/lib/services/queue/serviceBus/index-test.js new file mode 100644 index 0000000000..b9fdb14a05 --- /dev/null +++ b/lib/services/queue/serviceBus/index-test.js @@ -0,0 +1,95 @@ +import { expect } from 'chai'; +import { promisify, delay } from 'bluebird'; +import { subscribe, publish } from './index'; + +describe('serviceBus', () => { + it('should subscribe to queue', async () => { + let called = false; + let handler; + const handledPromise = new Promise((resolve) => { + handler = (data, done) => { + called = true; + expect(data.test).to.equal('data'); + resolve(); + done(); + }; + }); + + await promisify(subscribe)({ + queueName: 'test-queue', + handler + }); + + await promisify(publish)({ + queueName: 'test-queue', + payload: { + test: 'data' + } + }); + + await handledPromise; + expect(called).to.equal(true); + }).timeout(5000); + + it('should dead letter on error', async () => { + let called = false; + + let handler; + const handledPromise = new Promise((resolve) => { + + handler = (data, done) => { + called = true; + expect(data.test).to.equal('data'); + const errMsg = 'An error'; + resolve(); + done(errMsg); + }; + }); + + await promisify(subscribe)({ + queueName: 'test-queue-2', + handler, + deadLetter: 'test-queue-2-deadletter' + }); + + await promisify(publish)({ + queueName: 'test-queue-2', + payload: { + test: 'data' + } + }); + + await handledPromise; + + expect(called).to.equal(true); + }); + + it.skip('should review the lock', async () => { + let called = 0; + + let handler; + const donePromise = new Promise((resolve) => { + handler = async (data, done) => { + called += 1; + await delay(6 * 60 * 1000); + resolve(); + done(); + }; + }); + + await promisify(subscribe)({ + queueName: 'test-queue-3', + handler + }); + + await promisify(publish)({ + queueName: 'test-queue-3', + payload: { + test: 'data' + } + }); + + await donePromise; + expect(called).to.equal(1); + })/*.timeout(7 * 60 * 1000)*/; +}); diff --git a/lib/services/queue/serviceBus/index.js b/lib/services/queue/serviceBus/index.js new file mode 100644 index 0000000000..b2db4cb72c --- /dev/null +++ b/lib/services/queue/serviceBus/index.js @@ -0,0 +1,149 @@ +import { memoize, isString } from 'lodash'; +import logger from 'lib/logger'; +import { ServiceBusClient, ReceiveMode } from '@azure/service-bus'; +import azure from 'azure-sb'; +import { promisify, map as bmap, delay, race } from 'bluebird'; +import moment from 'moment'; + +let consumers = []; + +export const unsubscribeAll = async () => { + await bmap(consumers, async (consumer) => { + await consumer.close(); + }); + consumers = []; +}; + +const connectionString = process.env.SERVICE_BUS_ENDPOINT; + +const serviceBusClient = memoize(() => ServiceBusClient.createFromConnectionString(connectionString)); + +const sbService = memoize((() => azure.createServiceBusService(connectionString))); + +const getQueueClient = memoize(async (queueName, { + visibilityTimeout +} = {}) => { + const lockDurationSeconds = (visibilityTimeout > 300 ? 300 : visibilityTimeout); + const lockDuration = moment.duration(lockDurationSeconds, 'seconds').toISOString(); + + const options = (visibilityTimeout ? + { LockDuration: lockDuration } : {} + ); + + const service = sbService(); + await promisify(service.createQueueIfNotExists, { + context: service + })(queueName, options); + + return serviceBusClient().createQueueClient(queueName); +}, queueName => queueName); + +const getSender = memoize(async (queueName, { + visibilityTimeout +} = {}) => { + const queueClient = await getQueueClient(queueName, { + visibilityTimeout + }); + + const sender = queueClient.createSender(); + return sender; +}, queueName => queueName); + +export const subscribe = async ({ + queueName, + handler, + onProccessed = () => {}, + deadLetter, + retryDelay, + visibilityTimeout, +}, done) => { + const queueClient = await getQueueClient(queueName, { + visibilityTimeout + }); + + const receiver = queueClient.createReceiver(ReceiveMode.peekLock); + receiver.registerMessageHandler(async (brokeredMessage) => { + let payload; + try { + if (isString(brokeredMessage.body)) { + payload = JSON.parse(brokeredMessage.body); + } else { + payload = brokeredMessage.body; + } + } catch (err) { + logger.error(err); + await brokeredMessage.deadLetter({ + deadLetterErrorDescription: JSON.stringify(err, null, 2), + deadletterReason: 'Message was not valid JSON' + }); + } + try { + const renewLock = async () => { + let visibilityTimeoutUpdated = visibilityTimeout; + while (visibilityTimeoutUpdated > 0) { + const nextDelay = visibilityTimeoutUpdated < 250 ? visibilityTimeoutUpdated : 250; + await delay(nextDelay); + visibilityTimeoutUpdated -= nextDelay; + if (visibilityTimeoutUpdated > 0) { + receiver.renewMessageLock(payload); + } + } + }; + const renewLockPromise = renewLock(); + + await race(promisify(handler)(payload), renewLockPromise); + } catch (error) { + if (deadLetter) { + const sender = await getSender(deadLetter); + await sender.send({ + body: { + ...payload, + error + } + }); + } + } + + await brokeredMessage.complete(); + onProccessed(brokeredMessage.body); + }, async (err) => { + logger.error(`Error processing message for ${queueName}`, err); + }); + + consumers.push(receiver); + + done(); +}; + +// ================================================================================================= + +export const publish = async ({ + queueName, + payload, + retryDelay, + visibilityTimeout +}, done) => { + const sender = await getSender(queueName, { + visibilityTimeout + }); + + let stringPayload; + try { + stringPayload = JSON.stringify(payload); + } catch (err) { + done(err); + throw err; + } + + if (!retryDelay) { + await sender.send({ + body: stringPayload + }); + } else { + await sender.schedualMessage(moment().add(retryDelay, 'seconds').toDate(), { + body: stringPayload + }); + } + + done(); +}; diff --git a/package.json b/package.json index b92a17d7aa..03aa4a70ab 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "semantic-release": "ht2-release-private-circleci" }, "dependencies": { + "@azure/service-bus": "^1.0.2", "@azure/storage-blob": "^10.3.0", "@google-cloud/pubsub": "^0.16.1", "@google-cloud/storage": "^1.5.2", @@ -57,6 +58,7 @@ "async": "^2.0.0-rc.3", "aws-sdk": "^2.77.0", "axios": "^0.18.0", + "azure-sb": "^0.11.0", "babel-runtime": "6.22.0", "bcrypt": "^2", "bluebird": "^3.4.6", diff --git a/yarn.lock b/yarn.lock index 86f81052e5..2c74a4795a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,29 @@ # yarn lockfile v1 +"@azure/amqp-common@^1.0.0-preview.5": + version "1.0.0-preview.5" + resolved "https://registry.yarnpkg.com/@azure/amqp-common/-/amqp-common-1.0.0-preview.5.tgz#cd51bd5fbe2c32d59a23b932d3c2c7b9c90e0684" + dependencies: + "@azure/ms-rest-nodeauth" "^0.9.2" + "@types/async-lock" "^1.1.0" + "@types/is-buffer" "^2.0.0" + async-lock "^1.1.3" + buffer "^5.2.1" + debug "^3.1.0" + events "^3.0.0" + is-buffer "^2.0.3" + jssha "^2.3.1" + process "^0.11.10" + stream-browserify "^2.0.2" + tslib "^1.9.3" + url "^0.11.0" + util "^0.11.1" + +"@azure/ms-rest-azure-env@^1.1.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-1.1.2.tgz#8505873afd4a1227ec040894a64fdd736b4a101f" + "@azure/ms-rest-js@1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-1.2.3.tgz#0f08d9ce2e4b681b0348cfd774ae5f37371f1ce7" @@ -13,6 +36,41 @@ uuid "^3.2.1" xml2js "^0.4.19" +"@azure/ms-rest-js@^1.8.1": + version "1.8.13" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-1.8.13.tgz#ed0cd86469697378cd39d79d5589e877a3bc87a6" + dependencies: + "@types/tunnel" "0.0.0" + axios "^0.19.0" + form-data "^2.3.2" + tough-cookie "^2.4.3" + tslib "^1.9.2" + tunnel "0.0.6" + uuid "^3.2.1" + xml2js "^0.4.19" + +"@azure/ms-rest-nodeauth@^0.9.2": + version "0.9.3" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-0.9.3.tgz#aeaebc4f76d2a6e9d68d454a2161a20d7b391af9" + dependencies: + "@azure/ms-rest-azure-env" "^1.1.0" + "@azure/ms-rest-js" "^1.8.1" + adal-node "^0.1.22" + +"@azure/service-bus@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@azure/service-bus/-/service-bus-1.0.2.tgz#a60fb9d405db9e5e0be2459838e9fc05bd20239a" + dependencies: + "@azure/amqp-common" "^1.0.0-preview.5" + "@azure/ms-rest-nodeauth" "^0.9.2" + "@types/long" "^4.0.0" + debug "^3.1.0" + is-buffer "^2.0.3" + long "^4.0.0" + rhea "^1.0.4" + rhea-promise "^0.1.15" + tslib "^1.9.3" + "@azure/storage-blob@^10.3.0": version "10.3.0" resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-10.3.0.tgz#a93043dce9a2b136b306ef00d1ef14bea49dde73" @@ -428,6 +486,10 @@ version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" +"@types/async-lock@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.1.tgz#81f218213bebcc5f740efe9648272c774a2e4b4b" + "@types/bytebuffer@*": version "5.0.40" resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.40.tgz#d6faac40dcfb09cd856cdc4c01d3690ba536d3ee" @@ -447,6 +509,12 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/is-buffer@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/is-buffer/-/is-buffer-2.0.0.tgz#94de4d23540646de5f5df2cd346a1c162067ea5a" + dependencies: + "@types/node" "*" + "@types/long@*", "@types/long@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" @@ -463,6 +531,10 @@ version "10.14.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.12.tgz#0eec3155a46e6c4db1f27c3e588a205f767d622f" +"@types/node@^8.0.47": + version "8.10.49" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.49.tgz#f331afc5efed0796798e5591d6e0ece636969b7b" + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -482,6 +554,12 @@ version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" +"@types/tunnel@0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.0.tgz#c2a42943ee63c90652a5557b8c4e56cda77f944e" + dependencies: + "@types/node" "*" + "@tyriar/fibonacci-heap@^2.0.7": version "2.0.9" resolved "https://registry.yarnpkg.com/@tyriar/fibonacci-heap/-/fibonacci-heap-2.0.9.tgz#df3dcbdb1b9182168601f6318366157ee16666e9" @@ -560,6 +638,20 @@ acorn@^5.0.0, acorn@^5.5.0: version "5.7.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" +adal-node@^0.1.22: + version "0.1.28" + resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.1.28.tgz#468c4bb3ebbd96b1270669f4b9cba4e0065ea485" + dependencies: + "@types/node" "^8.0.47" + async ">=0.6.0" + date-utils "*" + jws "3.x.x" + request ">= 2.52.0" + underscore ">= 1.3.1" + uuid "^3.1.0" + xmldom ">= 0.1.x" + xpath.js "~1.1.0" + addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" @@ -935,12 +1027,20 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" +async-lock@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.0.tgz#cd6a53cb1ec3f86af25eafdeb6bc7c6e317258b8" + async@2.6.2, async@^2.0.0-rc.3, async@^2.0.1, async@^2.1.2, async@^2.1.4, async@^2.3.0, async@^2.4.0, async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" dependencies: lodash "^4.17.11" +async@>=0.6.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.1.0.tgz#42b3b12ae1b74927b5217d8c0016baaf62463772" + async@^1.4.0, async@~1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1019,6 +1119,37 @@ axios@^0.18.0: follow-redirects "1.5.10" is-buffer "^2.0.2" +axios@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +azure-common@^0.9.19: + version "0.9.20" + resolved "https://registry.yarnpkg.com/azure-common/-/azure-common-0.9.20.tgz#5ccb0979d93475ea6383370096a500b8e64506df" + dependencies: + dateformat "1.0.2-1.2.3" + duplexer "~0.1.1" + envconf "~0.0.4" + request "^2.81.0" + through "~2.3.4" + tunnel "~0.0.2" + underscore "1.4.x" + validator "^9.4.1" + xml2js "0.2.7" + xmlbuilder "0.4.3" + +azure-sb@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/azure-sb/-/azure-sb-0.11.0.tgz#2adb4ac1991c1aee1ccb8148e26f57f7fd7c327c" + dependencies: + azure-common "^0.9.19" + mpns "2.1.3" + underscore "^1.8.3" + wns "~0.5.3" + babel-cli@^6.24.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1" @@ -2171,6 +2302,13 @@ buffer@4.9.1, buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + bufferview@~1: version "1.0.1" resolved "https://registry.yarnpkg.com/bufferview/-/bufferview-1.0.1.tgz#7afd74a45f937fa422a1d338c08bbfdc76cd725d" @@ -3437,10 +3575,24 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" +date-utils@*: + version "1.2.21" + resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64" + +dateformat@1.0.2-1.2.3: + version "1.0.2-1.2.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.2-1.2.3.tgz#b0220c02de98617433b72851cf47de3df2cdbee9" + dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" +"debug@0.8.0 - 3.5.0", debug@^3.1.0, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + dependencies: + ms "^2.1.1" + debug@2, debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3465,12 +3617,6 @@ debug@4.1.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@^3.1.0, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - dependencies: - ms "^2.1.1" - debuglog@^1.0.0, debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -3848,6 +3994,10 @@ duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + duplexify@^3.1.2, duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -3964,6 +4114,10 @@ env-ci@^4.0.0: execa "^1.0.0" java-properties "^1.0.0" +envconf@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/envconf/-/envconf-0.0.4.tgz#85675afba237c43f98de2d46adc0e532a4dcf48b" + enzyme-adapter-react-16@^1.12.1: version "1.14.0" resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz#204722b769172bcf096cb250d33e6795c1f1858f" @@ -6337,7 +6491,7 @@ is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" -is-buffer@^2.0.2: +is-buffer@^2.0.2, is-buffer@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" @@ -7177,6 +7331,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jssha@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-2.3.1.tgz#147b2125369035ca4b2f7d210dc539f009b3de9a" + jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" @@ -7197,7 +7355,7 @@ jwa@~1.0.0: buffer-equal-constant-time "^1.0.1" ecdsa-sig-formatter "^1.0.0" -jws@^3.1.5, jws@^3.2.2: +jws@3.x.x, jws@^3.1.5, jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" dependencies: @@ -8537,6 +8695,10 @@ mpath@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.6.0.tgz#aa922029fca4f0f641f360e74c5c1b6a4c47078e" +mpns@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mpns/-/mpns-2.1.3.tgz#4328bb23ca79669e3383389074c898713e22ccb9" + mquery@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.1.tgz#8b059a49cdae0a8a9e804284ef64c2f58d3ac05d" @@ -11651,7 +11813,7 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" -request@^2.39.0, request@^2.54.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.85.0, request@^2.87.0, request@^2.88.0: +"request@>= 2.52.0", request@^2.39.0, request@^2.54.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.85.0, request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" dependencies: @@ -11825,6 +11987,20 @@ rgb@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" +rhea-promise@^0.1.15: + version "0.1.15" + resolved "https://registry.yarnpkg.com/rhea-promise/-/rhea-promise-0.1.15.tgz#00175324352224424d59b7712faf14097a84e8f2" + dependencies: + debug "^3.1.0" + rhea "^1.0.4" + tslib "^1.9.3" + +rhea@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/rhea/-/rhea-1.0.7.tgz#65808c1b88b6aa27578b9a3495a63467ec72f51c" + dependencies: + debug "0.8.0 - 3.5.0" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -11968,6 +12144,10 @@ sass-loader@^6.0.3: neo-async "^2.5.0" pify "^3.0.0" +sax@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea" + sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -12538,7 +12718,7 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stream-browserify@^2.0.1: +stream-browserify@^2.0.1, stream-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" dependencies: @@ -12936,7 +13116,7 @@ through2@^2.0.0, through2@^2.0.2, through2@^2.0.3, through2@~2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.6: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -13100,6 +13280,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.6, tunnel@~0.0.2: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -13222,11 +13406,15 @@ underscore.string@^3.2.3: sprintf-js "^1.0.3" util-deprecate "^1.0.2" +underscore@1.4.x: + version "1.4.4" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" + underscore@1.5.x: version "1.5.2" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.5.2.tgz#1335c5e4f5e6d33bbb4b006ba8c86a00f556de08" -underscore@^1.8.3: +"underscore@>= 1.3.1", underscore@^1.8.3: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" @@ -13428,7 +13616,7 @@ util@0.10.3: dependencies: inherits "2.0.1" -util@^0.11.0: +util@^0.11.0, util@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" dependencies: @@ -13487,6 +13675,10 @@ validator@^5.2.0: version "5.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-5.7.0.tgz#7a87a58146b695ac486071141c0c49d67da05e5c" +validator@^9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -13741,6 +13933,10 @@ winston@^2.1.1, winston@^2.3.1, winston@^2.4.2: isstream "0.1.x" stack-trace "0.0.x" +wns@~0.5.3: + version "0.5.4" + resolved "https://registry.yarnpkg.com/wns/-/wns-0.5.4.tgz#ad8e2ee60e675557da9610d94444a7f59eceaf78" + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" @@ -13829,6 +14025,12 @@ xml2js@0.1.x: dependencies: sax ">=0.1.1" +xml2js@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.7.tgz#1838518bb01741cae0878bab4915e494c32306af" + dependencies: + sax "0.5.2" + xml2js@0.4.19, xml2js@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" @@ -13836,14 +14038,22 @@ xml2js@0.4.19, xml2js@^0.4.19: sax ">=0.6.0" xmlbuilder "~9.0.1" +xmlbuilder@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.3.tgz#c4614ba74e0ad196e609c9272cd9e1ddb28a8a58" + xmlbuilder@~9.0.1: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" -xmldom@^0.1.19: +"xmldom@>= 0.1.x", xmldom@^0.1.19: version "0.1.27" resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" +xpath.js@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1" + "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"