diff --git a/.gitignore b/.gitignore index 0f3760dd..bfb7597d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ extension/token.js node_modules docs lib/twilio/constants.js +nodemon.json +tests/webpack/package-lock.json diff --git a/CHANGELOG.md b/CHANGELOG.md index fb792533..351a4ec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,49 @@ +2.2.0 (In Progress) +=================== + +New Features +------------ + +### Call Message Events (Beta) + +The SDK can now send and receive custom messages to and from Twilio's backend via the following new `Call` APIs. + +- [sendMessage](https://twilio.github.io/twilio-voice.js/classes/voice.call.html#sendmessage) +- [messageReceivedEvent](https://twilio.github.io/twilio-voice.js/classes/voice.call.html#messagereceivedevent) +- [messageSentEvent](https://twilio.github.io/twilio-voice.js/classes/voice.call.html#messagesentevent) + +Please visit this [page](https://www.twilio.com/docs/voice/sdks/call-message-events) for more details about this feature. Additionally, please see the following for more information on how to send and receive messages on the server. + +- [UserDefinedMessage](https://www.twilio.com/docs/voice/api/userdefinedmessage-resource) +- [UserDefinedMessageSubscription](https://www.twilio.com/docs/voice/api/userdefinedmessagesubscription-resource) + +**NOTE:** This feature should not be used with [PII](https://www.twilio.com/docs/glossary/what-is-personally-identifiable-information-pii). + +**Example** + +```js +const device = new Device(token, options); + +const setupCallHandlers = call => { + call.on('messageReceived', message => messageReceivedHandler(message)); + call.on('messageSent', message => messageSentHandler(message)); +}; + +// For outgoing calls +const call = await device.connect(); +setupCallHandlers(call); + +// For incoming calls +device.on('incoming', call => setupCallHandlers(call)); +await device.register(); + +// For sending a message +const eventSid = call.sendMessage({ + content: { foo: 'foo' }, + messageType: Call.MessageType.UserDefinedMessage, +}); +``` + 2.1.2 (October 26, 2022) ======================== @@ -17,8 +63,8 @@ Bug Fixes - Use DOMException instead of DOMError, which has been deprecated - Removed npm util from the package, instead favoring native functions -2.1.0 (December 16, 2021) - Release -=================================== +2.1.0 (December 16, 2021) +========================= New Features ------------ @@ -140,8 +186,8 @@ This patch increment was necessary because the 2.0.0 pilot artifact was erroneou now removed from npm so that it is not mistakenly used. The first npm artifact will be 2.0.1. -2.0.0 (July 7, 2021) - Release -============================== +2.0.0 (July 7, 2021) +==================== ## Migration from twilio-client.js 1.x This product, Twilio's JavaScript Voice SDK, is the next version of Twilio's Javascript Client SDK. It is diff --git a/lib/twilio/call.ts b/lib/twilio/call.ts index e9d98b24..82ee780d 100644 --- a/lib/twilio/call.ts +++ b/lib/twilio/call.ts @@ -10,6 +10,7 @@ import DialtonePlayer from './dialtonePlayer'; import { GeneralErrors, InvalidArgumentError, + InvalidStateError, MediaErrors, SignalingErrors, TwilioError, @@ -21,6 +22,7 @@ import RTCSample from './rtc/sample'; import RTCWarning from './rtc/warning'; import StatsMonitor from './statsMonitor'; import { isChrome } from './util'; +import { generateVoiceEventSid } from './uuid'; const Backoff = require('backoff'); const C = require('./constants'); @@ -213,6 +215,12 @@ class Call extends EventEmitter { */ private _mediaStatus: Call.State = Call.State.Pending; + /** + * A map of messages sent via sendMessage API using voiceEventSid as the key. + * The message will be deleted once an 'ack' or an error is received from the server. + */ + private _messages: Map = new Map(); + /** * A batch of metrics samples to send to Insights. Gets cleared after * each send and appended to on each new sample. @@ -236,6 +244,7 @@ class Call extends EventEmitter { MediaHandler: PeerConnection, offerSdp: null, shouldPlayDisconnect: () => true, + voiceEventSidGenerator: generateVoiceEventSid, }; /** @@ -278,6 +287,11 @@ class Call extends EventEmitter { */ private _status: Call.State = Call.State.Pending; + /** + * Voice event SID generator, creates a unique voice event SID. + */ + private _voiceEventSidGenerator: () => string; + /** * Whether the {@link Call} has been connected. Used to determine if we are reconnected. */ @@ -313,6 +327,9 @@ class Call extends EventEmitter { this._signalingReconnectToken = this._options.reconnectToken; } + this._voiceEventSidGenerator = + this._options.voiceEventSidGenerator || generateVoiceEventSid; + this._direction = this.parameters.CallSid ? Call.CallDirection.Incoming : Call.CallDirection.Outgoing; if (this._direction === Call.CallDirection.Incoming && this.parameters) { @@ -513,10 +530,13 @@ class Call extends EventEmitter { }; this._pstream = config.pstream; + this._pstream.on('ack', this._onAck); this._pstream.on('cancel', this._onCancel); + this._pstream.on('error', this._onSignalingError); this._pstream.on('ringing', this._onRinging); this._pstream.on('transportClose', this._onTransportClose); this._pstream.on('connected', this._onConnected); + this._pstream.on('message', this._onMessageReceived); this.on('error', error => { this._publisher.error('connection', 'error', { @@ -842,6 +862,52 @@ class Call extends EventEmitter { } } + /** + * Send a message to Twilio. Your backend application can listen for these + * messages to allow communication between your frontend and backend applications. + *

This feature is currently in Beta. + * @param message - The message object to send. + * @returns A voice event sid that uniquely identifies the message that was sent. + */ + sendMessage(message: Call.Message): string { + const { content, contentType, messageType } = message; + + if (typeof content === 'undefined' || content === null) { + throw new InvalidArgumentError('`content` is empty'); + } + + if (typeof messageType !== 'string') { + throw new InvalidArgumentError( + '`messageType` must be an enumeration value of `Call.MessageType` or ' + + 'a string.', + ); + } + + if (messageType.length === 0) { + throw new InvalidArgumentError( + '`messageType` must be a non-empty string.', + ); + } + + if (this._pstream === null) { + throw new InvalidStateError( + 'Could not send CallMessage; Signaling channel is disconnected', + ); + } + + const callSid = this.parameters.CallSid; + if (typeof this.parameters.CallSid === 'undefined') { + throw new InvalidStateError( + 'Could not send CallMessage; Call has no CallSid', + ); + } + + const voiceEventSid = this._voiceEventSidGenerator(); + this._messages.set(voiceEventSid, { content, contentType, messageType, voiceEventSid }); + this._pstream.sendMessage(callSid, content, contentType, messageType, voiceEventSid); + return voiceEventSid; + } + /** * Get the current {@link Call} status. */ @@ -890,12 +956,15 @@ class Call extends EventEmitter { const cleanup = () => { if (!this._pstream) { return; } + this._pstream.removeListener('ack', this._onAck); this._pstream.removeListener('answer', this._onAnswer); this._pstream.removeListener('cancel', this._onCancel); + this._pstream.removeListener('error', this._onSignalingError); this._pstream.removeListener('hangup', this._onHangup); this._pstream.removeListener('ringing', this._onRinging); this._pstream.removeListener('transportClose', this._onTransportClose); this._pstream.removeListener('connected', this._onConnected); + this._pstream.removeListener('message', this._onMessageReceived); }; // This is kind of a hack, but it lets us avoid rewriting more code. @@ -1029,6 +1098,21 @@ class Call extends EventEmitter { } } + /** + * Called when the {@link Call} receives an ack from signaling + * @param payload + */ + private _onAck = (payload: Record): void => { + const { acktype, callsid, voiceeventsid } = payload; + if (this.parameters.CallSid !== callsid) { + this._log.warn(`Received ack from a different callsid: ${callsid}`); + return; + } + if (acktype === 'message') { + this._onMessageSent(voiceeventsid); + } + } + /** * Called when the {@link Call} is answered. * @param payload @@ -1213,6 +1297,42 @@ class Call extends EventEmitter { } } + /** + * Raised when a Call receives a message from the backend. + * @param payload - A record representing the payload of the message from the + * Twilio backend. + */ + private _onMessageReceived = (payload: Record): void => { + const { callsid, content, contenttype, messagetype, voiceeventsid } = payload; + + if (this.parameters.CallSid !== callsid) { + this._log.warn(`Received a message from a different callsid: ${callsid}`); + return; + } + + this.emit('messageReceived', { + content, + contentType: contenttype, + messageType: messagetype, + voiceEventSid: voiceeventsid, + }); + } + + /** + * Raised when a Call receives an 'ack' with an 'acktype' of 'message. + * This means that the message sent via sendMessage API has been received by the signaling server. + * @param voiceEventSid + */ + private _onMessageSent = (voiceEventSid: string): void => { + if (!this._messages.has(voiceEventSid)) { + this._log.warn(`Received a messageSent with a voiceEventSid that doesn't exists: ${voiceEventSid}`); + return; + } + const message = this._messages.get(voiceEventSid); + this._messages.delete(voiceEventSid); + this.emit('messageSent', message); + } + /** * When we get a RINGING signal from PStream, update the {@link Call} status. * @param payload @@ -1253,6 +1373,22 @@ class Call extends EventEmitter { this.emit('sample', sample); } + /** + * Called when an 'error' event is received from the signaling stream. + */ + private _onSignalingError = (payload: Record): void => { + const { callsid, voiceeventsid } = payload; + if (this.parameters.CallSid !== callsid) { + this._log.warn(`Received an error from a different callsid: ${callsid}`); + return; + } + if (voiceeventsid && this._messages.has(voiceeventsid)) { + // Do not emit an error here. Device is handling all signaling related errors. + this._messages.delete(voiceeventsid); + this._log.warn(`Received an error while sending a message.`, payload); + } + } + /** * Called when signaling is restored */ @@ -1393,6 +1529,24 @@ namespace Call { */ declare function errorEvent(error: TwilioError): void; + /** + * Emitted when a Call receives a message from the backend. + *

This feature is currently in Beta. + * @param message - A message object representing the payload + * that was received from the Twilio backend. + * @event + */ + declare function messageReceivedEvent(message: Call.Message): void; + + /** + * Emitted after calling the {@link Call.sendMessage} API. + * This event indicates that Twilio has received the message. + *

This feature is currently in Beta. + * @param message - A message object that was sent to the Twilio backend. + * @event + */ + declare function messageSentEvent(message: Call.Message): void; + /** * Emitted when the {@link Call} is muted or unmuted. * @param isMuted - Whether the {@link Call} is muted. @@ -1514,6 +1668,19 @@ namespace Call { LowBytes = 'LowBytes', } + /** + * Known call message types. + */ + export enum MessageType { + /** + * Allows for any object types to be defined by the user. + * When this value is used in the {@link Call.Message} object, + * The {@link Call.Message.content} can be of any type as long as + * it matches the MIME type defined in {@link Call.Message.contentType}. + */ + UserDefinedMessage = 'user-defined-message', + } + /** * Options to be used to acquire media tracks and connect media. */ @@ -1583,6 +1750,35 @@ namespace Call { soundcache: Map; } + /** + * A Call Message represents the data that is being transferred between + * Twilio and the SDK. + */ + export interface Message { + /** + * The content of the message which should match the contentType parameter. + */ + content: any; + + /** + * The MIME type of the content. The default value is application/json + * and is the only contentType that is supported at the moment. + */ + contentType?: string; + + /** + * The type of message + */ + messageType: MessageType; + + /** + * An autogenerated id that uniquely identifies the instance of this message. + * This is not required when sending a message from the SDK as this is autogenerated. + * But it will be available after the message is sent, or when a message is received. + */ + voiceEventSid?: string; + } + /** * Options to be passed to the {@link Call} constructor. * @private @@ -1699,6 +1895,11 @@ namespace Call { * TwiML params for the call. May be set for either outgoing or incoming calls. */ twimlParams?: Record; + + /** + * Voice event SID generator. + */ + voiceEventSidGenerator?: () => string; } /** diff --git a/lib/twilio/device.ts b/lib/twilio/device.ts index fcf0d8e1..7e965f81 100644 --- a/lib/twilio/device.ts +++ b/lib/twilio/device.ts @@ -36,6 +36,7 @@ import { isUnifiedPlanDefault, queryToJson, } from './util'; +import { generateVoiceEventSid } from './uuid'; const C = require('./constants'); const Publisher = require('./eventpublisher'); @@ -142,6 +143,11 @@ export interface IExtendedDeviceOptions extends Device.Options { * Custom Sound constructor */ Sound?: ISound; + + /** + * Voice event SID generator. + */ + voiceEventSidGenerator?: () => string; } /** @@ -340,6 +346,7 @@ class Device extends EventEmitter { preflight: false, sounds: { }, tokenRefreshMs: 10000, + voiceEventSidGenerator: generateVoiceEventSid, }; /** @@ -559,6 +566,7 @@ class Device extends EventEmitter { const activeCall = this._activeCall = await this._makeCall(options.params || { }, { rtcConfiguration: options.rtcConfiguration, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, }); // Make sure any incoming calls are ignored @@ -974,6 +982,7 @@ class Device extends EventEmitter { rtcConstraints: this._options.rtcConstraints, shouldPlayDisconnect: () => this._enabledSounds.disconnect, twimlParams, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, }, options); const maybeUnsetPreferredUri = () => { @@ -1190,6 +1199,7 @@ class Device extends EventEmitter { callParameters, offerSdp: payload.sdp, reconnectToken: payload.reconnect, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, }); this._calls.push(call); @@ -1623,9 +1633,9 @@ namespace Device { * Options to be passed to {@link Device.connect}. */ export interface ConnectOptions extends Call.AcceptOptions { - /** - * A flat object containing key:value pairs to be sent to the TwiML app. - */ + /** + * A flat object containing key:value pairs to be sent to the TwiML app. + */ params?: Record; } diff --git a/lib/twilio/errors/generated.ts b/lib/twilio/errors/generated.ts index 18447df0..86c6cd54 100644 --- a/lib/twilio/errors/generated.ts +++ b/lib/twilio/errors/generated.ts @@ -252,6 +252,109 @@ export namespace GeneralErrors { } } +export namespace MalformedRequestErrors { + export class MalformedRequestError extends TwilioError { + causes: string[] = [ + 'Invalid content or MessageType passed to sendMessage method.', + ]; + code: number = 31100; + description: string = 'The request had malformed syntax.'; + explanation: string = 'The request could not be understood due to malformed syntax.'; + name: string = 'MalformedRequestError'; + solutions: string[] = [ + 'Ensure content and MessageType passed to sendMessage method are valid.', + ]; + + constructor(); + constructor(message: string); + constructor(error: Error | object); + constructor(message: string, error: Error | object); + constructor(messageOrError?: string | Error | object, error?: Error | object) { + super(messageOrError, error); + Object.setPrototypeOf(this, MalformedRequestErrors.MalformedRequestError.prototype); + + const message: string = typeof messageOrError === 'string' + ? messageOrError + : this.explanation; + + const originalError: Error | object | undefined = typeof messageOrError === 'object' + ? messageOrError + : error; + + this.message = `${this.name} (${this.code}): ${message}`; + this.originalError = originalError; + } + } +} + +export namespace AuthorizationErrors { + export class RateExceededError extends TwilioError { + causes: string[] = [ + 'Rate limit exceeded.', + ]; + code: number = 31206; + description: string = 'Rate exceeded authorized limit.'; + explanation: string = 'The request performed exceeds the authorized limit.'; + name: string = 'RateExceededError'; + solutions: string[] = [ + 'Ensure message send rate does not exceed authorized limits.', + ]; + + constructor(); + constructor(message: string); + constructor(error: Error | object); + constructor(message: string, error: Error | object); + constructor(messageOrError?: string | Error | object, error?: Error | object) { + super(messageOrError, error); + Object.setPrototypeOf(this, AuthorizationErrors.RateExceededError.prototype); + + const message: string = typeof messageOrError === 'string' + ? messageOrError + : this.explanation; + + const originalError: Error | object | undefined = typeof messageOrError === 'object' + ? messageOrError + : error; + + this.message = `${this.name} (${this.code}): ${message}`; + this.originalError = originalError; + } + } + + export class PayloadSizeExceededError extends TwilioError { + causes: string[] = [ + 'The payload size of Call Message Event exceeds the authorized limit.', + ]; + code: number = 31209; + description: string = 'Call Message Event Payload size exceeded authorized limit.'; + explanation: string = 'The request performed to send a Call Message Event exceeds the payload size authorized limit'; + name: string = 'PayloadSizeExceededError'; + solutions: string[] = [ + 'Reduce payload size of Call Message Event to be within the authorized limit and try again.', + ]; + + constructor(); + constructor(message: string); + constructor(error: Error | object); + constructor(message: string, error: Error | object); + constructor(messageOrError?: string | Error | object, error?: Error | object) { + super(messageOrError, error); + Object.setPrototypeOf(this, AuthorizationErrors.PayloadSizeExceededError.prototype); + + const message: string = typeof messageOrError === 'string' + ? messageOrError + : this.explanation; + + const originalError: Error | object | undefined = typeof messageOrError === 'object' + ? messageOrError + : error; + + this.message = `${this.name} (${this.code}): ${message}`; + this.originalError = originalError; + } + } +} + export namespace UserMediaErrors { export class PermissionDeniedError extends TwilioError { causes: string[] = [ @@ -507,6 +610,9 @@ export const errorsByCode: ReadonlyMap = new Map([ [ 31005, GeneralErrors.ConnectionError ], [ 31008, GeneralErrors.CallCancelledError ], [ 31009, GeneralErrors.TransportError ], + [ 31100, MalformedRequestErrors.MalformedRequestError ], + [ 31206, AuthorizationErrors.RateExceededError ], + [ 31209, AuthorizationErrors.PayloadSizeExceededError ], [ 31401, UserMediaErrors.PermissionDeniedError ], [ 31402, UserMediaErrors.AcquisitionFailedError ], [ 53000, SignalingErrors.ConnectionError ], diff --git a/lib/twilio/pstream.js b/lib/twilio/pstream.js index e58ec788..045deb27 100644 --- a/lib/twilio/pstream.js +++ b/lib/twilio/pstream.js @@ -177,6 +177,23 @@ PStream.prototype.setToken = function(token) { this._publish('listen', payload); }; +PStream.prototype.sendMessage = function( + callsid, + content, + contenttype = 'application/json', + messagetype, + voiceeventsid +) { + const payload = { + callsid, + content, + contenttype, + messagetype, + voiceeventsid, + }; + this._publish('message', payload, true); +}; + PStream.prototype.register = function(mediaCapabilities) { const regPayload = { media: mediaCapabilities diff --git a/lib/twilio/uuid.ts b/lib/twilio/uuid.ts new file mode 100644 index 00000000..07bbeb93 --- /dev/null +++ b/lib/twilio/uuid.ts @@ -0,0 +1,45 @@ +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ + +import * as md5 from 'md5'; +import { NotSupportedError } from '../twilio/errors'; + +function generateUuid(): string { + if (typeof window !== 'object') { + throw new NotSupportedError('This platform is not supported.'); + } + + const crypto: Crypto & { randomUUID?: () => string } = window.crypto; + if (typeof crypto !== 'object') { + throw new NotSupportedError( + 'The `crypto` module is not available on this platform.', + ); + } + if (typeof (crypto.randomUUID || crypto.getRandomValues) === 'undefined') { + throw new NotSupportedError( + 'Neither `crypto.randomUUID` or `crypto.getRandomValues` are available ' + + 'on this platform.', + ); + } + + const uInt32Arr: typeof Uint32Array = window.Uint32Array; + if (typeof uInt32Arr === 'undefined') { + throw new NotSupportedError( + 'The `Uint32Array` module is not available on this platform.', + ); + } + + const generateRandomValues: () => string = + typeof crypto.randomUUID === 'function' + ? () => crypto.randomUUID!() + : () => crypto.getRandomValues(new Uint32Array(32)).toString(); + + return md5(generateRandomValues()); +} + +export function generateVoiceEventSid() { + return `KX${generateUuid()}`; +} diff --git a/package-lock.json b/package-lock.json index 84252eb1..98b700e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,20 @@ { "name": "@twilio/voice-sdk", - "version": "2.1.2-dev", + "version": "2.2.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@twilio/voice-sdk", - "version": "2.1.2-dev", + "version": "2.2.0-dev", "license": "Apache-2.0", "dependencies": { "@twilio/audioplayer": "1.0.6", - "@twilio/voice-errors": "1.1.1", + "@twilio/voice-errors": "1.3.1", + "@types/md5": "2.3.2", "backoff": "2.5.0", "loglevel": "1.6.7", + "md5": "2.3.0", "rtcpeerconnection-shim": "1.2.8", "ws": "7.4.6", "xmlhttprequest": "1.8.0" @@ -711,12 +713,9 @@ } }, "node_modules/@twilio/voice-errors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.1.1.tgz", - "integrity": "sha512-3IJzRhgAqsS3uW2PO7crUXEFxuFhggHeLvt/Q4hz7lrTLFChl37hWiImCMIaM5VHiybQi6ECVQsId2X8UdTr2A==", - "dependencies": { - "npm-run-all": "^4.1.5" - } + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.3.1.tgz", + "integrity": "sha512-CtozqXquzeUqYkYNus3aOEuS6G007UQK6a31QJYKa28j0tZl8ziwTKVSghj6oeRLlQB2btxiiSQzALMMEh2cug==" }, "node_modules/@types/body-parser": { "version": "1.19.2", @@ -770,6 +769,11 @@ "@types/node": "*" } }, + "node_modules/@types/md5": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.2.tgz", + "integrity": "sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og==" + }, "node_modules/@types/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.0.tgz", @@ -2179,7 +2183,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base": { "version": "0.11.2", @@ -2335,6 +2340,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2696,6 +2702,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -2830,6 +2837,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -3118,6 +3133,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -3125,7 +3141,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colorette": { "version": "1.4.0", @@ -3205,7 +3222,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concat-stream": { "version": "1.6.2", @@ -3388,6 +3406,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -3403,10 +3422,19 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { "semver": "bin/semver" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -3574,6 +3602,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -4012,6 +4041,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -4020,6 +4050,7 @@ "version": "1.20.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -4056,6 +4087,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -4096,6 +4128,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -4154,6 +4187,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -5296,12 +5330,14 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/function.prototype.name": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -5325,6 +5361,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5393,6 +5430,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -5424,6 +5462,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -5614,7 +5653,8 @@ "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true }, "node_modules/growl": { "version": "1.10.5", @@ -5688,6 +5728,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -5711,6 +5752,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5740,6 +5782,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -5748,6 +5791,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.1" }, @@ -5759,6 +5803,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -5770,6 +5815,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -5976,7 +6022,8 @@ "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "node_modules/html-escaper": { "version": "2.0.2", @@ -6362,6 +6409,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -6462,12 +6510,14 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -6492,6 +6542,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6506,13 +6557,13 @@ "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6524,6 +6575,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -6558,6 +6610,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6723,6 +6776,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6743,6 +6797,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6840,6 +6895,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6876,6 +6932,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -6896,6 +6953,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6910,6 +6968,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -6982,6 +7041,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -7045,7 +7105,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", @@ -7484,7 +7545,8 @@ "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, "node_modules/json-schema": { "version": "0.4.0", @@ -8180,6 +8242,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -8194,6 +8257,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -8206,6 +8270,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { "node": ">=4" } @@ -8214,6 +8279,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "engines": { "node": ">=4" } @@ -8597,6 +8663,16 @@ "dev": true, "optional": true }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -8627,6 +8703,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, "engines": { "node": ">= 0.10.0" } @@ -8793,6 +8870,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9265,7 +9343,8 @@ "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node_modules/nise": { "version": "4.1.0", @@ -9357,6 +9436,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -9368,6 +9448,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { "semver": "bin/semver" } @@ -9400,6 +9481,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -9424,6 +9506,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -9435,6 +9518,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -9448,6 +9532,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -9858,6 +9943,7 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9882,6 +9968,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -10307,6 +10394,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, "engines": { "node": ">=4" } @@ -10314,7 +10402,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-platform": { "version": "0.11.15", @@ -10335,6 +10424,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "dependencies": { "pify": "^3.0.0" }, @@ -10346,6 +10436,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { "node": ">=4" } @@ -10400,6 +10491,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, "bin": { "pidtree": "bin/pidtree.js" }, @@ -10906,6 +10998,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -11498,6 +11591,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -11753,6 +11847,7 @@ "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -12125,6 +12220,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, "dependencies": { "shebang-regex": "^1.0.0" }, @@ -12136,6 +12232,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -12143,7 +12240,8 @@ "node_modules/shell-quote": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true }, "node_modules/shelljs": { "version": "0.8.5", @@ -12166,6 +12264,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -12609,6 +12708,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -12617,12 +12717,14 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -12631,7 +12733,8 @@ "node_modules/spdx-license-ids": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true }, "node_modules/split-string": { "version": "3.1.0", @@ -12923,6 +13026,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -12939,6 +13043,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -12952,6 +13057,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -13031,6 +13137,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -14120,6 +14227,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -14450,6 +14558,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -14600,6 +14709,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -14611,6 +14721,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -15485,12 +15596,9 @@ } }, "@twilio/voice-errors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.1.1.tgz", - "integrity": "sha512-3IJzRhgAqsS3uW2PO7crUXEFxuFhggHeLvt/Q4hz7lrTLFChl37hWiImCMIaM5VHiybQi6ECVQsId2X8UdTr2A==", - "requires": { - "npm-run-all": "^4.1.5" - } + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.3.1.tgz", + "integrity": "sha512-CtozqXquzeUqYkYNus3aOEuS6G007UQK6a31QJYKa28j0tZl8ziwTKVSghj6oeRLlQB2btxiiSQzALMMEh2cug==" }, "@types/body-parser": { "version": "1.19.2", @@ -15544,6 +15652,11 @@ "@types/node": "*" } }, + "@types/md5": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.2.tgz", + "integrity": "sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og==" + }, "@types/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.0.tgz", @@ -16822,7 +16935,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "base": { "version": "0.11.2", @@ -16945,6 +17059,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -17274,6 +17389,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -17370,6 +17486,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -17604,6 +17725,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -17611,7 +17733,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "colorette": { "version": "1.4.0", @@ -17687,7 +17810,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "concat-stream": { "version": "1.6.2", @@ -17853,6 +17977,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -17864,10 +17989,16 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -18003,6 +18134,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -18402,6 +18534,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -18410,6 +18543,7 @@ "version": "1.20.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -18440,6 +18574,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -18470,6 +18605,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -18518,7 +18654,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "escodegen": { "version": "1.8.1", @@ -19371,12 +19508,14 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "function.prototype.name": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -19393,7 +19532,8 @@ "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true }, "gauge": { "version": "1.2.7", @@ -19451,6 +19591,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -19473,6 +19614,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -19628,7 +19770,8 @@ "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true }, "growl": { "version": "1.10.5", @@ -19683,6 +19826,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -19699,7 +19843,8 @@ "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true }, "has-binary2": { "version": "1.0.3", @@ -19727,12 +19872,14 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -19740,12 +19887,14 @@ "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -19907,7 +20056,8 @@ "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "html-escaper": { "version": "2.0.2", @@ -20212,6 +20362,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -20287,12 +20438,14 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "requires": { "has-bigints": "^1.0.1" } @@ -20311,6 +20464,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -20319,18 +20473,19 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true }, "is-core-module": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -20358,6 +20513,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -20470,7 +20626,8 @@ "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true }, "is-number": { "version": "7.0.0", @@ -20482,6 +20639,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -20549,6 +20707,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -20573,6 +20732,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -20587,6 +20747,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -20595,6 +20756,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -20649,6 +20811,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -20697,7 +20860,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "isobject": { "version": "3.0.1", @@ -21037,7 +21201,8 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, "json-schema": { "version": "0.4.0", @@ -21619,6 +21784,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -21630,6 +21796,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -21638,12 +21805,14 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true } } }, @@ -21962,6 +22131,16 @@ "dev": true, "optional": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -21988,7 +22167,8 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true }, "meow": { "version": "3.7.0", @@ -22126,6 +22306,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -22507,7 +22688,8 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "nise": { "version": "4.1.0", @@ -22591,6 +22773,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -22601,7 +22784,8 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, @@ -22627,6 +22811,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -22643,6 +22828,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -22651,6 +22837,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -22661,6 +22848,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -22985,7 +23173,8 @@ "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true }, "object-is": { "version": "1.1.5", @@ -23000,7 +23189,8 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object-visit": { "version": "1.0.1", @@ -23340,12 +23530,14 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-platform": { "version": "0.11.15", @@ -23363,6 +23555,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "requires": { "pify": "^3.0.0" }, @@ -23370,7 +23563,8 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true } } }, @@ -23414,7 +23608,8 @@ "pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true }, "pify": { "version": "4.0.1", @@ -23824,6 +24019,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, "requires": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -24324,6 +24520,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -24533,6 +24730,7 @@ "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, "requires": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -24835,6 +25033,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -24842,12 +25041,14 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true }, "shell-quote": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true }, "shelljs": { "version": "0.8.5", @@ -24864,6 +25065,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -25238,6 +25440,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -25246,12 +25449,14 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -25260,7 +25465,8 @@ "spdx-license-ids": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true }, "split-string": { "version": "3.1.0", @@ -25511,6 +25717,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -25521,6 +25728,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -25531,6 +25739,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -25585,7 +25794,8 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "syntax-error": { "version": "1.4.0", @@ -26451,6 +26661,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -26716,6 +26927,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -26847,6 +27059,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -26855,6 +27068,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", diff --git a/package.json b/package.json index a8082e7a..6a1b9507 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@twilio/voice-sdk", - "version": "2.1.3-dev", + "version": "2.2.0-dev", "description": "Twilio's JavaScript Voice SDK", "main": "./es5/twilio.js", "types": "./es5/twilio.d.ts", @@ -26,7 +26,7 @@ "build:dev": "ENV=dev npm run build", "build:dist": "node ./scripts/build.js ./lib/browser.js ./LICENSE.md ./dist/twilio.js", "build:dist-min": "uglifyjs ./dist/twilio.js -o ./dist/twilio.min.js --comments \"/^! twilio-voice.js/\" -b beautify=false,ascii_only=true", - "build:release": "npm-run-all lint build test:webpack test:es5 status", + "build:release": "npm-run-all lint build test:es5 status", "build:ts": "./node_modules/typescript/bin/tsc", "build:constants": "node ./scripts/constants.js", "clean": "rimraf ./coverage ./dist ./es5", @@ -43,7 +43,7 @@ "release": "release", "start": "node server.js", "status": "git status", - "test": "npm-run-all lint build test:unit test:webpack test:es5 test:docker", + "test": "npm-run-all lint build test:unit test:es5 test:docker", "test:docker": "chmod +x ./scripts/circleci-run-tests.sh && ./scripts/circleci-run-tests.sh", "test:es5": "es-check es5 \"./es5/**/*.js\" ./dist/*.js", "test:framework:no-framework": "mocha tests/framework/no-framework.js", @@ -121,9 +121,11 @@ }, "dependencies": { "@twilio/audioplayer": "1.0.6", - "@twilio/voice-errors": "1.1.1", + "@twilio/voice-errors": "1.3.1", + "@types/md5": "2.3.2", "backoff": "2.5.0", "loglevel": "1.6.7", + "md5": "2.3.0", "rtcpeerconnection-shim": "1.2.8", "ws": "7.4.6", "xmlhttprequest": "1.8.0" diff --git a/templates/constants.js b/templates/constants.js index 6930854e..b4f2e204 100644 --- a/templates/constants.js +++ b/templates/constants.js @@ -15,11 +15,14 @@ module.exports.USED_ERRORS = [ 'AuthorizationErrors.AccessTokenExpired', 'AuthorizationErrors.AccessTokenInvalid', 'AuthorizationErrors.AuthenticationFailed', + 'AuthorizationErrors.PayloadSizeExceededError', + 'AuthorizationErrors.RateExceededError', 'ClientErrors.BadRequest', 'GeneralErrors.CallCancelledError', 'GeneralErrors.ConnectionError', 'GeneralErrors.TransportError', 'GeneralErrors.UnknownError', + 'MalformedRequestErrors.MalformedRequestError', 'MediaErrors.ClientLocalDescFailed', 'MediaErrors.ClientRemoteDescFailed', 'MediaErrors.ConnectionError', diff --git a/tests/index.ts b/tests/index.ts index d7ebd10d..97713513 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -95,3 +95,4 @@ require('./unit/statsMonitor'); require('./unit/error'); require('./unit/log'); require('./unit/regions'); +require('./unit/uuid'); diff --git a/tests/peerconnection.js b/tests/peerconnection.js index eba29ec1..a8531354 100644 --- a/tests/peerconnection.js +++ b/tests/peerconnection.js @@ -824,7 +824,7 @@ describe('PeerConnection', () => { eCallSid, eConstraints, eIceServers, - callback + callback, ); }); diff --git a/tests/pstream.js b/tests/pstream.js index 83d8a5f8..41d883a2 100644 --- a/tests/pstream.js +++ b/tests/pstream.js @@ -249,6 +249,47 @@ describe('PStream', () => { }); }); + describe('sendMessage', () => { + const callsid = 'testcallsid'; + const content = { foo: 'content' }; + const messagetype = 'user-defined-message'; + const voiceeventsid = 'testvoiceeventsid'; + + it('should send a message with the provided info', () => { + pstream.sendMessage(callsid, content, undefined, messagetype, voiceeventsid); + assert.equal(pstream.transport.send.callCount, 1); + console.log(pstream.transport.send.args[0][0]) + assert.deepEqual(JSON.parse(pstream.transport.send.args[0][0]), { + type: 'message', + version:EXPECTED_PSTREAM_VERSION, + payload: { + callsid, + content, + contenttype: 'application/json', + messagetype, + voiceeventsid, + } + }); + }); + + it('should override contenttype', () => { + pstream.sendMessage(callsid, content, 'text/plain', messagetype, voiceeventsid); + assert.equal(pstream.transport.send.callCount, 1); + console.log(pstream.transport.send.args[0][0]) + assert.deepEqual(JSON.parse(pstream.transport.send.args[0][0]), { + type: 'message', + version:EXPECTED_PSTREAM_VERSION, + payload: { + callsid, + content, + contenttype: 'text/plain', + messagetype, + voiceeventsid, + } + }); + }); + }); + describe('destroy', () => { it('should return this', () => { assert.equal(pstream.destroy(), pstream); diff --git a/tests/unit/call.ts b/tests/unit/call.ts index dbab4a8d..3e5b3793 100644 --- a/tests/unit/call.ts +++ b/tests/unit/call.ts @@ -4,7 +4,7 @@ import * as assert from 'assert'; import { EventEmitter } from 'events'; import { SinonFakeTimers, SinonSpy, SinonStubbedInstance } from 'sinon'; import * as sinon from 'sinon'; -import { GeneralErrors, MediaErrors } from '../../lib/twilio/errors'; +import { GeneralErrors, MediaErrors, InvalidStateError, InvalidArgumentError } from '../../lib/twilio/errors'; const Util = require('../../lib/twilio/util'); @@ -24,6 +24,9 @@ describe('Call', function() { let publisher: any; let rtcConfig: RTCConfiguration; let soundcache: Map; + let voiceEventSidGenerator: () => string; + + const wait = (timeout?: number) => new Promise(r => setTimeout(r, timeout || 0)); const MediaHandler = () => { mediaHandler = createEmitterStub(require('../../lib/twilio/rtc/peerconnection')); @@ -69,6 +72,8 @@ describe('Call', function() { soundcache.set(soundName, { play: sinon.spy() }); }); + voiceEventSidGenerator = sinon.stub().returns('foobar-voice-event-sid'); + config = { audioHelper, getUserMedia, @@ -82,6 +87,7 @@ describe('Call', function() { options = { MediaHandler, StatsMonitor, + voiceEventSidGenerator, }; conn = new Call(config, options); @@ -975,6 +981,98 @@ describe('Call', function() { }); }); + describe('.sendMessage()', () => { + let message: Call.Message; + + beforeEach(() => { + message = { + content: { foo: 'foo' }, + messageType: Call.MessageType.UserDefinedMessage, + }; + }); + + context('when messageType is invalid', () => { + [ + undefined, + null, + {}, + 1234, + true, + ].forEach((messageType: any) => { + it(`should throw on ${JSON.stringify(messageType)}`, () => { + assert.throws( + () => conn.sendMessage({ ...message, messageType }), + new InvalidArgumentError( + '`messageType` must be an enumeration value of ' + + '`Call.MessageType` or a string.', + ), + ); + }); + }); + }); + + context('when content is invalid', () => { + [ + undefined, + null, + ].forEach((content: any) => { + it(`should throw on ${JSON.stringify(content)}`, () => { + assert.throws( + () => conn.sendMessage({ ...message, content }), + new InvalidArgumentError('`content` is empty'), + ); + }); + }); + }); + + it('should throw if pstream is unavailable', () => { + // @ts-ignore + conn._pstream = null; + assert.throws( + () => conn.sendMessage(message), + new InvalidStateError( + 'Could not send CallMessage; Signaling channel is disconnected', + ), + ); + }); + + it('should throw if the call sid is not set', () => { + assert.throws( + () => conn.sendMessage(message), + new InvalidStateError( + 'Could not send CallMessage; Call has no CallSid', + ), + ); + }); + + it('should invoke pstream.sendMessage', () => { + const mockCallSid = conn.parameters.CallSid = 'foobar-callsid'; + conn.sendMessage(message); + sinon.assert.calledOnceWithExactly( + pstream.sendMessage, + mockCallSid, + message.content, + undefined, + Call.MessageType.UserDefinedMessage, + 'foobar-voice-event-sid', + ); + }); + + it('should return voiceEventSid', () => { + conn.parameters.CallSid = 'foobar-callsid'; + const sid = conn.sendMessage(message); + assert.strictEqual(sid, 'foobar-voice-event-sid'); + }); + + it('should generate a voiceEventSid', () => { + conn.parameters.CallSid = 'foobar-callsid'; + conn.sendMessage(message); + sinon.assert.calledOnceWithExactly( + voiceEventSidGenerator as sinon.SinonStub, + ); + }); + }); + describe('.sendDigits()', () => { context('when digit string is invalid', () => { [ @@ -1601,6 +1699,183 @@ describe('Call', function() { }); }); }); + + describe('pstream.ack event', () => { + const mockcallsid = 'mock-callsid'; + let mockMessagePayload: any; + let mockAckPayload: any; + + beforeEach(() => { + mockMessagePayload = { + content: { + foo: 'bar', + }, + contentType: 'application/json', + messageType: 'foo-bar', + }; + + mockAckPayload = { + acktype: 'message', + callsid: mockcallsid, + voiceeventsid: 'mockvoiceeventsid', + }; + + conn.parameters = { + ...conn.parameters, + CallSid: mockcallsid, + }; + clock.restore(); + }); + + it('should emit messageSent', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve) => { + conn.on('messageSent', resolve); + }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + assert.deepEqual(await payloadPromise, { ...mockMessagePayload, voiceEventSid: sid }); + }); + + it('should ignore ack when callSids do not match', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve, reject) => { + conn.on('messageSent', reject); + }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid, callsid: 'foo' }); + await Promise.race([ + wait(1), + payloadPromise, + ]); + }); + + it('should not emit messageSent when acktype is not `message`', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve, reject) => { + conn.on('messageSent', reject); + }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid, acktype: 'foo' }); + await Promise.race([ + wait(1), + payloadPromise, + ]); + }); + + it('should not emit messageSent if voiceEventSid was not previously sent', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve, reject) => { + conn.on('messageSent', reject); + }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: 'foo' }); + await Promise.race([ + wait(1), + payloadPromise, + ]); + }); + + it('should emit messageSent only once', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const handler = sinon.stub(); + const payloadPromise = new Promise((resolve) => { + conn.on('messageSent', () => { + handler(); + resolve(null); + }); + }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + await payloadPromise; + sinon.assert.calledOnce(handler); + }); + + it('should not emit messageSent after an error', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve, reject) => { + conn.on('messageSent', reject); + }); + pstream.emit('error', { callsid: mockcallsid, voiceeventsid: sid }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + await Promise.race([ + wait(1), + payloadPromise, + ]); + }); + + it('should ignore errors with different callsid', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve) => { + conn.on('messageSent', resolve); + }); + pstream.emit('error', { callsid: 'foo', voiceeventsid: sid }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + await payloadPromise; + }); + + it('should ignore errors with different voiceeventsid', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve) => { + conn.on('messageSent', resolve); + }); + pstream.emit('error', { callsid: mockcallsid, voiceeventsid: 'foo' }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + await payloadPromise; + }); + + it('should ignore errors with missing voiceeventsid', async () => { + const sid = conn.sendMessage(mockMessagePayload); + const payloadPromise = new Promise((resolve) => { + conn.on('messageSent', resolve); + }); + pstream.emit('error', { callsid: mockcallsid }); + pstream.emit('ack', { ...mockAckPayload, voiceeventsid: sid }); + await payloadPromise; + }); + }); + + describe('pstream.message event', () => { + const mockcallsid = 'mock-callsid'; + let mockPayload: any; + + beforeEach(() => { + mockPayload = { + callsid: mockcallsid, + content: { + foo: 'bar', + }, + contenttype: 'application/json', + messagetype: 'foo-bar', + voiceeventsid: 'mock-voiceeventsid-foobar', + }; + conn.parameters = { + ...conn.parameters, + CallSid: mockcallsid, + }; + clock.restore(); + }); + + it('should emit messageReceived', async () => { + const payloadPromise = new Promise((resolve) => { + conn.on('messageReceived', resolve); + }); + pstream.emit('message', mockPayload); + assert.deepEqual(await payloadPromise, { + content: mockPayload.content, + contentType: mockPayload.contenttype, + messageType: mockPayload.messagetype, + voiceEventSid: mockPayload.voiceeventsid, + }); + }); + + it('should ignore messages when callSids do not match', async () => { + const messagePromise = new Promise((resolve, reject) => { + conn.on('messageReceived', reject); + }); + pstream.emit('message', { ...mockPayload, callsid: 'foo' }); + await Promise.race([ + wait(1), + messagePromise, + ]); + }); + }); }); describe('on monitor.sample', () => { diff --git a/tests/unit/uuid.ts b/tests/unit/uuid.ts new file mode 100644 index 00000000..390fbc3f --- /dev/null +++ b/tests/unit/uuid.ts @@ -0,0 +1,102 @@ +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { NotSupportedError } from '../../lib/twilio/errors'; +import { generateVoiceEventSid } from '../../lib/twilio/uuid'; + +const root = global as any; + +describe('uuid util', () => { + let originalWindow: any; + let randomUUID: sinon.SinonStub; + let getRandomValues: sinon.SinonSpy<[Uint32Array], Uint32Array>; + let Uint32ArrMock: sinon.SinonSpy<[number], Uint32Array>; + + function injectWindow(injections: Record) { + root.window = { + ...root.window, + ...injections, + }; + } + + beforeEach(() => { + originalWindow = root.window; + + randomUUID = sinon + .stub() + .returns('here\'s a random uuid for you to use'); + + getRandomValues = sinon + .spy((arr: Uint32Array) => { + return arr.fill(42); + }); + + Uint32ArrMock = sinon.spy((n: number) => new Uint32Array(n)); + }); + + afterEach(() => { + root.window = originalWindow; + }); + + describe('generateVoiceEventSid', () => { + it('should throw if window is not available', () => { + root.window = undefined; + assert.throws( + () => generateVoiceEventSid(), + new NotSupportedError('This platform is not supported.'), + ); + }); + + it('should throw if crypto is not available', () => { + injectWindow({ Uint32Array: Uint32ArrMock }); + assert.throws( + () => generateVoiceEventSid(), + new NotSupportedError('The `crypto` module is not available on this platform.'), + ); + }); + + it('should throw if neither randomUUID or getRandomValues are available', () => { + injectWindow({ crypto: {}, Uint32Array: Uint32ArrMock }); + assert.throws( + () => generateVoiceEventSid(), + new NotSupportedError( + 'Neither `crypto.randomUUID` or `crypto.getRandomValues` are ' + + 'available on this platform.', + ), + ); + }); + + it('should throw if Uint32Array is not available', () => { + injectWindow({ crypto: { randomUUID, getRandomValues } }); + assert.throws( + () => generateVoiceEventSid(), + new NotSupportedError('The `Uint32Array` module is not available on this platform.'), + ); + }); + + it('should generate a uuid using randomUUID', () => { + injectWindow({ crypto: { randomUUID }, Uint32Array: Uint32ArrMock }); + assert(typeof generateVoiceEventSid() === 'string'); + sinon.assert.calledOnce(randomUUID); + }); + + it('should generate a uuid using getRandomValues', () => { + injectWindow({ crypto: { getRandomValues }, Uint32Array: Uint32ArrMock }); + assert(typeof generateVoiceEventSid() === 'string'); + sinon.assert.calledOnce(getRandomValues); + }); + + it('should prefer randomUUID if both randomUUID and getRandomValues are available', () => { + injectWindow({ crypto: { randomUUID, getRandomValues }, Uint32Array: Uint32ArrMock }); + assert(typeof generateVoiceEventSid() === 'string'); + sinon.assert.calledOnce(randomUUID); + sinon.assert.notCalled(getRandomValues); + }); + + it('should be prefixed with `KX`', () => { + injectWindow({ crypto: { randomUUID }, Uint32Array: Uint32ArrMock }); + const sid = generateVoiceEventSid(); + const matches = /^KX(.+)$/.exec(sid); + assert(matches); + }); + }); +});