queryParams['sessionId'];
const secret = queryParams['secret'];
const recorder = queryParams['recorder'];
- const coturnIp = queryParams['coturnIp'];
- const turnUsername = queryParams['turnUsername'];
- const turnCredential = queryParams['turnCredential'];
- const role = queryParams['role'];
const webrtcStatsInterval = queryParams['webrtcStatsInterval'];
- const openviduServerVersion = queryParams['version'];
if (!!secret) {
this.openvidu.secret = secret;
@@ -1285,31 +1399,9 @@ export class Session extends EventDispatcher {
if (!!recorder) {
this.openvidu.recorder = true;
}
- if (!!turnUsername && !!turnCredential) {
- const stunUrl = 'stun:' + coturnIp + ':3478';
- const turnUrl1 = 'turn:' + coturnIp + ':3478';
- const turnUrl2 = turnUrl1 + '?transport=tcp';
- this.openvidu.iceServers = [
- { urls: [stunUrl] },
- { urls: [turnUrl1, turnUrl2], username: turnUsername, credential: turnCredential }
- ];
- logger.log("STUN/TURN server IP: " + coturnIp);
- logger.log('TURN temp credentials [' + turnUsername + ':' + turnCredential + ']');
- }
- if (!!role) {
- this.openvidu.role = role;
- }
if (!!webrtcStatsInterval) {
this.openvidu.webrtcStatsInterval = +webrtcStatsInterval;
}
- if (!!openviduServerVersion) {
- logger.info("openvidu-server version: " + openviduServerVersion);
- if (openviduServerVersion !== this.openvidu.libraryVersion) {
- logger.warn('OpenVidu Server (' + openviduServerVersion +
- ') and OpenVidu Browser (' + this.openvidu.libraryVersion +
- ') versions do NOT match. There may be incompatibilities')
- }
- }
this.openvidu.wsUri = 'wss://' + url.host + '/openvidu';
this.openvidu.httpUri = 'https://' + url.host;
@@ -1319,4 +1411,32 @@ export class Session extends EventDispatcher {
}
}
+ private processJoinRoomResponse(opts: LocalConnectionOptions) {
+ this.sessionId = opts.session;
+ if (opts.coturnIp != null && opts.turnUsername != null && opts.turnCredential != null) {
+ const stunUrl = 'stun:' + opts.coturnIp + ':3478';
+ const turnUrl1 = 'turn:' + opts.coturnIp + ':3478';
+ const turnUrl2 = turnUrl1 + '?transport=tcp';
+ this.openvidu.iceServers = [
+ { urls: [stunUrl] },
+ { urls: [turnUrl1, turnUrl2], username: opts.turnUsername, credential: opts.turnCredential }
+ ];
+ logger.log("STUN/TURN server IP: " + opts.coturnIp);
+ logger.log('TURN temp credentials [' + opts.turnUsername + ':' + opts.turnCredential + ']');
+ }
+ this.openvidu.role = opts.role;
+ this.capabilities = {
+ subscribe: true,
+ publish: this.openvidu.role !== 'SUBSCRIBER',
+ forceUnpublish: this.openvidu.role === 'MODERATOR',
+ forceDisconnect: this.openvidu.role === 'MODERATOR'
+ };
+ logger.info("openvidu-server version: " + opts.version);
+ if (opts.version !== this.openvidu.libraryVersion) {
+ logger.warn('OpenVidu Server (' + opts.version +
+ ') and OpenVidu Browser (' + this.openvidu.libraryVersion +
+ ') versions do NOT match. There may be incompatibilities')
+ }
+ }
+
}
diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts
index bb06f680..5198dc14 100644
--- a/openvidu-browser/src/OpenVidu/Stream.ts
+++ b/openvidu-browser/src/OpenVidu/Stream.ts
@@ -30,19 +30,22 @@ import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpea
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
+import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
+import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
import hark = require('hark');
-import platform = require('platform');
-import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
-
+/**
+ * @hidden
+ */
+let platform: PlatformUtils;
/**
* Represents each one of the media streams available in OpenVidu Server for certain session.
@@ -108,7 +111,7 @@ export class Stream extends EventDispatcher {
* - `"SCREEN"`: when the video source comes from screen-sharing.
* - `"CUSTOM"`: when [[PublisherProperties.videoSource]] has been initialized in the Publisher side with a custom MediaStreamTrack when calling [[OpenVidu.initPublisher]]).
* - `"IPCAM"`: when the video source comes from an IP camera participant instead of a regular participant (see [IP cameras](/en/stable/advanced-features/ip-cameras/)).
- *
+ *
* If [[hasVideo]] is false, this property is undefined
*/
typeOfVideo?: string;
@@ -136,10 +139,10 @@ export class Stream extends EventDispatcher {
* [[Filter.execMethod]] and remove it with [[Stream.removeFilter]]. Be aware that the client calling this methods must have the
* necessary permissions: the token owned by the client must have been initialized with the appropriated `allowedFilters` array.
*/
- filter: Filter;
+ filter?: Filter;
protected webRtcPeer: WebRtcPeer;
- protected mediaStream: MediaStream;
+ protected mediaStream?: MediaStream;
private webRtcStats: WebRtcStats;
private isSubscribeToRemote = false;
@@ -203,7 +206,7 @@ export class Stream extends EventDispatcher {
/**
* @hidden
*/
- localMediaStreamWhenSubscribedToRemote: MediaStream;
+ localMediaStreamWhenSubscribedToRemote?: MediaStream;
/**
@@ -212,7 +215,7 @@ export class Stream extends EventDispatcher {
constructor(session: Session, options: InboundStreamOptions | OutboundStreamOptions | {}) {
super();
-
+ platform = PlatformUtils.getInstance();
this.session = session;
if (options.hasOwnProperty('id')) {
@@ -262,7 +265,7 @@ export class Stream extends EventDispatcher {
}
this.ee.on('mediastream-updated', () => {
- this.streamManager.updateMediaStream(this.mediaStream);
+ this.streamManager.updateMediaStream(this.mediaStream!);
logger.debug('Video srcObject [' + this.mediaStream + '] updated in stream [' + this.streamId + ']');
});
}
@@ -323,7 +326,7 @@ export class Stream extends EventDispatcher {
}
} else {
logger.info('Filter successfully applied on Stream ' + this.streamId);
- const oldValue: Filter = this.filter;
+ const oldValue: Filter = this.filter!;
this.filter = new Filter(type, options);
this.filter.stream = this;
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]);
@@ -356,10 +359,10 @@ export class Stream extends EventDispatcher {
}
} else {
logger.info('Filter successfully removed from Stream ' + this.streamId);
- const oldValue = this.filter;
+ const oldValue = this.filter!;
delete this.filter;
- this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter, oldValue, 'applyFilter')]);
- this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter, oldValue, 'applyFilter')]);
+ this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this, 'filter', this.filter!, oldValue, 'applyFilter')]);
+ this.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.streamManager, this, 'filter', this.filter!, oldValue, 'applyFilter')]);
resolve();
}
}
@@ -382,7 +385,7 @@ export class Stream extends EventDispatcher {
* @returns Native MediaStream Web API object
*/
getMediaStream(): MediaStream {
- return this.mediaStream;
+ return this.mediaStream!;
}
/* Hidden methods */
@@ -537,7 +540,7 @@ export class Stream extends EventDispatcher {
*/
isSendScreen(): boolean {
let screen = this.outboundStreamOpts.publisherProperties.videoSource === 'screen';
- if (platform.name === 'Electron') {
+ if (platform.isElectron()) {
screen = typeof this.outboundStreamOpts.publisherProperties.videoSource === 'string' &&
this.outboundStreamOpts.publisherProperties.videoSource.startsWith('screen:');
}
@@ -776,7 +779,7 @@ export class Stream extends EventDispatcher {
if (!this.getWebRtcPeer() || !this.getRTCPeerConnection()) {
return false;
}
- if (this.isLocal && !!this.session.openvidu.advancedConfiguration.forceMediaReconnectionAfterNetworkDrop) {
+ if (this.isLocal() && !!this.session.openvidu.advancedConfiguration.forceMediaReconnectionAfterNetworkDrop) {
logger.warn('OpenVidu Browser advanced configuration option "forceMediaReconnectionAfterNetworkDrop" is enabled. Publisher stream ' + this.streamId + 'will force a reconnection');
return true;
}
@@ -1022,8 +1025,8 @@ export class Stream extends EventDispatcher {
}
}
- protected initHarkEvents(): void {
- if (!!this.mediaStream.getAudioTracks()[0]) {
+ private initHarkEvents(): void {
+ if (!!this.mediaStream!.getAudioTracks()[0]) {
// Hark events can only be set if audio track is available
if (this.streamManager.remote) {
// publisherStartSpeaking/publisherStopSpeaking is only defined for remote streams
@@ -1226,4 +1229,4 @@ export class Stream extends EventDispatcher {
(report.type === 'candidate-pair' && report.nominated && report.bytesSent > 0);
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-browser/src/OpenVidu/StreamManager.ts b/openvidu-browser/src/OpenVidu/StreamManager.ts
index 2025d69b..a2d3e03f 100644
--- a/openvidu-browser/src/OpenVidu/StreamManager.ts
+++ b/openvidu-browser/src/OpenVidu/StreamManager.ts
@@ -22,14 +22,19 @@ import { Event } from '../OpenViduInternal/Events/Event';
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
-
-import platform = require('platform');
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
+import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
+
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
+/**
+ * @hidden
+ */
+let platform: PlatformUtils;
+
/**
* Interface in charge of displaying the media streams in the HTML DOM. This wraps any [[Publisher]] and [[Subscriber]] object.
* You can insert as many video players fo the same Stream as you want by calling [[StreamManager.addVideoElement]] or
@@ -82,7 +87,7 @@ export class StreamManager extends EventDispatcher {
/**
* @hidden
*/
- firstVideoElement: StreamManagerVideo;
+ firstVideoElement?: StreamManagerVideo;
/**
* @hidden
*/
@@ -101,7 +106,7 @@ export class StreamManager extends EventDispatcher {
*/
constructor(stream: Stream, targetElement?: HTMLElement | string) {
super();
-
+ platform = PlatformUtils.getInstance();
this.stream = stream;
this.stream.streamManager = this;
this.remote = !this.stream.isLocal();
@@ -121,7 +126,7 @@ export class StreamManager extends EventDispatcher {
id: '',
canplayListenerAdded: false
};
- if (platform.name === 'Safari') {
+ if (platform.isSafariBrowser()) {
this.firstVideoElement.video.setAttribute('playsinline', 'true');
}
this.targetElement = targEl;
@@ -379,7 +384,7 @@ export class StreamManager extends EventDispatcher {
video.autoplay = true;
video.controls = false;
- if (platform.name === 'Safari') {
+ if (platform.isSafariBrowser()) {
video.setAttribute('playsinline', 'true');
}
@@ -463,7 +468,7 @@ export class StreamManager extends EventDispatcher {
updateMediaStream(mediaStream: MediaStream) {
this.videos.forEach(streamManagerVideo => {
streamManagerVideo.video.srcObject = mediaStream;
- if (platform['isIonicIos']) {
+ if (platform.isIonicIos()) {
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
// the media stream to be updated
const vParent = streamManagerVideo.video.parentElement;
@@ -506,7 +511,7 @@ export class StreamManager extends EventDispatcher {
}
private mirrorVideo(video): void {
- if (!platform['isIonicIos']) {
+ if (!platform.isIonicIos()) {
video.style.transform = 'rotateY(180deg)';
video.style.webkitTransform = 'rotateY(180deg)';
}
diff --git a/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts
new file mode 100644
index 00000000..2747925e
--- /dev/null
+++ b/openvidu-browser/src/OpenViduInternal/Events/ConnectionPropertyChangedEvent.ts
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Connection } from '../../OpenVidu/Connection';
+import { Session } from '../../OpenVidu/Session';
+import { Event } from './Event';
+
+/**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * Defines event `connectionPropertyChanged` dispatched by [[Session]] object.
+ * This event is fired when any property of the local [[Connection]] object changes.
+ * The properties that may change are [[Connection.role]] and [[Connection.record]].
+ *
+ * The only way the Connection properties may change is by updating them through:
+ *
+ * - [API REST](/en/stable/reference-docs/REST-API/#patch-openviduapisessionsltsession_idgtconnectionltconnection_idgt)
+ * - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection)
+ * - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)
+ */
+export class ConnectionPropertyChangedEvent extends Event {
+
+ /**
+ * The Connection whose property has changed
+ */
+ connection: Connection;
+
+ /**
+ * The property of the stream that changed. This value is either `"role"` or `"record"`
+ */
+ changedProperty: string;
+
+ /**
+ * New value of the property (after change, current value)
+ */
+ newValue: Object;
+
+ /**
+ * Previous value of the property (before change)
+ */
+ oldValue: Object;
+
+ /**
+ * @hidden
+ */
+ constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) {
+ super(false, target, 'connectionPropertyChanged');
+ this.connection = connection;
+ this.changedProperty = changedProperty;
+ this.newValue = newValue;
+ this.oldValue = oldValue;
+ }
+
+ /**
+ * @hidden
+ */
+ // tslint:disable-next-line:no-empty
+ callDefaultBehavior() { }
+
+}
\ No newline at end of file
diff --git a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts
index bdb0fa58..ab327f17 100644
--- a/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts
+++ b/openvidu-browser/src/OpenViduInternal/Events/FilterEvent.ts
@@ -16,7 +16,6 @@
*/
import { Event } from './Event';
-import { Stream } from '../../OpenVidu/Stream';
import { Filter } from '../../OpenVidu/Filter';
diff --git a/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts
new file mode 100644
index 00000000..592e62a7
--- /dev/null
+++ b/openvidu-browser/src/OpenViduInternal/Events/NetworkQualityLevelChangedEvent.ts
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Event } from './Event';
+import { Session } from '../../OpenVidu/Session';
+import { Connection } from '../../OpenVidu/Connection';
+
+/**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * Defines event `networkQualityLevelChanged` dispatched by [[Session]].
+ * This event is fired when the network quality level of a [[Connection]] changes. See [network quality](/en/stable/advanced-features/network-quality/)
+ */
+export class NetworkQualityLevelChangedEvent extends Event {
+
+ /**
+ * New value of the network quality level
+ */
+ newValue: number;
+
+ /**
+ * Old value of the network quality level
+ */
+ oldValue: number;
+
+ /**
+ * Connection for whom the network quality level changed
+ */
+ connection: Connection
+
+ /**
+ * @hidden
+ */
+ constructor(target: Session, newValue: number, oldValue: number, connection: Connection) {
+ super(false, target, 'networkQualityLevelChanged');
+ this.newValue = newValue;
+ this.oldValue = oldValue;
+ this.connection = connection;
+ }
+
+ /**
+ * @hidden
+ */
+ // tslint:disable-next-line:no-empty
+ callDefaultBehavior() { }
+
+}
diff --git a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts
index 050c694e..c7eff22d 100644
--- a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts
+++ b/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts
@@ -62,12 +62,12 @@ export class SessionDisconnectedEvent extends Event {
// Dispose and delete all remote Connections
for (const connectionId in session.remoteConnections) {
if (!!session.remoteConnections[connectionId].stream) {
- session.remoteConnections[connectionId].stream.disposeWebRtcPeer();
- session.remoteConnections[connectionId].stream.disposeMediaStream();
- if (session.remoteConnections[connectionId].stream.streamManager) {
- session.remoteConnections[connectionId].stream.streamManager.removeAllVideos();
+ session.remoteConnections[connectionId].stream!.disposeWebRtcPeer();
+ session.remoteConnections[connectionId].stream!.disposeMediaStream();
+ if (session.remoteConnections[connectionId].stream!.streamManager) {
+ session.remoteConnections[connectionId].stream!.streamManager.removeAllVideos();
}
- delete session.remoteStreamsCreated[session.remoteConnections[connectionId].stream.streamId];
+ delete session.remoteStreamsCreated[session.remoteConnections[connectionId].stream!.streamId];
session.remoteConnections[connectionId].dispose();
}
delete session.remoteConnections[connectionId];
diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts
index 88d57dff..93ff0809 100644
--- a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts
+++ b/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts
@@ -101,8 +101,8 @@ export class StreamEvent extends Event {
// Delete StreamOptionsServer from remote Connection
const remoteConnection = this.stream.session.remoteConnections[this.stream.connection.connectionId];
- if (!!remoteConnection && !!remoteConnection.options) {
- const streamOptionsServer = remoteConnection.options.streams;
+ if (!!remoteConnection && !!remoteConnection.remoteOptions) {
+ const streamOptionsServer = remoteConnection.remoteOptions.streams;
for (let i = streamOptionsServer.length - 1; i >= 0; --i) {
if (streamOptionsServer[i].id === this.stream.streamId) {
streamOptionsServer.splice(i, 1);
diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts
new file mode 100644
index 00000000..b53c4940
--- /dev/null
+++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { RemoteConnectionOptions } from './RemoteConnectionOptions';
+
+export interface LocalConnectionOptions {
+ id: string;
+ createdAt: number;
+ metadata: string;
+ value: RemoteConnectionOptions[];
+ session: string; // OpenVidu Session identifier
+ sessionId: string; // JSON-RPC session identifier
+ role: string;
+ record: boolean;
+ coturnIp: string;
+ turnUsername: string;
+ turnCredential: string;
+ version: string;
+}
\ No newline at end of file
diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/ConnectionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts
similarity index 94%
rename from openvidu-browser/src/OpenViduInternal/Interfaces/Private/ConnectionOptions.ts
rename to openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts
index 12b38df7..250d406d 100644
--- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/ConnectionOptions.ts
+++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/RemoteConnectionOptions.ts
@@ -17,7 +17,7 @@
import { StreamOptionsServer } from './StreamOptionsServer';
-export interface ConnectionOptions {
+export interface RemoteConnectionOptions {
id: string;
createdAt: number;
metadata: string;
diff --git a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js
index c4a5b7a8..e443a56c 100644
--- a/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js
+++ b/openvidu-browser/src/OpenViduInternal/KurentoUtils/kurento-jsonrpc/clients/jsonrpcclient.js
@@ -17,6 +17,7 @@
var RpcBuilder = require('../');
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
+var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger;
Date.now = Date.now || function () {
return +new Date;
@@ -28,12 +29,11 @@ var RECONNECTING = 'RECONNECTING';
var CONNECTED = 'CONNECTED';
var DISCONNECTED = 'DISCONNECTED';
-var Logger = console;
+var Logger = OpenViduLogger.getInstance();
/**
*
* heartbeat: interval in ms for each heartbeat message,
- * sendCloseMessage : true / false, before closing the connection, it sends a closeSession message
*
* ws : {
* uri : URI to conntect to,
@@ -250,25 +250,13 @@ function JsonRpcClient(configuration) {
this.close = function (code, reason) {
Logger.debug("Closing with code: " + code + " because: " + reason);
-
if (pingInterval != undefined) {
Logger.debug("Clearing ping interval");
clearInterval(pingInterval);
}
pingPongStarted = false;
enabledPings = false;
-
- if (configuration.sendCloseMessage) {
- Logger.debug("Sending close message")
- this.send('closeSession', null, function (error, result) {
- if (error) {
- Logger.error("Error sending close message: " + JSON.stringify(error));
- }
- ws.close(code, reason);
- });
- } else {
- ws.close(code, reason);
- }
+ ws.close(code, reason);
}
// This method is only for testing
diff --git a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js b/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js
index 5947e717..4042f6a3 100644
--- a/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js
+++ b/openvidu-browser/src/OpenViduInternal/ScreenSharing/Screen-Capturing-Auto.js
@@ -13,7 +13,7 @@
getScreenId(function (error, sourceId, screen_constraints) {
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
// sourceId == null || 'string' || 'firefox'
-
+
if(microsoftEdge) {
navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
}
@@ -217,4 +217,4 @@ function postGetChromeExtensionStatusMessage() {
}, '*');
}
-exports.getScreenId = getScreenId;
\ No newline at end of file
+exports.getScreenId = window.getScreenId;
\ No newline at end of file
diff --git a/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts b/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts
new file mode 100644
index 00000000..3f4181a3
--- /dev/null
+++ b/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts
@@ -0,0 +1,205 @@
+import platform = require("platform");
+
+export class PlatformUtils {
+ protected static instance: PlatformUtils;
+ constructor() {}
+
+ static getInstance(): PlatformUtils {
+ if (!this.instance) {
+ this.instance = new PlatformUtils();
+ }
+ return PlatformUtils.instance;
+ }
+
+ public isChromeBrowser(): boolean {
+ return platform.name === "Chrome";
+ }
+
+ /**
+ * @hidden
+ */
+ public isSafariBrowser(): boolean {
+ return platform.name === "Safari";
+ }
+
+ /**
+ * @hidden
+ */
+ public isChromeMobileBrowser(): boolean {
+ return platform.name === "Chrome Mobile";
+ }
+
+ /**
+ * @hidden
+ */
+ public isFirefoxBrowser(): boolean {
+ return platform.name === "Firefox";
+ }
+
+ /**
+ * @hidden
+ */
+ public isFirefoxMobileBrowser(): boolean {
+ return platform.name === "Firefox Mobile";
+ }
+
+ /**
+ * @hidden
+ */
+ public isOperaBrowser(): boolean {
+ return platform.name === "Opera";
+ }
+
+ /**
+ * @hidden
+ */
+ public isOperaMobileBrowser(): boolean {
+ return platform.name === "Opera Mobile";
+ }
+
+ /**
+ * @hidden
+ */
+ public isEdgeBrowser(): boolean {
+ const version = platform?.version ? parseFloat(platform.version) : -1;
+ return platform.name === "Microsoft Edge" && version >= 80;
+ }
+
+ /**
+ * @hidden
+ */
+ public isEdgeMobileBrowser(): boolean {
+ const version = platform?.version ? parseFloat(platform.version) : -1;
+ return platform.name === "Microsoft Edge" && platform.os?.family === 'Android' && version > 45;
+ }
+
+ /**
+ * @hidden
+ */
+ public isAndroidBrowser(): boolean {
+ return platform.name === "Android Browser";
+ }
+
+ /**
+ * @hidden
+ */
+ public isElectron(): boolean {
+ return platform.name === "Electron";
+ }
+
+ /**
+ * @hidden
+ */
+ public isSamsungBrowser(): boolean {
+ return (
+ platform.name === "Samsung Internet Mobile" ||
+ platform.name === "Samsung Internet"
+ );
+ }
+
+ /**
+ * @hidden
+ */
+ public isIPhoneOrIPad(): boolean {
+ const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
+
+ const isTouchable = "ontouchend" in document;
+ const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
+ const isIPhone =
+ /\b(\w*iPhone\w*)\b/.test(userAgent) &&
+ /\b(\w*Mobile\w*)\b/.test(userAgent) &&
+ isTouchable;
+
+ return isIPad || isIPhone;
+ }
+
+ /**
+ * @hidden
+ */
+ public isIOSWithSafari(): boolean {
+ const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
+ return (
+ /\b(\w*Apple\w*)\b/.test(navigator.vendor) &&
+ /\b(\w*Safari\w*)\b/.test(userAgent) &&
+ !/\b(\w*CriOS\w*)\b/.test(userAgent) &&
+ !/\b(\w*FxiOS\w*)\b/.test(userAgent)
+ );
+ }
+
+ /**
+ * @hidden
+ */
+ public isIonicIos(): boolean {
+ return this.isIPhoneOrIPad() && platform.ua!!.indexOf("Safari") === -1;
+ }
+
+ /**
+ * @hidden
+ */
+ public isIonicAndroid(): boolean {
+ return (
+ platform.os!!.family === "Android" && platform.name == "Android Browser"
+ );
+ }
+
+ /**
+ * @hidden
+ */
+ public isMobileDevice(): boolean {
+ return platform.os!!.family === "iOS" || platform.os!!.family === "Android";
+ }
+
+ /**
+ * @hidden
+ */
+ public isReactNative(): boolean {
+ return false;
+ }
+
+ /**
+ * @hidden
+ */
+ public canScreenShare(): boolean {
+ const version = platform?.version ? parseFloat(platform.version) : -1;
+ // Reject mobile devices
+ if (this.isMobileDevice()) {
+ return false;
+ }
+ return (
+ this.isChromeBrowser() ||
+ this.isFirefoxBrowser() ||
+ this.isOperaBrowser() ||
+ this.isElectron() ||
+ this.isEdgeBrowser() ||
+ (this.isSafariBrowser() && version >= 13)
+ );
+ }
+
+ /**
+ * @hidden
+ */
+ public getName(): string {
+ return platform.name || "";
+ }
+
+ /**
+ * @hidden
+ */
+ public getVersion(): string {
+ return platform.version || "";
+ }
+
+ /**
+ * @hidden
+ */
+ public getFamily(): string {
+ return platform.os!!.family || "";
+ }
+
+ /**
+ * @hidden
+ */
+ public getDescription(): string {
+ return platform.description || "";
+ }
+}
diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
index b36488d2..1fdf895a 100644
--- a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
+++ b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
@@ -17,12 +17,17 @@
import freeice = require('freeice');
import uuid = require('uuid');
-import platform = require('platform');
import { OpenViduLogger } from '../Logger/OpenViduLogger';
+import { PlatformUtils } from '../Utils/Platform';
+
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
+/**
+ * @hidden
+ */
+let platform: PlatformUtils;
export interface WebRtcPeerConfiguration {
@@ -50,6 +55,7 @@ export class WebRtcPeer {
private candidategatheringdone = false;
constructor(protected configuration: WebRtcPeerConfiguration) {
+ platform = PlatformUtils.getInstance();
this.configuration.iceServers = (!!this.configuration.iceServers && this.configuration.iceServers.length > 0) ? this.configuration.iceServers : freeice();
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
@@ -139,8 +145,7 @@ export class WebRtcPeer {
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
- if (platform.name === 'Safari' && platform.ua!!.indexOf('Safari') !== -1) {
-
+ if (platform.isSafariBrowser() && !platform.isIonicIos()) {
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
if (offerAudio) {
this.pc.addTransceiver('audio', {
@@ -280,8 +285,35 @@ export class WebRtcPeer {
/**
* @hidden
*/
+
+// FIXME CONFLICT WITH MASTER
+// In the mediasoup branch, the special treatment for ionic was removed:
+// openvidu-browser: removed Ionic iOS timeout on first subscription
+// https://github.com/OpenVidu/openvidu/commit/23d64be8063f8fdb2a212ca845e304762f2803f5
+// and also the method was converted into async and returned a Promise.
+// However in master the code is still like the old one.
+<<<<<<< HEAD
async setRemoteDescription(sdp: RTCSessionDescriptionInit): Promise {
return this.pc.setRemoteDescription(sdp);
+=======
+ setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike | undefined) => void, reject: (reason?: any) => void) {
+ if (platform.isIonicIos()) {
+ // Ionic iOS platform
+ if (needsTimeoutOnProcessAnswer) {
+ // 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
+ setTimeout(() => {
+ logger.info('setRemoteDescription run after timeout for Ionic iOS device');
+ this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
+ }, 250);
+ } else {
+ // 400 ms have elapsed
+ this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
+ }
+ } else {
+ // Rest of platforms
+ this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
+ }
+>>>>>>> master
}
/**
@@ -369,4 +401,4 @@ export class WebRtcPeerSendrecv extends WebRtcPeer {
configuration.mode = 'sendrecv';
super(configuration);
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts b/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts
index 502adc0e..3c1327b7 100644
--- a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts
+++ b/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts
@@ -18,13 +18,16 @@
// tslint:disable:no-string-literal
import { Stream } from '../../OpenVidu/Stream';
-import platform = require('platform');
import { OpenViduLogger } from '../Logger/OpenViduLogger';
+import { PlatformUtils } from '../Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
-
+/**
+ * @hidden
+ */
+let platform: PlatformUtils;
export class WebRtcStats {
@@ -60,7 +63,9 @@ export class WebRtcStats {
}
};
- constructor(private stream: Stream) { }
+ constructor(private stream: Stream) {
+ platform = PlatformUtils.getInstance();
+ }
public isEnabled(): boolean {
return this.webRtcStatsEnabled;
@@ -103,7 +108,7 @@ export class WebRtcStats {
return new Promise((resolve, reject) => {
this.getStatsAgnostic(this.stream.getRTCPeerConnection(),
(stats) => {
- if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
+ if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
let localCandidateId, remoteCandidateId, googCandidatePair;
const localCandidates = {};
const remoteCandidates = {};
@@ -181,7 +186,7 @@ export class WebRtcStats {
const f = (stats) => {
- if (platform.name!.indexOf('Firefox') !== -1) {
+ if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
stats.forEach((stat) => {
let json = {};
@@ -278,7 +283,7 @@ export class WebRtcStats {
sendPost(JSON.stringify(json));
}
});
- } else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
+ } else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
for (const key of Object.keys(stats)) {
const stat = stats[key];
if (stat.type === 'ssrc') {
@@ -377,7 +382,7 @@ export class WebRtcStats {
logger.log(response);
const standardReport = {};
- if (platform.name!.indexOf('Firefox') !== -1) {
+ if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
Object.keys(response).forEach(key => {
logger.log(response[key]);
});
@@ -400,13 +405,13 @@ export class WebRtcStats {
}
private getStatsAgnostic(pc, successCb, failureCb) {
- if (platform.name!.indexOf('Firefox') !== -1) {
+ if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) {
// getStats takes args in different order in Chrome and Firefox
return pc.getStats(null).then(response => {
const report = this.standardizeReport(response);
successCb(report);
}).catch(failureCb);
- } else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) {
+ } else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
// In Chrome, the first two arguments are reversed
return pc.getStats((response) => {
const report = this.standardizeReport(response);
diff --git a/openvidu-browser/src/index.ts b/openvidu-browser/src/index.ts
index 828697bf..8576b022 100644
--- a/openvidu-browser/src/index.ts
+++ b/openvidu-browser/src/index.ts
@@ -22,7 +22,9 @@ export { StreamEvent } from './OpenViduInternal/Events/StreamEvent';
export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent';
export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent';
export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent';
+export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent';
export { FilterEvent } from './OpenViduInternal/Events/FilterEvent';
+export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities';
export { Device } from './OpenViduInternal/Interfaces/Public/Device';
diff --git a/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java b/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java
index c2c47515..c39ece51 100644
--- a/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java
+++ b/openvidu-client/src/main/java/io/openvidu/client/OpenViduException.java
@@ -50,8 +50,10 @@ public class OpenViduException extends JsonRpcErrorException {
DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701),
-
- FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800);
+
+ FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800),
+
+ MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901);
private int value;
diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java
index f27a9e78..1c495679 100644
--- a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java
+++ b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java
@@ -98,6 +98,15 @@ public class ProtocolElements {
public static final String STREAMPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
public static final String STREAMPROPERTYCHANGED_REASON_PARAM = "reason";
+ public static final String CONNECTIONPERTYCHANGED_METHOD = "connectionPropertyChanged";
+ public static final String CONNECTIONROPERTYCHANGED_PROPERTY_PARAM = "property";
+ public static final String CONNECTIONPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
+
+ public static final String NETWORKQUALITYLEVELCHANGED_METHOD = "networkQualityLevelChanged";
+ public static final String NETWORKQUALITYCHANGED_CONNECTIONID_PARAM = "connectionId";
+ public static final String NETWORKQUALITYCHANGED_NEWVALUE_PARAM = "newValue";
+ public static final String NETWORKQUALITYCHANGED_OLDVALUE_PARAM = "oldValue";
+
public static final String FORCEDISCONNECT_METHOD = "forceDisconnect";
public static final String FORCEDISCONNECT_CONNECTIONID_PARAM = "connectionId";
@@ -127,12 +136,22 @@ public class ProtocolElements {
public static final String RECONNECTSTREAM_STREAM_PARAM = "stream";
public static final String RECONNECTSTREAM_SDPSTRING_PARAM = "sdpString";
+ public static final String VIDEODATA_METHOD = "videoData";
+
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
public static final String PARTICIPANTJOINED_METHOD = "participantJoined";
public static final String PARTICIPANTJOINED_USER_PARAM = "id";
public static final String PARTICIPANTJOINED_CREATEDAT_PARAM = "createdAt";
public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata";
+ public static final String PARTICIPANTJOINED_VALUE_PARAM = "value";
+ public static final String PARTICIPANTJOINED_SESSION_PARAM = "session";
+ public static final String PARTICIPANTJOINED_VERSION_PARAM = "version";
+ public static final String PARTICIPANTJOINED_RECORD_PARAM = "record";
+ public static final String PARTICIPANTJOINED_ROLE_PARAM = "role";
+ public static final String PARTICIPANTJOINED_COTURNIP_PARAM = "coturnIp";
+ public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername";
+ public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential";
public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId";
diff --git a/openvidu-java-client/pom.xml b/openvidu-java-client/pom.xml
index 5520e10f..e7f39c9b 100644
--- a/openvidu-java-client/pom.xml
+++ b/openvidu-java-client/pom.xml
@@ -10,7 +10,7 @@
openvidu-java-client
- 2.15.1
+ 2.16.0
jar
OpenVidu Java Client
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java
index c962c03a..bef16873 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java
@@ -18,77 +18,194 @@
package io.openvidu.java.client;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
/**
- * See {@link io.openvidu.java.client.Session#getActiveConnections()}
+ * See {@link io.openvidu.java.client.Session#getConnections()}
*/
public class Connection {
private String connectionId;
- private long createdAt;
- private OpenViduRole role;
- private String token;
+ private String status;
+ private Long createdAt;
+ private Long activeAt;
private String location;
private String platform;
- private String serverData;
private String clientData;
+ private ConnectionProperties connectionProperties;
+ private String token;
- protected Map publishers;
- protected List subscribers;
+ protected Map publishers = new ConcurrentHashMap<>();
+ protected List subscribers = new ArrayList<>();
- protected Connection(String connectionId, long createdAt, OpenViduRole role, String token, String location,
- String platform, String serverData, String clientData, Map publishers,
- List subscribers) {
- this.connectionId = connectionId;
- this.createdAt = createdAt;
- this.role = role;
- this.token = token;
- this.location = location;
- this.platform = platform;
- this.serverData = serverData;
- this.clientData = clientData;
- this.publishers = publishers;
- this.subscribers = subscribers;
+ protected Connection(JsonObject json) {
+ this.resetWithJson(json);
}
/**
- * Returns the identifier of the connection. You can call
- * {@link io.openvidu.java.client.Session#forceDisconnect(String)} passing this
- * property as parameter
+ * Returns the identifier of the Connection. You can call methods
+ * {@link io.openvidu.java.client.Session#forceDisconnect(String)} or
+ * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
+ * passing this property as parameter
*/
public String getConnectionId() {
return connectionId;
}
/**
- * Timestamp when this connection was established, in UTC milliseconds (ms since
- * Jan 1, 1970, 00:00:00 UTC)
+ * Returns the status of the Connection. Can be:
+ *
+ * pending
: if the Connection is waiting for any user to use
+ * its internal token to connect to the session, calling method Session.connect in OpenVidu Browser.
+ * active
: if the internal token of the Connection has already
+ * been used by some user to connect to the session, and it cannot be used
+ * again.
*/
- public long createdAt() {
+ public String getStatus() {
+ return this.status;
+ }
+
+ /**
+ * Timestamp when this Connection was created, in UTC milliseconds (ms since Jan
+ * 1, 1970, 00:00:00 UTC)
+ */
+ public Long createdAt() {
return this.createdAt;
}
/**
- * Returns the role of the connection
+ * Timestamp when this Connection was taken by a user (passing from status
+ * "pending" to "active"), in UTC milliseconds (ms since Jan 1, 1970, 00:00:00
+ * UTC)
+ */
+ public Long activeAt() {
+ return this.activeAt;
+ }
+
+ /**
+ * Returns the type of Connection.
+ */
+ public ConnectionType getType() {
+ return this.connectionProperties.getType();
+ }
+
+ /**
+ * Returns the data associated to the Connection on the server-side. This value
+ * is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)}
+ * when calling {@link io.openvidu.java.client.Session#generateToken()}
+ */
+ public String getServerData() {
+ return this.connectionProperties.getData();
+ }
+
+ /**
+ * Whether the streams published by this Connection will be recorded or not.
+ * This only affects INDIVIDUAL recording .
+ */
+ public boolean record() {
+ return this.connectionProperties.record();
+ }
+
+ /**
+ * Returns the role of the Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}
*/
public OpenViduRole getRole() {
- return role;
+ return this.connectionProperties.getRole();
}
/**
- * Returns the token associated to the connection
+ * Returns the RTSP URI of the Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public String getRtspUri() {
+ return this.connectionProperties.getRtspUri();
+ }
+
+ /**
+ * Whether the Connection uses adaptative bitrate (and therefore adaptative
+ * quality) or not. For local network connections that do not require media
+ * transcoding this can be disabled to save CPU power. If you are not sure if
+ * transcoding might be necessary, setting this property to false may
+ * result in media connections not being established .
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Boolean adaptativeBitrate() {
+ return this.connectionProperties.adaptativeBitrate();
+ }
+
+ /**
+ * Whether the IP camera stream of this Connection will only be enabled when
+ * some user is subscribed to it, or not. This allows you to reduce power
+ * consumption and network bandwidth in your server while nobody is asking to
+ * receive the camera's video. On the counterpart, first user subscribing to the
+ * IP camera stream will take a little longer to receive its video.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Boolean onlyPlayWithSubscribers() {
+ return this.connectionProperties.onlyPlayWithSubscribers();
+ }
+
+ /**
+ * Returns the size of the buffer of the endpoint receiving the IP camera's
+ * stream, in milliseconds. The smaller it is, the less delay the signal will
+ * have, but more problematic will be in unstable networks. Use short buffers
+ * only if there is a quality connection between the IP camera and OpenVidu
+ * Server.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Integer getNetworkCache() {
+ return this.connectionProperties.getNetworkCache();
+ }
+
+ /**
+ * Returns the token string associated to the Connection. This is the value that
+ * must be sent to the client-side to be consumed in OpenVidu Browser method
+ * Session.connect .
*/
public String getToken() {
- return token;
+ return this.token;
}
/**
- * PRO
+ * PRO
*
* Returns the geo location of the connection, with the following format:
* "CITY, COUNTRY"
("unknown"
if it wasn't possible to
@@ -106,20 +223,11 @@ public class Connection {
return platform;
}
- /**
- * Returns the data associated to the connection on the server-side. This value
- * is set with {@link io.openvidu.java.client.TokenOptions.Builder#data(String)}
- * when calling {@link io.openvidu.java.client.Session#generateToken()}
- */
- public String getServerData() {
- return serverData;
- }
-
/**
* Returns the data associated to the connection on the client-side. This value
- * is set with second parameter of method
- * Session.connect in OpenVidu Browser
+ * is set with second parameter of method Session.connect in OpenVidu Browser
*/
public String getClientData() {
return clientData;
@@ -147,8 +255,171 @@ public class Connection {
return this.subscribers;
}
+ protected JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ json.addProperty("connectionId", this.getConnectionId());
+ json.addProperty("status", this.getStatus());
+ json.addProperty("createdAt", this.createdAt());
+ json.addProperty("activeAt", this.activeAt());
+ json.addProperty("location", this.getLocation());
+ json.addProperty("platform", this.getPlatform());
+ json.addProperty("clientData", this.getClientData());
+ json.addProperty("token", this.getToken());
+
+ JsonObject jsonConnectionProperties = this.connectionProperties.toJson("");
+ jsonConnectionProperties.remove("session");
+ json.addProperty("serverData", jsonConnectionProperties.get("data").getAsString());
+ jsonConnectionProperties.remove("data");
+ jsonConnectionProperties.entrySet().forEach(entry -> {
+ json.add(entry.getKey(), entry.getValue());
+ });
+
+ JsonArray pubs = new JsonArray();
+ this.getPublishers().forEach(p -> {
+ pubs.add(p.toJson());
+ });
+ JsonArray subs = new JsonArray();
+ this.getSubscribers().forEach(s -> {
+ subs.add(s);
+ });
+ json.add("publishers", pubs);
+ json.add("subscribers", subs);
+ return json;
+ }
+
+ protected void overrideConnectionProperties(ConnectionProperties newConnectionProperties) {
+ ConnectionProperties.Builder builder = new ConnectionProperties.Builder();
+ // For now only properties role and record can be updated
+ if (newConnectionProperties.getRole() != null) {
+ builder.role(newConnectionProperties.getRole());
+ } else {
+ builder.role(this.connectionProperties.getRole());
+ }
+ if (newConnectionProperties.record() != null) {
+ builder.record(newConnectionProperties.record());
+ } else {
+ builder.record(this.connectionProperties.record());
+ }
+ // Keep old configuration in the rest of properties
+ builder.type(this.connectionProperties.getType()).data(this.connectionProperties.getData())
+ .kurentoOptions(this.connectionProperties.getKurentoOptions())
+ .rtspUri(this.connectionProperties.getRtspUri());
+ if (this.connectionProperties.adaptativeBitrate() != null) {
+ builder.adaptativeBitrate(this.connectionProperties.adaptativeBitrate());
+ }
+ if (this.connectionProperties.onlyPlayWithSubscribers() != null) {
+ builder.onlyPlayWithSubscribers(this.connectionProperties.onlyPlayWithSubscribers());
+ }
+ if (this.connectionProperties.getNetworkCache() != null) {
+ builder.networkCache(this.connectionProperties.getNetworkCache());
+ }
+ this.connectionProperties = builder.build();
+ }
+
protected void setSubscribers(List subscribers) {
this.subscribers = subscribers;
}
+ protected Connection resetWithJson(JsonObject json) {
+
+ this.connectionId = json.get("connectionId").getAsString();
+ this.status = json.get("status").getAsString();
+ this.token = !json.get("token").isJsonNull() ? json.get("token").getAsString() : null;
+
+ if (!json.get("publishers").isJsonNull()) {
+ JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray();
+
+ // 1. Set to store fetched publishers and later remove closed ones
+ Set fetchedPublisherIds = new HashSet<>();
+ jsonArrayPublishers.forEach(publisherJsonElement -> {
+
+ JsonObject publisherJson = publisherJsonElement.getAsJsonObject();
+ Publisher publisherObj = new Publisher(publisherJson);
+ String id = publisherObj.getStreamId();
+ fetchedPublisherIds.add(id);
+
+ // 2. Update existing Publisher
+ this.publishers.computeIfPresent(id, (pId, p) -> {
+ p = p.resetWithJson(publisherJson);
+ return p;
+ });
+
+ // 3. Add new Publisher
+ this.publishers.computeIfAbsent(id, pId -> {
+ return publisherObj;
+ });
+ });
+
+ // 4. Remove closed connections from local collection
+ this.publishers.entrySet().removeIf(entry -> !fetchedPublisherIds.contains(entry.getValue().getStreamId()));
+ }
+
+ if (!json.get("subscribers").isJsonNull()) {
+ JsonArray jsonArraySubscribers = json.get("subscribers").getAsJsonArray();
+
+ // 1. Array to store fetched Subscribers and later remove closed ones
+ Set fetchedSubscriberIds = new HashSet<>();
+ jsonArraySubscribers.forEach(subscriber -> {
+
+ String sub = subscriber.getAsJsonObject().get("streamId").getAsString();
+ fetchedSubscriberIds.add(sub);
+
+ if (!this.subscribers.contains(sub)) {
+ // 2. Add new Subscriber
+ this.subscribers.add(sub);
+ }
+ });
+
+ // 3. Remove closed Subscribers from local collection
+ this.subscribers.removeIf(subId -> !fetchedSubscriberIds.contains(subId));
+ }
+
+ if (!json.get("createdAt").isJsonNull()) {
+ this.createdAt = json.get("createdAt").getAsLong();
+ }
+ if (!json.get("activeAt").isJsonNull()) {
+ this.activeAt = json.get("activeAt").getAsLong();
+ }
+ if (!json.get("location").isJsonNull()) {
+ this.location = json.get("location").getAsString();
+ }
+ if (!json.get("platform").isJsonNull()) {
+ this.platform = json.get("platform").getAsString();
+ }
+ if (!json.get("clientData").isJsonNull()) {
+ this.clientData = json.get("clientData").getAsString();
+ }
+
+ // COMMON
+ ConnectionType type = ConnectionType.valueOf(json.get("type").getAsString());
+ String data = (json.has("serverData") && !json.get("serverData").isJsonNull())
+ ? json.get("serverData").getAsString()
+ : null;
+ Boolean record = (json.has("record") && !json.get("record").isJsonNull()) ? json.get("record").getAsBoolean()
+ : null;
+
+ // WEBRTC
+ OpenViduRole role = (json.has("role") && !json.get("role").isJsonNull())
+ ? OpenViduRole.valueOf(json.get("role").getAsString())
+ : null;
+
+ // IPCAM
+ String rtspUri = (json.has("rtspUri") && !json.get("rtspUri").isJsonNull()) ? json.get("rtspUri").getAsString()
+ : null;
+ Boolean adaptativeBitrate = (json.has("adaptativeBitrate") && !json.get("adaptativeBitrate").isJsonNull())
+ ? json.get("adaptativeBitrate").getAsBoolean()
+ : null;
+ Boolean onlyPlayWithSubscribers = (json.has("onlyPlayWithSubscribers")
+ && !json.get("onlyPlayWithSubscribers").isJsonNull())
+ ? json.get("onlyPlayWithSubscribers").getAsBoolean()
+ : null;
+ Integer networkCache = (json.has("networkCache") && !json.get("networkCache").isJsonNull())
+ ? json.get("networkCache").getAsInt()
+ : null;
+ this.connectionProperties = new ConnectionProperties(type, data, record, role, null, rtspUri, adaptativeBitrate,
+ onlyPlayWithSubscribers, networkCache);
+
+ return this;
+ }
+
}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionProperties.java
new file mode 100644
index 00000000..b68504c3
--- /dev/null
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionProperties.java
@@ -0,0 +1,403 @@
+package io.openvidu.java.client;
+
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+
+/**
+ * See
+ * {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
+ */
+public class ConnectionProperties {
+
+ private ConnectionType type;
+ // COMMON
+ private String data;
+ private Boolean record;
+ // WEBRTC
+ private OpenViduRole role;
+ private KurentoOptions kurentoOptions;
+ // IPCAM
+ private String rtspUri;
+ private Boolean adaptativeBitrate;
+ private Boolean onlyPlayWithSubscribers;
+ private Integer networkCache;
+
+ /**
+ *
+ * Builder for {@link io.openvidu.java.client.ConnectionProperties}
+ *
+ */
+ public static class Builder {
+
+ private ConnectionType type;
+ // COMMON
+ private String data;
+ private Boolean record;
+ // WEBRTC
+ private OpenViduRole role;
+ private KurentoOptions kurentoOptions;
+ // IPCAM
+ private String rtspUri;
+ private Boolean adaptativeBitrate;
+ private Boolean onlyPlayWithSubscribers;
+ private Integer networkCache;
+
+ /**
+ * Builder for {@link io.openvidu.java.client.ConnectionProperties}.
+ */
+ public ConnectionProperties build() {
+ return new ConnectionProperties(this.type, this.data, this.record, this.role, this.kurentoOptions,
+ this.rtspUri, this.adaptativeBitrate, this.onlyPlayWithSubscribers, this.networkCache);
+ }
+
+ /**
+ * Call this method to set the type of Connection. The
+ * {@link io.openvidu.java.client.ConnectionType} dictates what properties will
+ * have effect:
+ *
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}:
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)
+ * data},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
+ * record},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#role(OpenViduRole)
+ * role},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#kurentoOptions(KurentoOptions)
+ * kurentoOptions}
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}:
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)
+ * data},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
+ * record},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#rtspUri(String)
+ * rtspUri},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#adaptativeBitrate(boolean)
+ * adaptativeBitrate},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#onlyPlayWithSubscribers(boolean)
+ * onlyPlayWithSubscribers},
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#networkCache(int)
+ * networkCache}
+ *
+ * If not set by default will be
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}.
+ */
+ public Builder type(ConnectionType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Call this method to set the secure (server-side) data associated to this
+ * Connection. Every client will receive this data in property
+ * Connection.data
. Object Connection
can be retrieved
+ * by subscribing to event connectionCreated
of Session object in
+ * your clients.
+ *
+ * If you have provided no data in your clients when calling method
+ * Session.connect(TOKEN, DATA)
(DATA
not defined),
+ * then Connection.data
will only have this
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)}
+ * property.
+ * If you have provided some data when calling
+ * Session.connect(TOKEN, DATA)
(DATA
defined), then
+ * Connection.data
will have the following structure:
+ * "CLIENT_DATA%/%SERVER_DATA"
, being
+ * CLIENT_DATA
the second parameter passed in OpenVidu Browser in
+ * method Session.connect
and SERVER_DATA
this
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#data(String)}
+ * property.
+ *
+ */
+ public Builder data(String data) {
+ this.data = data;
+ return this;
+ }
+
+ /**
+ * Call this method to flag the streams published by this Connection to be
+ * recorded or not. This only affects INDIVIDUAL recording . If not set by default will be true.
+ */
+ public Builder record(boolean record) {
+ this.record = record;
+ return this;
+ }
+
+ /**
+ * Call this method to set the role assigned to this Connection. If not set by
+ * default will be {@link io.openvidu.java.client.OpenViduRole#PUBLISHER
+ * PUBLISHER}.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}
+ */
+ public Builder role(OpenViduRole role) {
+ this.role = role;
+ return this;
+ }
+
+ /**
+ * Call this method to set a {@link io.openvidu.java.client.KurentoOptions}
+ * object for this Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}
+ */
+ public Builder kurentoOptions(KurentoOptions kurentoOptions) {
+ this.kurentoOptions = kurentoOptions;
+ return this;
+ }
+
+ /**
+ * Call this method to set the RTSP URI of an IP camera. For example:
+ * rtsp://your.camera.ip:7777/path
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Builder rtspUri(String rtspUri) {
+ this.rtspUri = rtspUri;
+ return this;
+ }
+
+ /**
+ * Call this method to set whether to use adaptative bitrate (and therefore
+ * adaptative quality) or not. For local network connections that do not require
+ * media transcoding this can be disabled to save CPU power. If you are not sure
+ * if transcoding might be necessary, setting this property to false may
+ * result in media connections not being established . Default to
+ * true
.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Builder adaptativeBitrate(boolean adaptativeBitrate) {
+ this.adaptativeBitrate = adaptativeBitrate;
+ return this;
+ }
+
+ /**
+ * Call this method to set whether to enable the IP camera stream only when some
+ * user is subscribed to it, or not. This allows you to reduce power consumption
+ * and network bandwidth in your server while nobody is asking to receive the
+ * camera's video. On the counterpart, first user subscribing to the IP camera
+ * stream will take a little longer to receive its video. Default to
+ * true
.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Builder onlyPlayWithSubscribers(boolean onlyPlayWithSubscribers) {
+ this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
+ return this;
+ }
+
+ /**
+ * Call this method to set the size of the buffer of the endpoint receiving the
+ * IP camera's stream, in milliseconds. The smaller it is, the less delay the
+ * signal will have, but more problematic will be in unstable networks. Use
+ * short buffers only if there is a quality connection between the IP camera and
+ * OpenVidu Server. Default to 2000
.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Builder networkCache(int networkCache) {
+ this.networkCache = networkCache;
+ return this;
+ }
+ }
+
+ ConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role,
+ KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers,
+ Integer networkCache) {
+ this.type = type;
+ this.data = data;
+ this.record = record;
+ this.role = role;
+ this.kurentoOptions = kurentoOptions;
+ this.rtspUri = rtspUri;
+ this.adaptativeBitrate = adaptativeBitrate;
+ this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
+ this.networkCache = networkCache;
+ }
+
+ /**
+ * Returns the type of Connection.
+ */
+ public ConnectionType getType() {
+ return this.type;
+ }
+
+ /**
+ * Returns the secure (server-side) metadata assigned to this Connection.
+ */
+ public String getData() {
+ return this.data;
+ }
+
+ /**
+ * PRO Whether the streams published by this Connection will be
+ * recorded or not. This only affects INDIVIDUAL recording .
+ */
+ public Boolean record() {
+ return this.record;
+ }
+
+ /**
+ * Returns the role assigned to this Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}
+ */
+ public OpenViduRole getRole() {
+ return this.role;
+ }
+
+ /**
+ * Returns the KurentoOptions assigned to this Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#WEBRTC}
+ */
+ public KurentoOptions getKurentoOptions() {
+ return this.kurentoOptions;
+ }
+
+ /**
+ * Returns the RTSP URI of this Connection.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public String getRtspUri() {
+ return this.rtspUri;
+ }
+
+ /**
+ * Whether this Connection uses adaptative bitrate (and therefore adaptative
+ * quality) or not. For local network connections that do not require media
+ * transcoding this can be disabled to save CPU power. If you are not sure if
+ * transcoding might be necessary, setting this property to false may
+ * result in media connections not being established .
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Boolean adaptativeBitrate() {
+ return this.adaptativeBitrate;
+ }
+
+ /**
+ * Whether to enable the IP camera stream only when some user is subscribed to
+ * it. This allows you to reduce power consumption and network bandwidth in your
+ * server while nobody is asking to receive the camera's video. On the
+ * counterpart, first user subscribing to the IP camera stream will take a
+ * little longer to receive its video.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Boolean onlyPlayWithSubscribers() {
+ return this.onlyPlayWithSubscribers;
+ }
+
+ /**
+ * Size of the buffer of the endpoint receiving the IP camera's stream, in
+ * milliseconds. The smaller it is, the less delay the signal will have, but
+ * more problematic will be in unstable networks. Use short buffers only if
+ * there is a quality connection between the IP camera and OpenVidu Server.
+ *
+ *
+ *
+ * Only for
+ * {@link io.openvidu.java.client.ConnectionType#IPCAM}
+ */
+ public Integer getNetworkCache() {
+ return this.networkCache;
+ }
+
+ public JsonObject toJson(String sessionId) {
+ JsonObject json = new JsonObject();
+ json.addProperty("session", sessionId);
+ // COMMON
+ if (getType() != null) {
+ json.addProperty("type", getType().name());
+ } else {
+ json.add("type", JsonNull.INSTANCE);
+ }
+ if (getData() != null) {
+ json.addProperty("data", getData());
+ } else {
+ json.add("data", JsonNull.INSTANCE);
+ }
+ if (record() != null) {
+ json.addProperty("record", record());
+ } else {
+ json.add("record", JsonNull.INSTANCE);
+ }
+ // WEBRTC
+ if (getRole() != null) {
+ json.addProperty("role", getRole().name());
+ } else {
+ json.add("role", JsonNull.INSTANCE);
+ }
+ if (this.kurentoOptions != null) {
+ json.add("kurentoOptions", kurentoOptions.toJson());
+ } else {
+ json.add("kurentoOptions", JsonNull.INSTANCE);
+ }
+ // IPCAM
+ if (getRtspUri() != null) {
+ json.addProperty("rtspUri", getRtspUri());
+ } else {
+ json.add("rtspUri", JsonNull.INSTANCE);
+ }
+ if (adaptativeBitrate() != null) {
+ json.addProperty("adaptativeBitrate", adaptativeBitrate());
+ } else {
+ json.add("adaptativeBitrate", JsonNull.INSTANCE);
+ }
+ if (onlyPlayWithSubscribers() != null) {
+ json.addProperty("onlyPlayWithSubscribers", onlyPlayWithSubscribers());
+ } else {
+ json.add("onlyPlayWithSubscribers", JsonNull.INSTANCE);
+ }
+ if (getNetworkCache() != null) {
+ json.addProperty("networkCache", getNetworkCache());
+ } else {
+ json.add("networkCache", JsonNull.INSTANCE);
+ }
+ return json;
+ }
+
+}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionType.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionType.java
new file mode 100644
index 00000000..11c1d2a8
--- /dev/null
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionType.java
@@ -0,0 +1,20 @@
+package io.openvidu.java.client;
+
+/**
+ * See
+ * {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
+ */
+public enum ConnectionType {
+
+ /**
+ * WebRTC connection. This is the normal type of Connection for a regular user
+ * connecting to a session from an application.
+ */
+ WEBRTC,
+
+ /**
+ * IP camera connection. This is the type of Connection used by IP cameras to
+ * connect to a session.
+ */
+ IPCAM
+}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java
index c29730ac..0f5f5fa3 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java
@@ -17,6 +17,11 @@
package io.openvidu.java.client;
+import java.util.Arrays;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
/**
* See {@link io.openvidu.java.client.TokenOptions#getKurentoOptions()}
*/
@@ -105,10 +110,10 @@ public class KurentoOptions {
}
/**
- * Defines the maximum number of Kbps that the client owning the token will be
- * able to receive from Kurento Media Server. 0 means unconstrained. Giving a
- * value to this property will override the global configuration set in OpenVidu Server configuration (parameter
* OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH
) for every incoming
* stream of the user owning the token.
@@ -122,10 +127,10 @@ public class KurentoOptions {
}
/**
- * Defines the minimum number of Kbps that the client owning the token will try
- * to receive from Kurento Media Server. 0 means unconstrained. Giving a value
- * to this property will override the global configuration set in OpenVidu Server configuration (parameter
* OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH
) for every incoming
* stream of the user owning the token.
@@ -135,10 +140,10 @@ public class KurentoOptions {
}
/**
- * Defines the maximum number of Kbps that the client owning the token will be
- * able to send to Kurento Media Server. 0 means unconstrained. Giving a value
- * to this property will override the global configuration set in OpenVidu Server configuration (parameter
* OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH
) for every outgoing
* stream of the user owning the token.
@@ -151,10 +156,10 @@ public class KurentoOptions {
}
/**
- * Defines the minimum number of Kbps that the client owning the token will try
- * to send to Kurento Media Server. 0 means unconstrained. Giving a value to
- * this property will override the global configuration set in OpenVidu Server configuration (parameter
* OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH
) for every outgoing
* stream of the user owning the token.
@@ -164,13 +169,50 @@ public class KurentoOptions {
}
/**
- * Defines the names of the filters the user owning the token will be able to
- * apply. See
- * Voice and
- * video filters
+ * Defines the names of the filters the Connection will be able to apply to its
+ * published streams. See
+ * Voice and video filters .
*/
public String[] getAllowedFilters() {
return allowedFilters;
}
+ /**
+ * See if the Connection can apply certain filter. See
+ * Voice and video filters .
+ */
+ public boolean isFilterAllowed(String filterType) {
+ if (filterType == null) {
+ return false;
+ }
+ return Arrays.stream(allowedFilters).anyMatch(filterType::equals);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ if (this.getVideoMaxRecvBandwidth() != null) {
+ json.addProperty("videoMaxRecvBandwidth", this.getVideoMaxRecvBandwidth());
+ }
+ if (this.getVideoMinRecvBandwidth() != null) {
+ json.addProperty("videoMinRecvBandwidth", this.getVideoMinRecvBandwidth());
+ }
+ if (this.getVideoMaxSendBandwidth() != null) {
+ json.addProperty("videoMaxSendBandwidth", this.getVideoMaxSendBandwidth());
+ }
+ if (this.getVideoMinSendBandwidth() != null) {
+ json.addProperty("videoMinSendBandwidth", this.getVideoMinSendBandwidth());
+ }
+ if (this.getAllowedFilters().length > 0) {
+ JsonArray filtersJson = new JsonArray();
+ String[] filters = this.getAllowedFilters();
+ for (String filter : filters) {
+ filtersJson.add(filter);
+ }
+ json.add("allowedFilters", filtersJson);
+ }
+ return json;
+ }
+
}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java
index 298c07ee..502a458d 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java
@@ -31,7 +31,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
@@ -70,16 +69,19 @@ public class OpenVidu {
protected HttpClient httpClient;
protected Map activeSessions = new ConcurrentHashMap<>();
- protected final static String API_SESSIONS = "api/sessions";
- protected final static String API_TOKENS = "api/tokens";
- protected final static String API_RECORDINGS = "api/recordings";
- protected final static String API_RECORDINGS_START = "/start";
- protected final static String API_RECORDINGS_STOP = "/stop";
+ protected final static String API_PATH = "openvidu/api";
+ protected final static String API_SESSIONS = API_PATH + "/sessions";
+ protected final static String API_TOKENS = API_PATH + "/tokens";
+ protected final static String API_RECORDINGS = API_PATH + "/recordings";
+ protected final static String API_RECORDINGS_START = API_RECORDINGS + "/start";
+ protected final static String API_RECORDINGS_STOP = API_RECORDINGS + "/stop";
/**
- * @param urlOpenViduServer Public accessible IP where your instance of OpenVidu
- * Server is up an running
- * @param secret Secret used on OpenVidu Server initialization
+ * @param hostname URL where your instance of OpenVidu Server is up an running.
+ * It must be the full URL (e.g.
+ * https://12.34.56.78:1234/
)
+ *
+ * @param secret Secret used on OpenVidu Server initialization
*/
public OpenVidu(String hostname, String secret) {
@@ -192,16 +194,19 @@ public class OpenVidu {
public Recording startRecording(String sessionId, RecordingProperties properties)
throws OpenViduJavaClientException, OpenViduHttpException {
- HttpPost request = new HttpPost(this.hostname + API_RECORDINGS + API_RECORDINGS_START);
+ HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_START);
JsonObject json = new JsonObject();
json.addProperty("session", sessionId);
json.addProperty("name", properties.name());
- json.addProperty("outputMode", properties.outputMode().name());
+ json.addProperty("outputMode", properties.outputMode() != null ? properties.outputMode().name() : null);
json.addProperty("hasAudio", properties.hasAudio());
json.addProperty("hasVideo", properties.hasVideo());
+ json.addProperty("shmSize", properties.shmSize());
+ json.addProperty("mediaNode", properties.mediaNode());
- if ((Recording.OutputMode.COMPOSED.equals(properties.outputMode()) || (Recording.OutputMode.COMPOSED_QUICK_START.equals(properties.outputMode())))
+ if ((properties.outputMode() == null || Recording.OutputMode.COMPOSED.equals(properties.outputMode())
+ || (Recording.OutputMode.COMPOSED_QUICK_START.equals(properties.outputMode())))
&& properties.hasVideo()) {
json.addProperty("resolution", properties.resolution());
json.addProperty("recordingLayout",
@@ -236,8 +241,9 @@ public class OpenVidu {
if (activeSession != null) {
activeSession.setIsBeingRecorded(true);
} else {
- log.warn("No active session found for sessionId '" + r.getSessionId()
- + "'. This instance of OpenVidu Java Client didn't create this session");
+ log.warn(
+ "No active session found for sessionId '{}'. This instance of OpenVidu Java Client didn't create this session",
+ r.getSessionId());
}
return r;
} else {
@@ -352,7 +358,7 @@ public class OpenVidu {
*
*/
public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
- HttpPost request = new HttpPost(this.hostname + API_RECORDINGS + API_RECORDINGS_STOP + "/" + recordingId);
+ HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_STOP + "/" + recordingId);
HttpResponse response;
try {
response = this.httpClient.execute(request);
@@ -368,8 +374,9 @@ public class OpenVidu {
if (activeSession != null) {
activeSession.setIsBeingRecorded(false);
} else {
- log.warn("No active session found for sessionId '" + r.getSessionId()
- + "'. This instance of OpenVidu Java Client didn't create this session");
+ log.warn(
+ "No active session found for sessionId '{}'. This instance of OpenVidu Java Client didn't create this session",
+ r.getSessionId());
}
return r;
} else {
@@ -492,6 +499,10 @@ public class OpenVidu {
* since the last time method {@link io.openvidu.java.client.OpenVidu#fetch()}
* was called. Exceptions to this rule are:
*
+ * Calling
+ * {@link io.openvidu.java.client.OpenVidu#createSession(SessionProperties)
+ * OpenVidu.createSession} automatically adds the new Session object to the
+ * local collection.
* Calling {@link io.openvidu.java.client.Session#fetch()} updates that
* specific Session status
* Calling {@link io.openvidu.java.client.Session#close()} automatically
@@ -531,7 +542,7 @@ public class OpenVidu {
* @throws OpenViduJavaClientException
*/
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
- HttpGet request = new HttpGet(this.hostname + API_SESSIONS);
+ HttpGet request = new HttpGet(this.hostname + API_SESSIONS + "?pendingConnections=true");
HttpResponse response;
try {
@@ -543,44 +554,55 @@ public class OpenVidu {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
+
JsonObject jsonSessions = httpResponseToJson(response);
JsonArray jsonArraySessions = jsonSessions.get("content").getAsJsonArray();
- // Set to store fetched sessionIds and later remove closed sessions
- Set fetchedSessionIds = new HashSet<>();
// Boolean to store if any Session has changed
final boolean[] hasChanged = { false };
- jsonArraySessions.forEach(session -> {
- String sessionId = (session.getAsJsonObject()).get("sessionId").getAsString();
- fetchedSessionIds.add(sessionId);
- this.activeSessions.computeIfPresent(sessionId, (sId, s) -> {
+
+ // 1. Set to store fetched sessionIds and later remove closed ones
+ Set fetchedSessionIds = new HashSet<>();
+ jsonArraySessions.forEach(sessionJsonElement -> {
+
+ JsonObject sessionJson = sessionJsonElement.getAsJsonObject();
+ final Session sessionObj = new Session(this, sessionJson);
+ String id = sessionObj.getSessionId();
+ fetchedSessionIds.add(id);
+
+ // 2. Update existing Session
+ this.activeSessions.computeIfPresent(id, (sId, s) -> {
String beforeJSON = s.toJson();
- s = s.resetSessionWithJson(session.getAsJsonObject());
+ s = s.resetWithJson(sessionJson);
String afterJSON = s.toJson();
boolean changed = !beforeJSON.equals(afterJSON);
hasChanged[0] = hasChanged[0] || changed;
- log.info("Available session '{}' info fetched. Any change: {}", sessionId, changed);
+ log.info("Available session '{}' info fetched. Any change: {}", id, changed);
return s;
});
- this.activeSessions.computeIfAbsent(sessionId, sId -> {
- log.info("New session '{}' fetched", sessionId);
+
+ // 3. Add new Session
+ this.activeSessions.computeIfAbsent(id, sId -> {
+ log.info("New session '{}' fetched", id);
hasChanged[0] = true;
- return new Session(this, session.getAsJsonObject());
+ return sessionObj;
});
});
- // Remove closed sessions from activeSessions map
- this.activeSessions = this.activeSessions.entrySet().stream().filter(entry -> {
+ // 4. Remove closed sessions from local collection
+ this.activeSessions.entrySet().removeIf(entry -> {
if (fetchedSessionIds.contains(entry.getKey())) {
- return true;
+ return false;
} else {
log.info("Removing closed session {}", entry.getKey());
hasChanged[0] = true;
- return false;
+ return true;
}
- }).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+ });
+
log.info("Active sessions info fetched: {}", this.activeSessions.keySet());
return hasChanged[0];
+
} else {
throw new OpenViduHttpException(statusCode);
}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java
index 89030b51..7eeefe0e 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Publisher.java
@@ -17,16 +17,15 @@
package io.openvidu.java.client;
-import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* See {@link io.openvidu.java.client.Connection#getPublishers()}.
*
*
- * This is a backend representation of a published media stream (see
- * OpenVidu
- * Browser Stream class ).
+ * This is a backend representation of a published media stream (see OpenVidu Browser Stream class ).
*/
public class Publisher {
@@ -40,36 +39,18 @@ public class Publisher {
private String typeOfVideo;
private String videoDimensions;
- protected Publisher(String streamId, long createdAt, boolean hasAudio, boolean hasVideo, JsonElement audioActive,
- JsonElement videoActive, JsonElement frameRate, JsonElement typeOfVideo, JsonElement videoDimensions) {
- this.streamId = streamId;
- this.createdAt = createdAt;
- this.hasAudio = hasAudio;
- this.hasVideo = hasVideo;
- if (audioActive != null && !audioActive.isJsonNull()) {
- this.audioActive = audioActive.getAsBoolean();
- }
- if (videoActive != null && !videoActive.isJsonNull()) {
- this.videoActive = videoActive.getAsBoolean();
- }
- if (frameRate != null && !frameRate.isJsonNull()) {
- this.frameRate = frameRate.getAsInt();
- }
- if (typeOfVideo != null && !typeOfVideo.isJsonNull()) {
- this.typeOfVideo = typeOfVideo.getAsString();
- }
- if (videoDimensions != null && !videoDimensions.isJsonNull()) {
- this.videoDimensions = videoDimensions.getAsString();
- }
+ protected Publisher(JsonObject json) {
+ this.resetWithJson(json);
}
/**
- * Returns the unique identifier of the
- * Stream associated to this Publisher. Each Publisher is paired
- * with only one Stream, so you can identify each Publisher by its
- * Stream.streamId
+ * Returns the unique identifier of the Stream associated to this Publisher. Each Publisher is
+ * paired with only one Stream, so you can identify each Publisher by its
+ * Stream.streamId
*/
public String getStreamId() {
return streamId;
@@ -84,56 +65,70 @@ public class Publisher {
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public boolean hasVideo() {
return this.hasVideo;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public boolean hasAudio() {
return this.hasAudio;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public Boolean isAudioActive() {
return this.audioActive;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public Boolean isVideoActive() {
return this.videoActive;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public Integer getFrameRate() {
return this.frameRate;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public String getTypeOfVideo() {
return this.typeOfVideo;
}
/**
- * See properties of Stream object in OpenVidu Browser library to find out more
+ * See properties of Stream object in OpenVidu Browser library to find out
+ * more
*/
public String getVideoDimensions() {
return this.videoDimensions;
@@ -152,4 +147,32 @@ public class Publisher {
return json;
}
+ protected Publisher resetWithJson(JsonObject json) {
+ this.streamId = json.get("streamId").getAsString();
+ this.createdAt = json.get("createdAt").getAsLong();
+
+ if (json.has("mediaOptions") && !json.get("mediaOptions").isJsonNull()) {
+ JsonObject mediaOptions = json.get("mediaOptions").getAsJsonObject();
+ this.hasAudio = mediaOptions.get("hasAudio").getAsBoolean();
+ this.hasVideo = mediaOptions.get("hasVideo").getAsBoolean();
+ if (mediaOptions.has("audioActive") && !mediaOptions.get("audioActive").isJsonNull()) {
+ this.audioActive = mediaOptions.get("audioActive").getAsBoolean();
+ }
+ if (mediaOptions.has("videoActive") && !mediaOptions.get("videoActive").isJsonNull()) {
+ this.videoActive = mediaOptions.get("videoActive").getAsBoolean();
+ }
+ if (mediaOptions.has("frameRate") && !mediaOptions.get("frameRate").isJsonNull()) {
+ this.frameRate = mediaOptions.get("frameRate").getAsInt();
+ }
+ if (mediaOptions.has("typeOfVideo") && !mediaOptions.get("typeOfVideo").isJsonNull()) {
+ this.typeOfVideo = mediaOptions.get("typeOfVideo").getAsString();
+ }
+ if (mediaOptions.has("videoDimensions") && !mediaOptions.get("videoDimensions").isJsonNull()) {
+ this.videoDimensions = mediaOptions.get("videoDimensions").getAsString();
+ }
+ }
+
+ return this;
+ }
+
}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java
index 305201c6..b984ed8f 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java
@@ -48,9 +48,8 @@ public class Recording {
stopped,
/**
- * The recording has finished OK and is available for download through OpenVidu
- * Server recordings endpoint:
- * https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
+ * The recording has finished being processed and is available for download
+ * through property {@link Recording#getUrl}
*/
ready,
@@ -75,27 +74,25 @@ public class Recording {
* Record each stream individually
*/
INDIVIDUAL,
-
+
/**
- * Works the same way as COMPOSED mode, but the necessary recorder
- * service module will start some time in advance and won't be terminated
- * once a specific session recording has ended. This module will remain
- * up and running as long as the session remains active.
+ * Works the same way as COMPOSED mode, but the necessary recorder service
+ * module will start some time in advance and won't be terminated once a
+ * specific session recording has ended. This module will remain up and running
+ * as long as the session remains active.
+ *
*
*
- *
- * Pros vs COMPOSED : the process of starting the recording will be noticeably
- * faster. This can be very useful in use cases where a session needs to be
- * recorded multiple times over time, when a better response time is usually
- * desirable.
- *
- *
- * Cons vs COMPOSED : for every session initialized with COMPOSED_QUICK_START
- * recording output mode, extra CPU power will be required in OpenVidu Server.
- * The recording module will be continuously rendering all of the streams being
- * published to the session even when the session is not being recorded. And that
- * is for every session configured with COMPOSED_QUICK_START.
- *
+ * Pros vs COMPOSED : the process of starting the recording
+ * will be noticeably faster. This can be very useful in use cases where a
+ * session needs to be recorded multiple times over time, when a better response
+ * time is usually desirable.
+ * Cons vs COMPOSED : for every session initialized with
+ * COMPOSED_QUICK_START recording output mode, extra CPU power will be required
+ * in OpenVidu Server. The recording module will be continuously rendering all
+ * of the streams being published to the session even when the session is not
+ * being recorded. And that is for every session configured with
+ * COMPOSED_QUICK_START.
*
*/
COMPOSED_QUICK_START;
@@ -129,7 +126,8 @@ public class Recording {
OutputMode outputMode = OutputMode.valueOf(json.get("outputMode").getAsString());
RecordingProperties.Builder builder = new RecordingProperties.Builder().name(json.get("name").getAsString())
.outputMode(outputMode).hasAudio(hasAudio).hasVideo(hasVideo);
- if ((OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode)) && hasVideo) {
+ if ((OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode))
+ && hasVideo) {
builder.resolution(json.get("resolution").getAsString());
builder.recordingLayout(RecordingLayout.valueOf(json.get("recordingLayout").getAsString()));
JsonElement customLayout = json.get("customLayout");
@@ -219,8 +217,8 @@ public class Recording {
/**
* URL of the recording. You can access the file from there. It is
* null
until recording reaches "ready" or "failed" status. If
- * OpenVidu Server configuration property
+ * OpenVidu Server configuration property
* OPENVIDU_RECORDING_PUBLIC_ACCESS
is false, this path will be
* secured with OpenVidu credentials
*/
@@ -230,7 +228,8 @@ public class Recording {
/**
* Resolution of the video file. Only defined if OutputMode of the Recording is
- * set to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}
+ * set to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}
*/
public String getResolution() {
return this.recordingProperties.resolution();
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java
index 47f526ae..a88092b1 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java
@@ -17,8 +17,6 @@
package io.openvidu.java.client;
-import io.openvidu.java.client.Recording.OutputMode;
-
/**
* See
* {@link io.openvidu.java.client.OpenVidu#startRecording(String, RecordingProperties)}
@@ -33,6 +31,7 @@ public class RecordingProperties {
private boolean hasAudio;
private boolean hasVideo;
private long shmSize; // For COMPOSED recording
+ private String mediaNode;
/**
* Builder for {@link io.openvidu.java.client.RecordingProperties}
@@ -40,27 +39,21 @@ public class RecordingProperties {
public static class Builder {
private String name = "";
- private Recording.OutputMode outputMode = Recording.OutputMode.COMPOSED;
+ private Recording.OutputMode outputMode;
private RecordingLayout recordingLayout;
private String customLayout;
private String resolution;
private boolean hasAudio = true;
private boolean hasVideo = true;
private long shmSize = 536870912L;
+ private String mediaNode;
/**
* Builder for {@link io.openvidu.java.client.RecordingProperties}
*/
public RecordingProperties build() {
- if (OutputMode.COMPOSED.equals(this.outputMode) || OutputMode.COMPOSED_QUICK_START.equals(this.outputMode)) {
- this.recordingLayout = this.recordingLayout != null ? this.recordingLayout : RecordingLayout.BEST_FIT;
- this.resolution = this.resolution != null ? this.resolution : "1920x1080";
- if (RecordingLayout.CUSTOM.equals(this.recordingLayout)) {
- this.customLayout = this.customLayout != null ? this.customLayout : "";
- }
- }
return new RecordingProperties(this.name, this.outputMode, this.recordingLayout, this.customLayout,
- this.resolution, this.hasAudio, this.hasVideo, this.shmSize);
+ this.resolution, this.hasAudio, this.hasVideo, this.shmSize, this.mediaNode);
}
/**
@@ -102,11 +95,14 @@ public class RecordingProperties {
* method to set the relative path to the specific custom layout you want to
* use.
* Will only have effect if
- * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
- * has been called with value
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.
- * See
+ * See Custom recording layouts to learn more
*/
public RecordingProperties.Builder customLayout(String path) {
@@ -119,13 +115,15 @@ public class RecordingProperties {
* format "WIDTHxHEIGHT", being both WIDTH and HEIGHT the number of pixels
* between 100 and 1999.
* Will only have effect if
- * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
- * has been called with value
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}. For
- * {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
- * individual video files will have the native resolution of the published
- * stream
+ * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)
+ * Builder.outputMode()} has been called with value
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED
+ * OutputMode.COMPOSED} or
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START
+ * OutputMode.COMPOSED_QUICK_START}. For
+ * {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL
+ * OutputMode.INDIVIDUAL} all individual video files will have the native
+ * resolution of the published stream
*/
public RecordingProperties.Builder resolution(String resolution) {
this.resolution = resolution;
@@ -134,7 +132,8 @@ public class RecordingProperties {
/**
* Call this method to specify whether to record audio or not. Cannot be set to
- * false at the same time as {@link hasVideo(boolean)}
+ * false at the same time as
+ * {@link RecordingProperties.Builder#hasVideo(boolean)}
*/
public RecordingProperties.Builder hasAudio(boolean hasAudio) {
this.hasAudio = hasAudio;
@@ -143,7 +142,8 @@ public class RecordingProperties {
/**
* Call this method to specify whether to record video or not. Cannot be set to
- * false at the same time as {@link hasAudio(boolean)}
+ * false at the same time as
+ * {@link RecordingProperties.Builder#hasAudio(boolean)}
*/
public RecordingProperties.Builder hasVideo(boolean hasVideo) {
this.hasVideo = hasVideo;
@@ -160,10 +160,26 @@ public class RecordingProperties {
return this;
}
+ /**
+ * PRO Call this method to force the recording to be hosted in
+ * the Media Node with identifier mediaNodeId
. This property only
+ * applies to COMPOSED recordings and is ignored for INDIVIDUAL recordings, that
+ * are always hosted in the same Media Node hosting its Session
+ */
+ public RecordingProperties.Builder mediaNode(String mediaNodeId) {
+ this.mediaNode = mediaNodeId;
+ return this;
+ }
+
}
protected RecordingProperties(String name, Recording.OutputMode outputMode, RecordingLayout layout,
- String customLayout, String resolution, boolean hasAudio, boolean hasVideo, long shmSize) {
+ String customLayout, String resolution, boolean hasAudio, boolean hasVideo, long shmSize,
+ String mediaNode) {
this.name = name;
this.outputMode = outputMode;
this.recordingLayout = layout;
@@ -172,6 +188,7 @@ public class RecordingProperties {
this.hasAudio = hasAudio;
this.hasVideo = hasVideo;
this.shmSize = shmSize;
+ this.mediaNode = mediaNode;
}
/**
@@ -185,12 +202,12 @@ public class RecordingProperties {
/**
* Defines the mode of recording: {@link Recording.OutputMode#COMPOSED} or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START} for a
- * single archive in a grid layout or {@link Recording.OutputMode#INDIVIDUAL}
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START} for
+ * a single archive in a grid layout or {@link Recording.OutputMode#INDIVIDUAL}
* for one archive for each stream.
*
*
- * Default to {@link Recording.OutputMode#COMPOSED}
+ * Default to {@link Recording.OutputMode#COMPOSED OutputMode.COMPOSED}
*/
public Recording.OutputMode outputMode() {
return this.outputMode;
@@ -199,12 +216,14 @@ public class RecordingProperties {
/**
* Defines the layout to be used in the recording.
* Will only have effect if
- * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
- * has been called with value {@link Recording.OutputMode#COMPOSED} or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.
+ * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)
+ * Builder.outputMode()} has been called with value
+ * {@link Recording.OutputMode#COMPOSED OutputMode.COMPOSED} or
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START
+ * OutputMode.COMPOSED_QUICK_START}.
*
*
- * Default to {@link RecordingLayout#BEST_FIT}
+ * Default to {@link RecordingLayout#BEST_FIT RecordingLayout.BEST_FIT}
*/
public RecordingLayout recordingLayout() {
return this.recordingLayout;
@@ -214,7 +233,8 @@ public class RecordingProperties {
* If {@link io.openvidu.java.client.RecordingProperties#recordingLayout()} is
* set to {@link io.openvidu.java.client.RecordingLayout#CUSTOM}, this property
* defines the relative path to the specific custom layout you want to use.
- * See Custom recording layouts to learn more
*/
public String customLayout() {
@@ -227,8 +247,8 @@ public class RecordingProperties {
* {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)}
* has been called with value
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}. For
- * {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}.
+ * For {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all
* individual video files will have the native resolution of the published
* stream.
*
@@ -241,7 +261,7 @@ public class RecordingProperties {
/**
* Defines whether to record audio or not. Cannot be set to false at the same
- * time as {@link hasVideo()}.
+ * time as {@link RecordingProperties#hasVideo()}.
*
*
* Default to true
@@ -252,7 +272,7 @@ public class RecordingProperties {
/**
* Defines whether to record video or not. Cannot be set to false at the same
- * time as {@link hasAudio()}.
+ * time as {@link RecordingProperties#hasAudio()}.
*
*
* Default to true
@@ -273,4 +293,18 @@ public class RecordingProperties {
return this.shmSize;
}
+ /**
+ * PRO The Media Node where to host the recording. The default
+ * option if this property is not defined is the same Media Node hosting the
+ * Session to record. This property only applies to COMPOSED recordings and is
+ * ignored for INDIVIDUAL recordings
+ */
+ public String mediaNode() {
+ return this.mediaNode;
+ }
+
}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java
index a7fc8d56..1fbaeab4 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java
@@ -19,9 +19,10 @@ package io.openvidu.java.client;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -34,6 +35,7 @@ import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
@@ -48,7 +50,7 @@ public class Session {
private long createdAt;
private OpenVidu openVidu;
private SessionProperties properties;
- private Map activeConnections = new ConcurrentHashMap<>();
+ private Map connections = new ConcurrentHashMap<>();
private boolean recording = false;
protected Session(OpenVidu openVidu) throws OpenViduJavaClientException, OpenViduHttpException {
@@ -66,11 +68,11 @@ public class Session {
protected Session(OpenVidu openVidu, JsonObject json) {
this.openVidu = openVidu;
- this.resetSessionWithJson(json);
+ this.resetWithJson(json);
}
/**
- * Gets the unique identifier of the Session
+ * Gets the unique identifier of the Session.
*
* @return The sessionId
*/
@@ -80,78 +82,49 @@ public class Session {
/**
* Timestamp when this session was created, in UTC milliseconds (ms since Jan 1,
- * 1970, 00:00:00 UTC)
+ * 1970, 00:00:00 UTC).
*/
public long createdAt() {
return this.createdAt;
}
/**
- * Gets a new token associated to Session object with default values for
- * {@link io.openvidu.java.client.TokenOptions}. This always translates into a
- * new request to OpenVidu Server
+ * @deprecated Use {@link Session#createConnection() Session.createConnection()}
+ * instead to get a {@link io.openvidu.java.client.Connection}
+ * object.
+ *
+ * @return The generated token String
*
- * @return The generated token
- *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
+ @Deprecated
public String generateToken() throws OpenViduJavaClientException, OpenViduHttpException {
- return this.generateToken(new TokenOptions.Builder().role(OpenViduRole.PUBLISHER).build());
+ return generateToken(new TokenOptions.Builder().data("").role(OpenViduRole.PUBLISHER).build());
}
/**
- * Gets a new token associated to Session object configured with
- * tokenOptions
. This always translates into a new request to
- * OpenVidu Server
+ * @deprecated Use
+ * {@link Session#createConnection(io.openvidu.java.client.ConnectionProperties)
+ * Session.createConnection(ConnectionProperties)} instead to get a
+ * {@link io.openvidu.java.client.Connection} object.
+ *
+ * @return The generated token String
*
- * @return The generated token
- *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
+ @Deprecated
public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException {
-
if (!this.hasSessionId()) {
this.getSessionId();
}
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_TOKENS);
- JsonObject json = new JsonObject();
- json.addProperty("session", this.sessionId);
- json.addProperty("role", tokenOptions.getRole().name());
- json.addProperty("data", tokenOptions.getData());
- if (tokenOptions.getKurentoOptions() != null) {
- JsonObject kurentoOptions = new JsonObject();
- if (tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth() != null) {
- kurentoOptions.addProperty("videoMaxRecvBandwidth",
- tokenOptions.getKurentoOptions().getVideoMaxRecvBandwidth());
- }
- if (tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth() != null) {
- kurentoOptions.addProperty("videoMinRecvBandwidth",
- tokenOptions.getKurentoOptions().getVideoMinRecvBandwidth());
- }
- if (tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth() != null) {
- kurentoOptions.addProperty("videoMaxSendBandwidth",
- tokenOptions.getKurentoOptions().getVideoMaxSendBandwidth());
- }
- if (tokenOptions.getKurentoOptions().getVideoMinSendBandwidth() != null) {
- kurentoOptions.addProperty("videoMinSendBandwidth",
- tokenOptions.getKurentoOptions().getVideoMinSendBandwidth());
- }
- if (tokenOptions.getKurentoOptions().getAllowedFilters().length > 0) {
- JsonArray allowedFilters = new JsonArray();
- for (String filter : tokenOptions.getKurentoOptions().getAllowedFilters()) {
- allowedFilters.add(filter);
- }
- kurentoOptions.add("allowedFilters", allowedFilters);
- }
- json.add("kurentoOptions", kurentoOptions);
- }
StringEntity params;
try {
- params = new StringEntity(json.toString());
+ params = new StringEntity(tokenOptions.toJsonObject(sessionId).toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
@@ -180,10 +153,79 @@ public class Session {
}
}
+ /**
+ * Same as
+ * {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
+ * but with default ConnectionProperties values.
+ *
+ * @return The generated {@link io.openvidu.java.client.Connection Connection}
+ * object.
+ *
+ * @throws OpenViduJavaClientException
+ * @throws OpenViduHttpException
+ */
+ public Connection createConnection() throws OpenViduJavaClientException, OpenViduHttpException {
+ return createConnection(
+ new ConnectionProperties.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build());
+ }
+
+ /**
+ * Creates a new Connection object associated to Session object and configured
+ * with connectionProperties
. Each user connecting to the Session
+ * requires a Connection. The token string value to send to the client side can
+ * be retrieved with {@link io.openvidu.java.client.Connection#getToken()
+ * Connection.getToken()}.
+ *
+ * @return The generated {@link io.openvidu.java.client.Connection Connection}
+ * object.
+ *
+ * @throws OpenViduJavaClientException
+ * @throws OpenViduHttpException
+ */
+ public Connection createConnection(ConnectionProperties connectionProperties)
+ throws OpenViduJavaClientException, OpenViduHttpException {
+ if (!this.hasSessionId()) {
+ this.getSessionId();
+ }
+
+ HttpPost request = new HttpPost(
+ this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection");
+
+ StringEntity params;
+ try {
+ params = new StringEntity(connectionProperties.toJson(sessionId).toString());
+ } catch (UnsupportedEncodingException e1) {
+ throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
+ }
+
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setEntity(params);
+
+ HttpResponse response;
+ try {
+ response = this.openVidu.httpClient.execute(request);
+ } catch (IOException e2) {
+ throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause());
+ }
+
+ try {
+ int statusCode = response.getStatusLine().getStatusCode();
+ if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
+ Connection connection = new Connection(httpResponseToJson(response));
+ this.connections.put(connection.getConnectionId(), connection);
+ return connection;
+ } else {
+ throw new OpenViduHttpException(statusCode);
+ }
+ } finally {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
+ }
+
/**
* Gracefully closes the Session: unpublishes all streams and evicts every
- * participant
- *
+ * participant.
+ *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@@ -217,22 +259,25 @@ public class Session {
* connections to the Session
* ({@link io.openvidu.java.client.Session#getActiveConnections()}) and use
* those values to call
- * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)} or
- * {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)}.
- *
- * To update every Session object owned by OpenVidu object, call
- * {@link io.openvidu.java.client.OpenVidu#fetch()}
- *
+ * {@link io.openvidu.java.client.Session#forceDisconnect(Connection)},
+ * {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)} or
+ * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}.
+ *
+ *
+ * To update all Session objects owned by OpenVidu object at once, call
+ * {@link io.openvidu.java.client.OpenVidu#fetch()}.
+ *
* @return true if the Session status has changed with respect to the server,
* false if not. This applies to any property or sub-property of the
- * object
- *
+ * object.
+ *
* @throws OpenViduHttpException
* @throws OpenViduJavaClientException
*/
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
- String beforeJSON = this.toJson();
- HttpGet request = new HttpGet(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId);
+ final String beforeJSON = this.toJson();
+ HttpGet request = new HttpGet(
+ this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "?pendingConnections=true");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response;
@@ -245,8 +290,8 @@ public class Session {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
- this.resetSessionWithJson(httpResponseToJson(response));
- String afterJSON = this.toJson();
+ this.resetWithJson(httpResponseToJson(response));
+ final String afterJSON = this.toJson();
boolean hasChanged = !beforeJSON.equals(afterJSON);
log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged);
return hasChanged;
@@ -259,16 +304,31 @@ public class Session {
}
/**
- * Forces the user represented by connection
to leave the session.
- * OpenVidu Browser will trigger the proper events on the client-side
- * (streamDestroyed
, connectionDestroyed
,
+ * Removes the Connection from the Session. This can translate into a forced
+ * eviction of a user from the Session if the Connection had status
+ * active
, or into a token invalidation if no user had taken the
+ * Connection yet (status pending
).
+ *
+ *
+ * In the first case, OpenVidu Browser will trigger the proper events on the
+ * client-side (streamDestroyed
, connectionDestroyed
,
* sessionDisconnected
) with reason set to
- * "forceDisconnectByServer"
- *
- * You can get connection
parameter with
- * {@link io.openvidu.java.client.Session#fetch()} and then
- * {@link io.openvidu.java.client.Session#getActiveConnections()}
- *
+ * "forceDisconnectByServer"
.
+ *
+ *
+ * In the second case, the token of the Connection will be invalidated and no
+ * user will be able to connect to the session with it.
+ *
+ *
+ * This method automatically updates the properties of the local affected
+ * objects. This means that there is no need to call
+ * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
+ * changes consequence of the execution of this method applied in the local
+ * objects.
+ *
+ * @param connection The Connection to remove
+ *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@@ -277,17 +337,13 @@ public class Session {
}
/**
- * Forces the user with Connection connectionId
to leave the
- * session. OpenVidu Browser will trigger the proper events on the client-side
- * (streamDestroyed
, connectionDestroyed
,
- * sessionDisconnected
) with reason set to
- * "forceDisconnectByServer"
- *
- * You can get connectionId
parameter with
- * {@link io.openvidu.java.client.Session#fetch()} (use
- * {@link io.openvidu.java.client.Connection#getConnectionId()} to get the
- * `connectionId` you want)
- *
+ * Same as {@link io.openvidu.java.client.Session#forceDisconnect(Connection)
+ * forceDisconnect(ConnectionProperties)} but providing the
+ * {@link io.openvidu.java.client.Connection#getConnectionId() connectionId}
+ * instead of the Connection object.
+ *
+ * @param connectionId The identifier of the Connection object to remove
+ *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@@ -307,13 +363,13 @@ public class Session {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
// Remove connection from activeConnections map
- Connection connectionClosed = this.activeConnections.remove(connectionId);
+ Connection connectionClosed = this.connections.remove(connectionId);
// Remove every Publisher of the closed connection from every subscriber list of
// other connections
if (connectionClosed != null) {
for (Publisher publisher : connectionClosed.getPublishers()) {
String streamId = publisher.getStreamId();
- for (Connection connection : this.activeConnections.values()) {
+ for (Connection connection : this.connections.values()) {
connection.setSubscribers(connection.getSubscribers().stream()
.filter(subscriber -> !streamId.equals(subscriber)).collect(Collectors.toList()));
}
@@ -334,17 +390,28 @@ public class Session {
}
/**
- * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
- * proper events on the client-side (streamDestroyed
) with reason
- * set to "forceUnpublishByServer".
- *
+ * Forces some Connection to unpublish a Stream. OpenVidu Browser will trigger
+ * the proper events in the client-side (streamDestroyed
) with
+ * reason set to "forceUnpublishByServer"
.
+ *
+ *
* You can get publisher
parameter with
* {@link io.openvidu.java.client.Session#getActiveConnections()} and then for
* each Connection you can call
* {@link io.openvidu.java.client.Connection#getPublishers()}. Remember to call
* {@link io.openvidu.java.client.Session#fetch()} before to fetch the current
- * actual properties of the Session from OpenVidu Server
- *
+ * actual properties of the Session from OpenVidu Server.
+ *
+ *
+ * This method automatically updates the properties of the local affected
+ * objects. This means that there is no need to call
+ * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
+ * changes consequence of the execution of this method applied in the local
+ * objects.
+ *
+ * @param publisher The Publisher object to unpublish
+ *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@@ -353,19 +420,13 @@ public class Session {
}
/**
- * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
- * proper events on the client-side (streamDestroyed
) with reason
- * set to "forceUnpublishByServer".
- *
- * You can get streamId
parameter with
- * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for
- * each Connection you can call
- * {@link io.openvidu.java.client.Connection#getPublishers()}. Finally
- * {@link io.openvidu.java.client.Publisher#getStreamId()}) will give you the
- * streamId
. Remember to call
- * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current
- * actual properties of the Session from OpenVidu Server
- *
+ * Same as {@link io.openvidu.java.client.Session#forceUnpublish(Publisher)
+ * forceUnpublish(Publisher)} but providing the
+ * {@link io.openvidu.java.client.Publisher#getStreamId() streamId} instead of
+ * the Publisher object.
+ *
+ * @param streamId The identifier of the Publisher object to remove
+ *
* @throws OpenViduJavaClientException
* @throws OpenViduHttpException
*/
@@ -384,7 +445,7 @@ public class Session {
try {
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
- for (Connection connection : this.activeConnections.values()) {
+ for (Connection connection : this.connections.values()) {
// Try to remove the Publisher from the Connection publishers collection
if (connection.publishers.remove(streamId) != null) {
continue;
@@ -402,44 +463,194 @@ public class Session {
}
/**
- * Returns the list of active connections to the session. This value
- * will remain unchanged since the last time method
- * {@link io.openvidu.java.client.Session#fetch()} was called .
- * Exceptions to this rule are:
+ * PRO Updates the properties of a Connection with a
+ * {@link io.openvidu.java.client.ConnectionProperties} object. Only these
+ * properties can be updated:
*
- * Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
- * updates each affected Connection status
- * Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
- * updates each affected Connection status
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#role(OpenViduRole)
+ * ConnectionProperties.Builder.role(OpenViduRole)}
+ * {@link io.openvidu.java.client.ConnectionProperties.Builder#record(boolean)
+ * ConnectionProperties.Builder.record(boolean)}
*
*
- * To get the list of active connections with their current actual value, you
- * must call first {@link io.openvidu.java.client.Session#fetch()} and then
- * {@link io.openvidu.java.client.Session#getActiveConnections()}
+ *
+ * This method automatically updates the properties of the local affected
+ * objects. This means that there is no need to call
+ * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the
+ * changes consequence of the execution of this method applied in the local
+ * objects.
+ *
+ *
+ * The affected client will trigger one ConnectionPropertyChangedEvent for each modified
+ * property.
+ *
+ * @param connectionId The Connection to modify
+ * @param connectionProperties A ConnectionProperties object with the new values
+ * to apply
+ *
+ * @return The updated {@link io.openvidu.java.client.Connection Connection}
+ * object
+ *
+ * @throws OpenViduJavaClientException
+ * @throws OpenViduHttpException
*/
- public List getActiveConnections() {
- return new ArrayList<>(this.activeConnections.values());
+ public Connection updateConnection(String connectionId, ConnectionProperties connectionProperties)
+ throws OpenViduJavaClientException, OpenViduHttpException {
+
+ HttpPatch request = new HttpPatch(
+ this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId);
+
+ StringEntity params;
+ try {
+ params = new StringEntity(connectionProperties.toJson(this.sessionId).toString());
+ } catch (UnsupportedEncodingException e1) {
+ throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
+ }
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setEntity(params);
+
+ HttpResponse response;
+ try {
+ response = this.openVidu.httpClient.execute(request);
+ } catch (IOException e) {
+ throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
+ }
+
+ try {
+ int statusCode = response.getStatusLine().getStatusCode();
+ if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
+ log.info("Connection {} updated", connectionId);
+ } else if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
+ log.info("Properties of Connection {} remain the same", connectionId);
+ } else {
+ throw new OpenViduHttpException(statusCode);
+ }
+ JsonObject json = httpResponseToJson(response);
+
+ // Update the actual Connection object with the new options
+ Connection existingConnection = this.connections.get(connectionId);
+
+ if (existingConnection == null) {
+ // The updated Connection is not available in local map
+ Connection newConnection = new Connection(json);
+ this.connections.put(connectionId, newConnection);
+ return newConnection;
+ } else {
+ // The updated Connection was available in local map
+ existingConnection.overrideConnectionProperties(connectionProperties);
+ return existingConnection;
+ }
+
+ } finally {
+ EntityUtils.consumeQuietly(response.getEntity());
+ }
}
/**
- * Returns whether the session is being recorded or not
+ * Returns a Connection of the Session. This method only returns the local
+ * available object and does not query OpenVidu Server. To get the current
+ * actual value you must call first
+ * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
+ *
+ * @param id The Connection to get
+ *
+ * @return The {@link io.openvidu.java.client.Connection Connection} object, or
+ * null
if no Connection is found for param id
+ */
+ public Connection getConnection(String id) {
+ return this.connections.get(id);
+ }
+
+ /**
+ * Returns all the Connections of the Session. This method only returns the
+ * local available objects and does not query OpenVidu Server. To get the
+ * current actual value you must call first
+ * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
+ *
+ * The list of Connections will remain unchanged since the last time
+ * method {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was
+ * called . Exceptions to this rule are:
+ *
+ * Calling
+ * {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)
+ * createConnection(ConnectionProperties)} automatically adds the new Connection
+ * object to the local collection.
+ * Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
+ * automatically updates each affected local Connection object.
+ * Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
+ * automatically updates each affected local Connection object.
+ * Calling
+ * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
+ * automatically updates the attributes of the affected local Connection
+ * object.
+ *
+ *
+ * To get the list of connections with their current actual value, you must call
+ * first {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or
+ * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}.
+ */
+ public List getConnections() {
+ return this.connections.values().stream().collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the list of active Connections of the Session. These are the
+ * Connections returning active
in response to
+ * {@link io.openvidu.java.client.Connection#getStatus()}. This method only
+ * returns the local available objects and does not query OpenVidu Server.
+ * The list of active Connections will remain unchanged since the last
+ * time method {@link io.openvidu.java.client.Session#fetch() Session.fetch()}
+ * or {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was
+ * called . Exceptions to this rule are:
+ *
+ * Calling
+ * {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)
+ * createConnection(ConnectionProperties)} automatically adds the new Connection
+ * object to the local collection.
+ * Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)}
+ * automatically updates each affected local Connection object.
+ * Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)}
+ * automatically updates each affected local Connection object.
+ * Calling
+ * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionProperties)}
+ * automatically updates the attributes of the affected local Connection
+ * object.
+ *
+ *
+ * To get the list of active connections with their current actual value, you
+ * must call first {@link io.openvidu.java.client.Session#fetch()
+ * Session.fetch()} or {@link io.openvidu.java.client.OpenVidu#fetch()
+ * OpenVidu.fetch()} OpenVidu.fetch()}.
+ */
+ public List getActiveConnections() {
+ return this.connections.values().stream().filter(con -> "active".equals(con.getStatus()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns whether the session is being recorded or not.
*/
public boolean isBeingRecorded() {
return this.recording;
}
/**
- * Returns the properties defining the session
+ * Returns the properties defining the session.
*/
public SessionProperties getProperties() {
return this.properties;
}
- @Override
- public String toString() {
- return this.sessionId;
- }
-
private boolean hasSessionId() {
return (this.sessionId != null && !this.sessionId.isEmpty());
}
@@ -450,27 +661,9 @@ public class Session {
}
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_SESSIONS);
-
- JsonObject json = new JsonObject();
- json.addProperty("mediaMode", properties.mediaMode().name());
- json.addProperty("recordingMode", properties.recordingMode().name());
- json.addProperty("defaultOutputMode", properties.defaultOutputMode().name());
- json.addProperty("defaultRecordingLayout", properties.defaultRecordingLayout().name());
- json.addProperty("defaultCustomLayout", properties.defaultCustomLayout());
- json.addProperty("customSessionId", properties.customSessionId());
-
- // forcedVideoCodec codec and allowTranscoding could be null because
- // both default values are loaded by openvidu server
- if (properties.forcedVideoCodec() != null) {
- json.addProperty("forcedVideoCodec", properties.forcedVideoCodec().name());
- }
- if (properties.isTranscodingAllowed() != null) {
- json.addProperty("allowTranscoding", properties.isTranscodingAllowed());
- }
-
StringEntity params = null;
try {
- params = new StringEntity(json.toString());
+ params = new StringEntity(properties.toJson().toString());
} catch (UnsupportedEncodingException e1) {
throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause());
}
@@ -490,19 +683,24 @@ public class Session {
JsonObject responseJson = httpResponseToJson(response);
this.sessionId = responseJson.get("id").getAsString();
this.createdAt = responseJson.get("createdAt").getAsLong();
+
+ // forcedVideoCodec and allowTranscoding values are configured in OpenVidu Server
+ // via configuration or session
VideoCodec forcedVideoCodec = VideoCodec.valueOf(responseJson.get("forcedVideoCodec").getAsString());
Boolean allowTranscoding = responseJson.get("allowTranscoding").getAsBoolean();
-
+
SessionProperties responseProperties = new SessionProperties.Builder()
- .mediaMode(properties.mediaMode())
- .recordingMode(properties.recordingMode())
- .defaultOutputMode(properties.defaultOutputMode())
- .defaultRecordingLayout(properties.defaultRecordingLayout())
- .defaultCustomLayout(properties.defaultCustomLayout())
- .forcedVideoCodec(forcedVideoCodec)
- .allowTranscoding(allowTranscoding)
- .build();
-
+ .customSessionId(properties.customSessionId())
+ .mediaMode(properties.mediaMode())
+ .recordingMode(properties.recordingMode())
+ .defaultOutputMode(properties.defaultOutputMode())
+ .defaultRecordingLayout(properties.defaultRecordingLayout())
+ .defaultCustomLayout(properties.defaultCustomLayout())
+ .mediaNode(properties.mediaNode())
+ .forcedVideoCodec(forcedVideoCodec)
+ .allowTranscoding(allowTranscoding)
+ .build();
+
this.properties = responseProperties;
log.info("Session '{}' created", this.sessionId);
} else if (statusCode == org.apache.http.HttpStatus.SC_CONFLICT) {
@@ -530,7 +728,7 @@ public class Session {
this.recording = recording;
}
- protected Session resetSessionWithJson(JsonObject json) {
+ protected Session resetWithJson(JsonObject json) {
this.sessionId = json.get("sessionId").getAsString();
this.createdAt = json.get("createdAt").getAsLong();
this.recording = json.get("recording").getAsBoolean();
@@ -544,50 +742,44 @@ public class Session {
if (json.has("defaultCustomLayout")) {
builder.defaultCustomLayout(json.get("defaultCustomLayout").getAsString());
}
+ if (json.has("customSessionId")) {
+ builder.customSessionId(json.get("customSessionId").getAsString());
+ }
+
if (json.has("forcedVideoCodec")) {
builder.forcedVideoCodec(VideoCodec.valueOf(json.get("forcedVideoCodec").getAsString()));
}
if (json.has("allowTranscoding")) {
builder.allowTranscoding(json.get("allowTranscoding").getAsBoolean());
}
- if (this.properties != null && this.properties.customSessionId() != null) {
- builder.customSessionId(this.properties.customSessionId());
- } else if (json.has("customSessionId")) {
- builder.customSessionId(json.get("customSessionId").getAsString());
- }
+
this.properties = builder.build();
JsonArray jsonArrayConnections = (json.get("connections").getAsJsonObject()).get("content").getAsJsonArray();
- this.activeConnections.clear();
- jsonArrayConnections.forEach(connection -> {
- JsonObject con = connection.getAsJsonObject();
- Map publishers = new ConcurrentHashMap<>();
- JsonArray jsonArrayPublishers = con.get("publishers").getAsJsonArray();
- jsonArrayPublishers.forEach(publisher -> {
- JsonObject pubJson = publisher.getAsJsonObject();
- JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject();
- Publisher pub = new Publisher(pubJson.get("streamId").getAsString(),
- pubJson.get("createdAt").getAsLong(), mediaOptions.get("hasAudio").getAsBoolean(),
- mediaOptions.get("hasVideo").getAsBoolean(), mediaOptions.get("audioActive"),
- mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), mediaOptions.get("typeOfVideo"),
- mediaOptions.get("videoDimensions"));
- publishers.put(pub.getStreamId(), pub);
+ // 1. Set to store fetched connections and later remove closed ones
+ Set fetchedConnectionIds = new HashSet<>();
+ jsonArrayConnections.forEach(connectionJsonElement -> {
+
+ JsonObject connectionJson = connectionJsonElement.getAsJsonObject();
+ Connection connectionObj = new Connection(connectionJson);
+ String id = connectionObj.getConnectionId();
+ fetchedConnectionIds.add(id);
+
+ // 2. Update existing Connection
+ this.connections.computeIfPresent(id, (cId, c) -> {
+ c = c.resetWithJson(connectionJson);
+ return c;
});
- List subscribers = new ArrayList<>();
- JsonArray jsonArraySubscribers = con.get("subscribers").getAsJsonArray();
- jsonArraySubscribers.forEach(subscriber -> {
- subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString());
+ // 3. Add new Connection
+ this.connections.computeIfAbsent(id, cId -> {
+ return connectionObj;
});
-
- Connection c = new Connection(con.get("connectionId").getAsString(), con.get("createdAt").getAsLong(),
- OpenViduRole.valueOf(con.get("role").getAsString()),
- (con.has("token") ? con.get("token").getAsString() : null), con.get("location").getAsString(),
- con.get("platform").getAsString(), con.get("serverData").getAsString(),
- con.get("clientData").getAsString(), publishers, subscribers);
-
- this.activeConnections.put(con.get("connectionId").getAsString(), c);
});
+
+ // 4. Remove closed connections from local collection
+ this.connections.entrySet()
+ .removeIf(entry -> !fetchedConnectionIds.contains(entry.getValue().getConnectionId()));
return this;
}
@@ -609,26 +801,10 @@ public class Session {
json.addProperty("allowTranscoding", this.properties.isTranscodingAllowed());
}
JsonObject connections = new JsonObject();
- connections.addProperty("numberOfElements", this.getActiveConnections().size());
+ connections.addProperty("numberOfElements", this.getConnections().size());
JsonArray jsonArrayConnections = new JsonArray();
- this.getActiveConnections().forEach(con -> {
- JsonObject c = new JsonObject();
- c.addProperty("connectionId", con.getConnectionId());
- c.addProperty("role", con.getRole().name());
- c.addProperty("token", con.getToken());
- c.addProperty("clientData", con.getClientData());
- c.addProperty("serverData", con.getServerData());
- JsonArray pubs = new JsonArray();
- con.getPublishers().forEach(p -> {
- pubs.add(p.toJson());
- });
- JsonArray subs = new JsonArray();
- con.getSubscribers().forEach(s -> {
- subs.add(s);
- });
- c.add("publishers", pubs);
- c.add("subscribers", subs);
- jsonArrayConnections.add(c);
+ this.getConnections().forEach(con -> {
+ jsonArrayConnections.add(con.toJson());
});
connections.add("content", jsonArrayConnections);
json.add("connections", connections);
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java
index ef50557f..dc1b263b 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java
@@ -17,6 +17,8 @@
package io.openvidu.java.client;
+import com.google.gson.JsonObject;
+
import io.openvidu.java.client.Recording.OutputMode;
/**
@@ -30,6 +32,7 @@ public class SessionProperties {
private RecordingLayout defaultRecordingLayout;
private String defaultCustomLayout;
private String customSessionId;
+ private String mediaNode;
private VideoCodec forcedVideoCodec;
private Boolean allowTranscoding;
@@ -44,6 +47,7 @@ public class SessionProperties {
private RecordingLayout defaultRecordingLayout = RecordingLayout.BEST_FIT;
private String defaultCustomLayout = "";
private String customSessionId = "";
+ private String mediaNode;
private VideoCodec forcedVideoCodec;
private Boolean allowTranscoding;
@@ -53,7 +57,7 @@ public class SessionProperties {
*/
public SessionProperties build() {
return new SessionProperties(this.mediaMode, this.recordingMode, this.defaultOutputMode,
- this.defaultRecordingLayout, this.defaultCustomLayout, this.customSessionId,
+ this.defaultRecordingLayout, this.defaultCustomLayout, this.customSessionId, this.mediaNode,
this.forcedVideoCodec, this.allowTranscoding);
}
@@ -62,7 +66,7 @@ public class SessionProperties {
* your clients: routed through OpenVidu Media Node
* (MediaMode.ROUTED
) or attempting direct p2p connections
* (MediaMode.RELAYED
, not available yet )
- *
+ *
* Default value is MediaMode.ROUTED
*/
public SessionProperties.Builder mediaMode(MediaMode mediaMode) {
@@ -120,11 +124,11 @@ public class SessionProperties {
* {@link io.openvidu.java.client.RecordingProperties.Builder#customLayout(String)}
* with any other value.
*
- *
+ *
* Custom layouts are only applicable to recordings with OutputMode
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}) and
- * RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START})
+ * and RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
*/
public SessionProperties.Builder defaultCustomLayout(String path) {
this.defaultCustomLayout = path;
@@ -142,20 +146,55 @@ public class SessionProperties {
this.customSessionId = customSessionId;
return this;
}
-
+
/**
* Call this method to define which video codec do you want to be forcibly used for this session.
* This allows browsers/clients to use the same codec avoiding transcoding in the media server.
- * If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
+ * If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
* is false
and exception will occur.
- *
- * If forcedVideoCodec is set to NONE, no codec will be forced.
+ *
+ * If forcedVideoCodec is set to NONE, no codec will be forced.
*/
public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) {
this.forcedVideoCodec = forcedVideoCodec;
return this;
}
-
+
+ /**
+ * Call this method to define if you want to allow transcoding in the media server or not
+ * when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client.
+ */
+ public SessionProperties.Builder allowTranscoding(Boolean allowTranscoding) {
+ this.allowTranscoding = allowTranscoding;
+ return this;
+ }
+
+ /**
+ * PRO Call this method to force the session to be hosted in the
+ * Media Node with identifier mediaNodeId
+ */
+ public SessionProperties.Builder mediaNode(String mediaNodeId) {
+ this.mediaNode = mediaNodeId;
+ return this;
+ }
+
+ /**
+ * Call this method to define which video codec do you want to be forcibly used for this session.
+ * This allows browsers/clients to use the same codec avoiding transcoding in the media server.
+ * If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(Boolean)}
+ * is false
and exception will occur.
+ *
+ * If forcedVideoCodec is set to NONE, no codec will be forced.
+ */
+ public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) {
+ this.forcedVideoCodec = forcedVideoCodec;
+ return this;
+ }
+
/**
* Call this method to define if you want to allow transcoding in the media server or not
* when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client.
@@ -174,11 +213,11 @@ public class SessionProperties {
this.defaultRecordingLayout = RecordingLayout.BEST_FIT;
this.defaultCustomLayout = "";
this.customSessionId = "";
- this.allowTranscoding = false;
+ this.mediaNode = "";
}
private SessionProperties(MediaMode mediaMode, RecordingMode recordingMode, OutputMode outputMode,
- RecordingLayout layout, String defaultCustomLayout, String customSessionId,
+ RecordingLayout layout, String defaultCustomLayout, String customSessionId, String mediaNode,
VideoCodec forcedVideoCodec, Boolean allowTranscoding) {
this.mediaMode = mediaMode;
this.recordingMode = recordingMode;
@@ -186,6 +225,7 @@ public class SessionProperties {
this.defaultRecordingLayout = layout;
this.defaultCustomLayout = defaultCustomLayout;
this.customSessionId = customSessionId;
+ this.mediaNode = mediaNode;
this.forcedVideoCodec = forcedVideoCodec;
this.allowTranscoding = allowTranscoding;
}
@@ -244,8 +284,8 @@ public class SessionProperties {
* with any other value.
* Custom layouts are only applicable to recordings with OutputMode
* {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} (or
- * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START}) and
- * RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
+ * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START})
+ * and RecordingLayout {@link io.openvidu.java.client.RecordingLayout#CUSTOM}
*/
public String defaultCustomLayout() {
return this.defaultCustomLayout;
@@ -261,14 +301,14 @@ public class SessionProperties {
public String customSessionId() {
return this.customSessionId;
}
-
+
/**
* Defines which video codec is being forced to be used in the browser/client
*/
public VideoCodec forcedVideoCodec() {
return this.forcedVideoCodec;
}
-
+
/**
* Defines if transcoding is allowed or not when {@link #forcedVideoCodec}
* is not a compatible codec with the browser/client.
@@ -277,4 +317,54 @@ public class SessionProperties {
return this.allowTranscoding;
}
-}
\ No newline at end of file
+ /**
+ * PRO The Media Node where to host the session. The default
+ * option if this property is not defined is the less loaded Media Node at the
+ * moment the first user joins the session.
+ */
+ public String mediaNode() {
+ return this.mediaNode;
+ }
+
+ /**
+ * Defines which video codec is being forced to be used in the browser/client
+ */
+ public VideoCodec forcedVideoCodec() {
+ return this.forcedVideoCodec;
+ }
+
+ /**
+ * Defines if transcoding is allowed or not when {@link #forcedVideoCodec}
+ * is not a compatible codec with the browser/client.
+ */
+ public Boolean isTranscodingAllowed() {
+ return this.allowTranscoding;
+ }
+
+ protected JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ json.addProperty("mediaMode", mediaMode().name());
+ json.addProperty("recordingMode", recordingMode().name());
+ json.addProperty("defaultOutputMode", defaultOutputMode().name());
+ json.addProperty("defaultRecordingLayout", defaultRecordingLayout().name());
+ json.addProperty("defaultCustomLayout", defaultCustomLayout());
+ json.addProperty("customSessionId", customSessionId());
+ if (mediaNode() != null) {
+ JsonObject mediaNodeJson = new JsonObject();
+ mediaNodeJson.addProperty("id", mediaNode());
+ json.add("mediaNode", mediaNodeJson);
+ }
+ if (forcedVideoCodec() != null) {
+ json.addProperty("forcedVideoCodec", forcedVideoCodec().name());
+ }
+ if (isTranscodingAllowed() != null) {
+ json.addProperty("allowTranscoding", isTranscodingAllowed());
+ }
+ return json;
+ }
+
+}
diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java
index 96c39fd6..90629781 100644
--- a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java
+++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java
@@ -17,31 +17,42 @@
package io.openvidu.java.client;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+
/**
- * See {@link io.openvidu.java.client.Session#generateToken(TokenOptions)}
+ * @deprecated Use {@link io.openvidu.java.client.ConnectionProperties
+ * ConnectionProperties} instead
*/
public class TokenOptions {
- private String data;
private OpenViduRole role;
+ private String data;
private KurentoOptions kurentoOptions;
/**
- *
- * Builder for {@link io.openvidu.java.client.TokenOptions}
- *
+ * @deprecated Use {@link io.openvidu.java.client.ConnectionProperties.Builder
+ * ConnectionProperties.Builder} instead
*/
public static class Builder {
- private String data = "";
private OpenViduRole role = OpenViduRole.PUBLISHER;
+ private String data;
private KurentoOptions kurentoOptions;
/**
- * Builder for {@link io.openvidu.java.client.TokenOptions}
+ * Builder for {@link io.openvidu.java.client.TokenOptions}.
*/
public TokenOptions build() {
- return new TokenOptions(this.data, this.role, this.kurentoOptions);
+ return new TokenOptions(this.role, this.data, this.kurentoOptions);
+ }
+
+ /**
+ * Call this method to set the role assigned to this token.
+ */
+ public Builder role(OpenViduRole role) {
+ this.role = role;
+ return this;
}
/**
@@ -71,17 +82,9 @@ public class TokenOptions {
return this;
}
- /**
- * Call this method to set the role assigned to this token
- */
- public Builder role(OpenViduRole role) {
- this.role = role;
- return this;
- }
-
/**
* Call this method to set a {@link io.openvidu.java.client.KurentoOptions}
- * object for this token
+ * object for this token.
*/
public Builder kurentoOptions(KurentoOptions kurentoOptions) {
this.kurentoOptions = kurentoOptions;
@@ -90,26 +93,26 @@ public class TokenOptions {
}
- private TokenOptions(String data, OpenViduRole role, KurentoOptions kurentoOptions) {
- this.data = data;
+ TokenOptions(OpenViduRole role, String data, KurentoOptions kurentoOptions) {
this.role = role;
+ this.data = data;
this.kurentoOptions = kurentoOptions;
}
/**
- * Returns the secure (server-side) metadata assigned to this token
- */
- public String getData() {
- return this.data;
- }
-
- /**
- * Returns the role assigned to this token
+ * Returns the role assigned to this token.
*/
public OpenViduRole getRole() {
return this.role;
}
+ /**
+ * Returns the secure (server-side) metadata assigned to this token.
+ */
+ public String getData() {
+ return this.data;
+ }
+
/**
* Returns the Kurento options assigned to this token
*/
@@ -117,4 +120,23 @@ public class TokenOptions {
return this.kurentoOptions;
}
+ protected JsonObject toJsonObject(String sessionId) {
+ JsonObject json = new JsonObject();
+ json.addProperty("session", sessionId);
+ if (getRole() != null) {
+ json.addProperty("role", getRole().name());
+ } else {
+ json.add("role", JsonNull.INSTANCE);
+ }
+ if (getData() != null) {
+ json.addProperty("data", getData());
+ } else {
+ json.add("data", JsonNull.INSTANCE);
+ }
+ if (this.kurentoOptions != null) {
+ json.add("kurentoOptions", kurentoOptions.toJson());
+ }
+ return json;
+ }
+
}
diff --git a/openvidu-node-client/.gitignore b/openvidu-node-client/.gitignore
index 51757794..3cdfcc6e 100644
--- a/openvidu-node-client/.gitignore
+++ b/openvidu-node-client/.gitignore
@@ -45,4 +45,6 @@ Thumbs.db
/lib/
docs/
-lib/
\ No newline at end of file
+lib/
+
+*.tgz
diff --git a/openvidu-node-client/config/typedoc.js b/openvidu-node-client/config/typedoc.js
index 62bd81de..987eccd7 100644
--- a/openvidu-node-client/config/typedoc.js
+++ b/openvidu-node-client/config/typedoc.js
@@ -12,6 +12,7 @@ module.exports = {
externalPattern: "node_modules",
excludeExternals: true,
excludePrivate: true,
+ excludeProtected: true,
excludeNotExported: true,
theme: "default",
readme: "none",
diff --git a/openvidu-node-client/package-lock.json b/openvidu-node-client/package-lock.json
index 82fbaa40..189f5f83 100644
--- a/openvidu-node-client/package-lock.json
+++ b/openvidu-node-client/package-lock.json
@@ -1,39 +1,91 @@
{
"name": "openvidu-node-client",
- "version": "2.15.0",
+ "version": "2.16.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
- "version": "7.10.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
- "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true,
"requires": {
- "@babel/highlight": "^7.10.1"
+ "@babel/highlight": "^7.10.4"
}
},
"@babel/helper-validator-identifier": {
- "version": "7.10.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz",
- "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
"@babel/highlight": {
- "version": "7.10.1",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz",
- "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"dev": true,
"requires": {
- "@babel/helper-validator-identifier": "^7.10.1",
+ "@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "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"
+ }
+ }
}
},
"@types/node": {
- "version": "14.0.13",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
- "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==",
+ "version": "14.14.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
+ "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==",
"dev": true
},
"abbrev": {
@@ -49,12 +101,12 @@
"dev": true
},
"ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "color-convert": "^1.9.0"
+ "color-convert": "^2.0.1"
}
},
"anymatch": {
@@ -125,12 +177,6 @@
"integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
"dev": true
},
- "array-find-index": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
- "dev": true
- },
"array-slice": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
@@ -161,6 +207,12 @@
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
"dev": true
},
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -168,11 +220,11 @@
"dev": true
},
"axios": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
- "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+ "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"requires": {
- "follow-redirects": "1.5.10"
+ "follow-redirects": "^1.10.0"
}
},
"balanced-match": {
@@ -309,12 +361,12 @@
}
},
"buffer": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
- "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.2.tgz",
+ "integrity": "sha512-XeXCUm+F7uY7fIzq4pKy+BLbZk4SgYS5xwlZOFYD3UEcAD+PwOoTaFr/SaXvhR1yRa8SKyPSZ7LNX4N65w7h8A==",
"requires": {
- "base64-js": "^1.0.2",
- "ieee754": "^1.1.4"
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
}
},
"buffer-from": {
@@ -352,31 +404,14 @@
"unset-value": "^1.0.0"
}
},
- "camelcase": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
- "dev": true
- },
- "camelcase-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
- "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
- "dev": true,
- "requires": {
- "camelcase": "^2.0.0",
- "map-obj": "^1.0.0"
- }
- },
"chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
}
},
"chokidar": {
@@ -433,12 +468,6 @@
}
}
},
- "coffeescript": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
- "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
- "dev": true
- },
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -450,18 +479,18 @@
}
},
"color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
- "color-name": "1.1.3"
+ "color-name": "~1.1.4"
}
},
"color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"colors": {
@@ -507,12 +536,16 @@
"dev": true
},
"cross-spawn": {
- "version": "0.2.9",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.9.tgz",
- "integrity": "sha1-vWf5bAfvtjA7f+lMHpefiEeOCjk=",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "lru-cache": "^2.5.0"
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
}
},
"csproj2ts": {
@@ -535,48 +568,27 @@
}
}
},
- "currently-unhandled": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
- "dev": true,
- "requires": {
- "array-find-index": "^1.0.1"
- }
- },
"dargs": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
- "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-6.1.0.tgz",
+ "integrity": "sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ==",
+ "dev": true
},
"dateformat": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
- "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
- "dev": true,
- "requires": {
- "get-stdin": "^4.0.1",
- "meow": "^3.3.0"
- }
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
+ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
+ "dev": true
},
"debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"requires": {
"ms": "2.0.0"
}
},
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
@@ -652,9 +664,9 @@
"dev": true
},
"duplexer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
"dev": true
},
"error": {
@@ -666,15 +678,6 @@
"string-template": "~0.2.1"
}
},
- "error-ex": {
- "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"
- }
- },
"es6-promise": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-0.1.2.tgz",
@@ -720,15 +723,6 @@
"to-regex": "^3.0.1"
},
"dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@@ -905,16 +899,6 @@
}
}
},
- "find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
- "dev": true,
- "requires": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
"findup-sync": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
@@ -959,12 +943,9 @@
"dev": true
},
"follow-redirects": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
- "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
- "requires": {
- "debug": "=3.1.0"
- }
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
+ "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
},
"for-in": {
"version": "1.0.2",
@@ -991,14 +972,15 @@
}
},
"fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
+ "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"dev": true,
"requires": {
+ "at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
+ "jsonfile": "^6.0.1",
+ "universalify": "^1.0.0"
}
},
"fs.realpath": {
@@ -1018,6 +1000,12 @@
"nan": "^2.12.1"
}
},
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
"gaze": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
@@ -1027,12 +1015,6 @@
"globule": "^1.0.0"
}
},
- "get-stdin": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
- "dev": true
- },
"get-value": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -1046,15 +1028,15 @@
"dev": true
},
"glob": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
- "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
- "minimatch": "^3.0.2",
+ "minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
@@ -1102,22 +1084,6 @@
"glob": "~7.1.1",
"lodash": "~4.17.10",
"minimatch": "~3.0.2"
- },
- "dependencies": {
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
}
},
"graceful-fs": {
@@ -1127,48 +1093,26 @@
"dev": true
},
"grunt": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz",
- "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.3.0.tgz",
+ "integrity": "sha512-6ILlMXv11/4cxuhSMfSU+SfvbxrPuqZrAtLN64+tZpQ3DAKfSQPQHRbTjSbdtxfyQhGZPtN0bDZJ/LdCM5WXXA==",
"dev": true,
"requires": {
- "coffeescript": "~1.10.0",
- "dateformat": "~1.0.12",
+ "dateformat": "~3.0.3",
"eventemitter2": "~0.4.13",
- "exit": "~0.1.1",
+ "exit": "~0.1.2",
"findup-sync": "~0.3.0",
- "glob": "~7.0.0",
- "grunt-cli": "~1.2.0",
+ "glob": "~7.1.6",
+ "grunt-cli": "~1.3.2",
"grunt-known-options": "~1.1.0",
- "grunt-legacy-log": "~2.0.0",
- "grunt-legacy-util": "~1.1.1",
+ "grunt-legacy-log": "~3.0.0",
+ "grunt-legacy-util": "~2.0.0",
"iconv-lite": "~0.4.13",
- "js-yaml": "~3.13.1",
- "minimatch": "~3.0.2",
- "mkdirp": "~1.0.3",
+ "js-yaml": "~3.14.0",
+ "minimatch": "~3.0.4",
+ "mkdirp": "~1.0.4",
"nopt": "~3.0.6",
- "path-is-absolute": "~1.0.0",
- "rimraf": "~2.6.2"
- },
- "dependencies": {
- "grunt-cli": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
- "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
- "dev": true,
- "requires": {
- "findup-sync": "~0.3.0",
- "grunt-known-options": "~1.1.0",
- "nopt": "~3.0.6",
- "resolve": "~1.1.0"
- }
- },
- "resolve": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
- "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
- "dev": true
- }
+ "rimraf": "~3.0.2"
}
},
"grunt-cli": {
@@ -1234,61 +1178,141 @@
}
},
"grunt-contrib-sass": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/grunt-contrib-sass/-/grunt-contrib-sass-1.0.0.tgz",
- "integrity": "sha1-gGg4JRy8DhqU1k1RXN00z2dNcBs=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-sass/-/grunt-contrib-sass-2.0.0.tgz",
+ "integrity": "sha512-RxZ3dlZZTX4YBPu2zMu84NPYgJ2AYAlIdEqlBaixNVyLNbgvJBGUr5Gi0ec6IiOQbt/I/z7uZVN9HsRxgznIRw==",
"dev": true,
"requires": {
- "async": "^0.9.0",
- "chalk": "^1.0.0",
- "cross-spawn": "^0.2.3",
- "dargs": "^4.0.0",
- "which": "^1.0.5"
+ "async": "^2.6.1",
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "dargs": "^6.0.0",
+ "which": "^1.3.1"
},
"dependencies": {
"ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
- },
- "async": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
- "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
- "dev": true
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "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": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
+ "color-convert": "^1.9.0"
}
},
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "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"
+ }
}
}
},
"grunt-contrib-uglify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-4.0.1.tgz",
- "integrity": "sha512-dwf8/+4uW1+7pH72WButOEnzErPGmtUvc8p08B0eQS/6ON0WdeQu0+WFeafaPTbbY1GqtS25lsHWaDeiTQNWPg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.0.0.tgz",
+ "integrity": "sha512-rIFFPJMWKnh6oxDe2b810Ysg5SKoiI0u/FvuvAVpvJ7VHILkKtGqA4jgJ1JWruWQ+1m5FtB1lVSK81YyzIgDUw==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"maxmin": "^2.1.0",
"uglify-js": "^3.5.0",
"uri-path": "^1.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "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"
+ }
+ }
}
},
"grunt-contrib-watch": {
@@ -1321,39 +1345,39 @@
"dev": true
},
"grunt-legacy-log": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
- "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz",
+ "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==",
"dev": true,
"requires": {
"colors": "~1.1.2",
- "grunt-legacy-log-utils": "~2.0.0",
+ "grunt-legacy-log-utils": "~2.1.0",
"hooker": "~0.2.3",
- "lodash": "~4.17.5"
+ "lodash": "~4.17.19"
}
},
"grunt-legacy-log-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
- "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz",
+ "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==",
"dev": true,
"requires": {
- "chalk": "~2.4.1",
- "lodash": "~4.17.10"
+ "chalk": "~4.1.0",
+ "lodash": "~4.17.19"
}
},
"grunt-legacy-util": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
- "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.0.tgz",
+ "integrity": "sha512-ZEmYFB44bblwPE2oz3q3ygfF6hseQja9tx8I3UZIwbUik32FMWewA+d1qSFicMFB+8dNXDkh35HcDCWlpRsGlA==",
"dev": true,
"requires": {
"async": "~1.5.2",
"exit": "~0.1.1",
"getobject": "~0.1.0",
"hooker": "~0.2.3",
- "lodash": "~4.17.10",
- "underscore.string": "~3.3.4",
+ "lodash": "~4.17.20",
+ "underscore.string": "~3.3.5",
"which": "~1.3.0"
}
},
@@ -1366,6 +1390,58 @@
"chalk": "^2.1.0",
"diff": "^3.0.0",
"postcss": "^6.0.11"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "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"
+ }
+ }
}
},
"grunt-string-replace": {
@@ -1471,6 +1547,15 @@
}
}
},
+ "has": {
+ "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"
+ }
+ },
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -1481,9 +1566,9 @@
}
},
"has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"has-value": {
@@ -1519,9 +1604,9 @@
}
},
"highlight.js": {
- "version": "10.0.3",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.0.3.tgz",
- "integrity": "sha512-9FG7SSzv9yOY5CGGxfI6NDm7xLYtMOjKtPBxw7Zff3t5UcRcUNTGEeS8lNjhceL34KeetLMoGMFTGoaa83HwyQ==",
+ "version": "10.3.2",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz",
+ "integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==",
"dev": true
},
"homedir-polyfill": {
@@ -1539,12 +1624,6 @@
"integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
"dev": true
},
- "hosted-git-info": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
- "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
- "dev": true
- },
"http-parser-js": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz",
@@ -1561,18 +1640,9 @@
}
},
"ieee754": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
- },
- "indent-string": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
- "dev": true,
- "requires": {
- "repeating": "^2.0.0"
- }
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inflight": {
"version": "1.0.6",
@@ -1632,12 +1702,6 @@
}
}
},
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
- "dev": true
- },
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@@ -1653,6 +1717,15 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
+ "is-core-module": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz",
+ "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -1803,9 +1876,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -1819,12 +1892,21 @@
"dev": true
},
"jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
- "graceful-fs": "^4.1.6"
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ },
+ "dependencies": {
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ }
}
},
"kind-of": {
@@ -1869,45 +1951,16 @@
"integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==",
"dev": true
},
- "load-json-file": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
- "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "strip-bom": "^2.0.0"
- }
- },
"lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
- },
- "loud-rejection": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
- "dev": true,
- "requires": {
- "currently-unhandled": "^0.4.1",
- "signal-exit": "^3.0.0"
- }
- },
- "lru-cache": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
- "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"lunr": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz",
- "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==",
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"make-error": {
@@ -1931,12 +1984,6 @@
"integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
"dev": true
},
- "map-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
- "dev": true
- },
"map-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@@ -1947,9 +1994,9 @@
}
},
"marked": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/marked/-/marked-1.0.0.tgz",
- "integrity": "sha512-Wo+L1pWTVibfrSr+TTtMuiMfNzmZWiOPeO7rZsQUY5bgsxpHesBEcIWJloWVTFnrMXnf/TL30eTFSGJddmQAng==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.3.tgz",
+ "integrity": "sha512-RQuL2i6I6Gn+9n81IDNGbL0VHnta4a+8ZhqvryXEniTb/hQNtf3i26hi1XWUhzb9BgVyWHKR3UO8MaHtKoYibw==",
"dev": true
},
"maxmin": {
@@ -1991,24 +2038,6 @@
}
}
},
- "meow": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
- "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
- "dev": true,
- "requires": {
- "camelcase-keys": "^2.0.0",
- "decamelize": "^1.1.2",
- "loud-rejection": "^1.0.0",
- "map-obj": "^1.0.1",
- "minimist": "^1.1.3",
- "normalize-package-data": "^2.3.4",
- "object-assign": "^4.0.1",
- "read-pkg-up": "^1.0.1",
- "redent": "^1.0.0",
- "trim-newlines": "^1.0.0"
- }
- },
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -2075,12 +2104,13 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
},
"nan": {
- "version": "2.14.1",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
- "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
+ "version": "2.14.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"dev": true,
"optional": true
},
@@ -2110,9 +2140,15 @@
"dev": true
},
"neo-async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
- "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"nopt": {
@@ -2124,18 +2160,6 @@
"abbrev": "1"
}
},
- "normalize-package-data": {
- "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",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -2267,15 +2291,6 @@
"path-root": "^0.1.1"
}
},
- "parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
- "dev": true,
- "requires": {
- "error-ex": "^1.2.0"
- }
- },
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
@@ -2294,21 +2309,18 @@
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
"dev": true
},
- "path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
- "dev": true,
- "requires": {
- "pinkie-promise": "^2.0.0"
- }
- },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
@@ -2330,38 +2342,6 @@
"integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
"dev": true
},
- "path-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
- "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- },
- "pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
- "dev": true
- },
- "pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
- "dev": true,
- "requires": {
- "pinkie": "^2.0.0"
- }
- },
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -2379,11 +2359,61 @@
"supports-color": "^5.4.0"
},
"dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
+ },
+ "supports-color": {
+ "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"
+ }
}
}
},
@@ -2424,27 +2454,6 @@
"string_decoder": "0.10"
}
},
- "read-pkg": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
- "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
- "dev": true,
- "requires": {
- "load-json-file": "^1.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^1.0.0"
- }
- },
- "read-pkg-up": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
- "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
- "dev": true,
- "requires": {
- "find-up": "^1.0.0",
- "read-pkg": "^1.0.0"
- }
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -2497,16 +2506,6 @@
"resolve": "^1.1.6"
}
},
- "redent": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
- "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
- "dev": true,
- "requires": {
- "indent-string": "^2.1.0",
- "strip-indent": "^1.0.1"
- }
- },
"regex-not": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@@ -2545,11 +2544,12 @@
}
},
"resolve": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
- "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
+ "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
"dev": true,
"requires": {
+ "is-core-module": "^2.0.0",
"path-parse": "^1.0.6"
}
},
@@ -2576,28 +2576,12 @@
"dev": true
},
"rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
- },
- "dependencies": {
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
}
},
"safe-buffer": {
@@ -2662,6 +2646,21 @@
}
}
},
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
"shelljs": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
@@ -2673,12 +2672,6 @@
"rechoir": "^0.6.2"
}
},
- "signal-exit": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
- "dev": true
- },
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -2695,15 +2688,6 @@
"use": "^3.1.0"
},
"dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@@ -2838,38 +2822,6 @@
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
"dev": true
},
- "spdx-correct": {
- "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"
- }
- },
- "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==",
- "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"
- }
- },
- "spdx-license-ids": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
- "dev": true
- },
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -2936,22 +2888,13 @@
"is-utf8": "^0.2.0"
}
},
- "strip-indent": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
- "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
- "dev": true,
- "requires": {
- "get-stdin": "^4.0.1"
- }
- },
"supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
- "has-flag": "^3.0.0"
+ "has-flag": "^4.0.0"
}
},
"tiny-lr": {
@@ -2966,6 +2909,23 @@
"livereload-js": "^2.3.0",
"object-assign": "^4.1.0",
"qs": "^6.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
}
},
"to-object-path": {
@@ -3010,16 +2970,10 @@
"repeat-string": "^1.6.1"
}
},
- "trim-newlines": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
- "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
- "dev": true
- },
"ts-node": {
- "version": "8.10.2",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
- "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+ "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
"dev": true,
"requires": {
"arg": "^4.1.0",
@@ -3038,15 +2992,15 @@
}
},
"tslib": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
- "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tslint": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz",
- "integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==",
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
@@ -3060,29 +3014,56 @@
"mkdirp": "^0.5.3",
"resolve": "^1.3.2",
"semver": "^5.3.0",
- "tslib": "^1.10.0",
+ "tslib": "^1.13.0",
"tsutils": "^2.29.0"
},
"dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "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",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "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"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
},
"mkdirp": {
"version": "0.5.5",
@@ -3092,6 +3073,15 @@
"requires": {
"minimist": "^1.2.5"
}
+ },
+ "supports-color": {
+ "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"
+ }
}
}
},
@@ -3105,31 +3095,37 @@
}
},
"typedoc": {
- "version": "0.17.7",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.7.tgz",
- "integrity": "sha512-PEnzjwQAGjb0O8a6VDE0lxyLAadqNujN5LltsTUhZETolRMiIJv6Ox+Toa8h0XhKHqAOh8MOmB0eBVcWz6nuAw==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.2.tgz",
+ "integrity": "sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==",
"dev": true,
"requires": {
- "fs-extra": "^8.1.0",
+ "fs-extra": "^9.0.1",
"handlebars": "^4.7.6",
- "highlight.js": "^10.0.0",
- "lodash": "^4.17.15",
- "lunr": "^2.3.8",
- "marked": "1.0.0",
+ "highlight.js": "^10.2.0",
+ "lodash": "^4.17.20",
+ "lunr": "^2.3.9",
+ "marked": "^1.1.1",
"minimatch": "^3.0.0",
"progress": "^2.0.3",
+ "semver": "^7.3.2",
"shelljs": "^0.8.4",
- "typedoc-default-themes": "^0.10.1"
+ "typedoc-default-themes": "^0.11.4"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+ "dev": true
+ }
}
},
"typedoc-default-themes": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.1.tgz",
- "integrity": "sha512-SuqAQI0CkwhqSJ2kaVTgl37cWs733uy9UGUqwtcds8pkFK8oRF4rZmCq+FXTGIb9hIUOu40rf5Kojg0Ha6akeg==",
- "dev": true,
- "requires": {
- "lunr": "^2.3.8"
- }
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz",
+ "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==",
+ "dev": true
},
"typescript": {
"version": "3.8.3",
@@ -3138,13 +3134,10 @@
"dev": true
},
"uglify-js": {
- "version": "3.9.4",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.4.tgz",
- "integrity": "sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==",
- "dev": true,
- "requires": {
- "commander": "~2.20.3"
- }
+ "version": "3.11.5",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.5.tgz",
+ "integrity": "sha512-btvv/baMqe7HxP7zJSF7Uc16h1mSfuuSplT0/qdjxseesDU+yYzH33eHBH+eMdeRXwujXspaCTooWHQVVBh09w==",
+ "dev": true
},
"unc-path-regex": {
"version": "0.1.2",
@@ -3175,9 +3168,9 @@
}
},
"universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
+ "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
"dev": true
},
"unset-value": {
@@ -3259,16 +3252,6 @@
"homedir-polyfill": "^1.0.1"
}
},
- "validate-npm-package-license": {
- "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"
- }
- },
"websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
diff --git a/openvidu-node-client/package.json b/openvidu-node-client/package.json
index 9958fe5a..40ea4b8e 100644
--- a/openvidu-node-client/package.json
+++ b/openvidu-node-client/package.json
@@ -1,24 +1,24 @@
{
"author": "OpenVidu",
"dependencies": {
- "axios": "0.19.2",
- "buffer": "5.6.0"
+ "axios": "0.21.1",
+ "buffer": "6.0.2"
},
"description": "OpenVidu Node Client",
"devDependencies": {
- "@types/node": "14.0.13",
- "grunt": "1.1.0",
+ "@types/node": "14.14.7",
+ "grunt": "1.3.0",
"grunt-cli": "1.3.2",
"grunt-contrib-copy": "1.0.0",
- "grunt-contrib-sass": "1.0.0",
- "grunt-contrib-uglify": "4.0.1",
+ "grunt-contrib-sass": "2.0.0",
+ "grunt-contrib-uglify": "5.0.0",
"grunt-contrib-watch": "1.1.0",
"grunt-postcss": "0.9.0",
"grunt-string-replace": "1.3.1",
"grunt-ts": "6.0.0-beta.22",
- "ts-node": "8.10.2",
- "tslint": "6.1.1",
- "typedoc": "0.17.7",
+ "ts-node": "9.0.0",
+ "tslint": "6.1.3",
+ "typedoc": "0.19.2",
"typescript": "3.8.3"
},
"license": "Apache-2.0",
@@ -33,5 +33,5 @@
"docs": "./generate-docs.sh"
},
"typings": "lib/index.d.ts",
- "version": "2.15.0"
+ "version": "2.16.0"
}
diff --git a/openvidu-node-client/src/Connection.ts b/openvidu-node-client/src/Connection.ts
index 836eaa81..88e4263d 100644
--- a/openvidu-node-client/src/Connection.ts
+++ b/openvidu-node-client/src/Connection.ts
@@ -15,37 +15,47 @@
*
*/
-import { OpenViduRole } from './OpenViduRole';
import { Publisher } from './Publisher';
+import { ConnectionProperties } from './ConnectionProperties';
+import { OpenViduRole } from './OpenViduRole';
/**
- * See [[Session.activeConnections]]
+ * See [[Session.connections]]
*/
export class Connection {
/**
- * Identifier of the connection. You can call [[Session.forceDisconnect]] passing this property as parameter
+ * Identifier of the Connection. You can call methods [[Session.forceDisconnect]]
+ * or [[Session.updateConnection]] passing this property as parameter
*/
connectionId: string;
/**
- * Timestamp when this connection was established, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
+ * Returns the status of the Connection. Can be:
+ * - `pending`: if the Connection is waiting for any user to use
+ * its internal token to connect to the session, calling method
+ * [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect)
+ * in OpenVidu Browser.
+ * - `active`: if the internal token of the Connection has already
+ * been used by some user to connect to the session, and it cannot be used
+ * again.
+ */
+ status: string;
+
+ /**
+ * Timestamp when the Connection was created, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
*/
createdAt: number;
/**
- * Role of the connection
+ * Timestamp when the Connection was taken by a user (passing from status "pending" to "active")
+ * in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC)
*/
- role: OpenViduRole;
-
- /**
- * Token associated to the connection
- */
- token: string;
+ activeAt: number;
/**
* PRO
- * Geo location of the connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it)
+ * Geo location of the Connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it)
*/
location: string;
@@ -55,16 +65,22 @@ export class Connection {
platform: string;
/**
- * Data associated to the connection on the server-side. This value is set with property [[TokenOptions.data]] when calling [[Session.generateToken]]
- */
- serverData: string;
-
- /**
- * Data associated to the connection on the client-side. This value is set with second parameter of method
+ * Data associated to the Connection on the client-side. This value is set with second parameter of method
* [Session.connect](/en/stable/api/openvidu-browser/classes/session.html#connect) in OpenVidu Browser
*/
clientData: string;
+ /**
+ * The [[ConnectionProperties]] assigned to the Connection
+ */
+ connectionProperties: ConnectionProperties;
+
+ /**
+ * Token associated to the Connection. This is the value that must be sent to the client-side to be consumed in OpenVidu Browser
+ * method [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect).
+ */
+ token: string;
+
/**
* Array of Publisher objects this particular Connection is publishing to the Session (each Publisher object has one Stream, uniquely
* identified by its `streamId`). You can call [[Session.forceUnpublish]] passing any of this values as parameter
@@ -77,21 +93,112 @@ export class Connection {
*/
subscribers: string[] = [];
+ /**
+ * @hidden deprecated. Inside ConnectionProperties
+ */
+ role?: OpenViduRole;
+ /**
+ * @hidden deprecated. Inside ConnectionProperties
+ */
+ serverData?: string;
+
/**
* @hidden
*/
- constructor(connectionId: string, createdAt: number, role: OpenViduRole, token: string, location: string, platform: string, serverData: string, clientData: string,
- publishers: Publisher[], subscribers: string[]) {
- this.connectionId = connectionId;
- this.createdAt = createdAt;
- this.role = role;
- this.token = token;
- this.location = location;
- this.platform = platform;
- this.serverData = serverData;
- this.clientData = clientData;
- this.publishers = publishers;
- this.subscribers = subscribers;
+ constructor(json) {
+ this.resetWithJson(json);
+ }
+
+ /**
+ * @hidden
+ */
+ resetWithJson(json): Connection {
+
+ this.connectionId = json.connectionId;
+ this.status = json.status;
+ this.createdAt = json.createdAt;
+ this.activeAt = json.activeAt;
+ this.location = json.location;
+ this.platform = json.platform;
+ this.clientData = json.clientData;
+ this.token = json.token;
+ if (this.connectionProperties != null) {
+ this.connectionProperties.type = json.type;
+ this.connectionProperties.data = json.serverData;
+ this.connectionProperties.record = json.record;
+ this.connectionProperties.role = json.role;
+ this.connectionProperties.kurentoOptions = json.kurentoOptions;
+ this.connectionProperties.rtspUri = json.rtspUri;
+ this.connectionProperties.adaptativeBitrate = json.adaptativeBitrate;
+ this.connectionProperties.onlyPlayWithSubscribers = json.onlyPlayWithSubscribers;
+ this.connectionProperties.networkCache = json.networkCache;
+ } else {
+ this.connectionProperties = {
+ type: json.type,
+ data: json.serverData,
+ record: json.record,
+ role: json.role,
+ kurentoOptions: json.kurentoOptions,
+ rtspUri: json.rtspUri,
+ adaptativeBitrate: json.adaptativeBitrate,
+ onlyPlayWithSubscribers: json.onlyPlayWithSubscribers,
+ networkCache: json.networkCache
+ }
+ }
+ this.role = json.role;
+ this.serverData = json.serverData;
+
+ // publishers may be null
+ if (json.publishers != null) {
+
+ // 1. Array to store fetched Publishers and later remove closed ones
+ const fetchedPublisherIds: string[] = [];
+ json.publishers.forEach(jsonPublisher => {
+
+ const publisherObj: Publisher = new Publisher(jsonPublisher);
+ fetchedPublisherIds.push(publisherObj.streamId);
+ let storedPublisher = this.publishers.find(c => c.streamId === publisherObj.streamId);
+
+ if (!!storedPublisher) {
+ // 2. Update existing Publisher
+ storedPublisher.resetWithJson(jsonPublisher);
+ } else {
+ // 3. Add new Publisher
+ this.publishers.push(publisherObj);
+ }
+ });
+
+ // 4. Remove closed Publishers from local collection
+ for (var i = this.publishers.length - 1; i >= 0; --i) {
+ if (!fetchedPublisherIds.includes(this.publishers[i].streamId)) {
+ this.publishers.splice(i, 1);
+ }
+ }
+
+ }
+
+ // subscribers may be null
+ if (json.subscribers != null) {
+
+ // 1. Array to store fetched Subscribers and later remove closed ones
+ const fetchedSubscriberIds: string[] = [];
+ json.subscribers.forEach(jsonSubscriber => {
+ fetchedSubscriberIds.push(jsonSubscriber.streamId)
+ if (this.subscribers.indexOf(jsonSubscriber.streamId) === -1) {
+ // 2. Add new Subscriber
+ this.subscribers.push(jsonSubscriber.streamId);
+ }
+ });
+
+ // 3. Remove closed Subscribers from local collection
+ for (var i = this.subscribers.length - 1; i >= 0; --i) {
+ if (!fetchedSubscriberIds.includes(this.subscribers[i])) {
+ this.subscribers.splice(i, 1);
+ }
+ }
+ }
+
+ return this;
}
/**
@@ -100,17 +207,32 @@ export class Connection {
equalTo(other: Connection): boolean {
let equals: boolean = (
this.connectionId === other.connectionId &&
+ this.status === other.status &&
this.createdAt === other.createdAt &&
- this.role === other.role &&
+ this.activeAt === other.activeAt &&
+ this.connectionProperties.type === other.connectionProperties.type &&
+ this.connectionProperties.data === other.connectionProperties.data &&
+ this.connectionProperties.record === other.connectionProperties.record &&
+ this.connectionProperties.role === other.connectionProperties.role &&
+ this.connectionProperties.rtspUri === other.connectionProperties.rtspUri &&
+ this.connectionProperties.adaptativeBitrate === other.connectionProperties.adaptativeBitrate &&
+ this.connectionProperties.onlyPlayWithSubscribers === other.connectionProperties.onlyPlayWithSubscribers &&
+ this.connectionProperties.networkCache === other.connectionProperties.networkCache &&
this.token === other.token &&
this.location === other.location &&
this.platform === other.platform &&
- this.serverData === other.serverData &&
this.clientData === other.clientData &&
this.subscribers.length === other.subscribers.length &&
this.publishers.length === other.publishers.length);
if (equals) {
- equals = JSON.stringify(this.subscribers) === JSON.stringify(other.subscribers);
+ if (this.connectionProperties.kurentoOptions != null) {
+ equals = JSON.stringify(this.connectionProperties.kurentoOptions) === JSON.stringify(other.connectionProperties.kurentoOptions);
+ } else {
+ equals = (this.connectionProperties.kurentoOptions === other.connectionProperties.kurentoOptions);
+ }
+ }
+ if (equals) {
+ equals = JSON.stringify(this.subscribers.sort()) === JSON.stringify(other.subscribers.sort());
if (equals) {
let i = 0;
while (equals && i < this.publishers.length) {
@@ -125,4 +247,18 @@ export class Connection {
return false;
}
}
+
+ /**
+ * @hidden
+ */
+ overrideConnectionProperties(newConnectionProperties: ConnectionProperties): void {
+ // For now only properties record and role
+ if (newConnectionProperties.record != null) {
+ this.connectionProperties.record = newConnectionProperties.record;
+ }
+ if (newConnectionProperties.role != null) {
+ this.connectionProperties.role = newConnectionProperties.role;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/openvidu-node-client/src/ConnectionProperties.ts b/openvidu-node-client/src/ConnectionProperties.ts
new file mode 100644
index 00000000..ca88e962
--- /dev/null
+++ b/openvidu-node-client/src/ConnectionProperties.ts
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { ConnectionType } from './ConnectionType';
+import { OpenViduRole } from './OpenViduRole';
+
+export interface ConnectionProperties {
+
+ /**
+ * Type of Connection. The [[ConnectionType]] dictates what properties will have effect:
+ *
+ * - **[[ConnectionType.WEBRTC]]**: [[data]], [[record]], [[role]], [[kurentoOptions]]
+ * - **[[ConnectionType.IPCAM]]**: [[data]], [[record]], [[rtspUri]], [[adaptativeBitrate]], [[onlyPlayWithSubscribers]], [[networkCache]]
+ *
+ * @default WEBRTC
+ */
+ type?: ConnectionType;
+
+ /**
+ * Secure (server-side) data associated to this Connection. Every client will receive this data in property `Connection.data`. Object `Connection` can be retrieved by subscribing to event `connectionCreated` of Session object.
+ * - If you have provided no data in your clients when calling method `Session.connect(TOKEN, DATA)` (`DATA` not defined), then `Connection.data` will only have this [[ConnectionProperties.data]] property.
+ * - If you have provided some data when calling `Session.connect(TOKEN, DATA)` (`DATA` defined), then `Connection.data` will have the following structure: `"CLIENT_DATA%/%SERVER_DATA"`, being `CLIENT_DATA` the second
+ * parameter passed in OpenVidu Browser in method `Session.connect` and `SERVER_DATA` this [[ConnectionProperties.data]] property.
+ */
+ data?: string;
+
+ /**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * Whether to record the streams published by this Connection or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded)
+ *
+ * @default true
+ */
+ record?: boolean;
+
+ /**
+ * The role assigned to this Connection
+ *
+ * **Only for [[ConnectionType.WEBRTC]]**
+ *
+ * @default PUBLISHER
+ */
+ role?: OpenViduRole;
+
+ /**
+ * **WARNING**: experimental option. This interface may change in the near future
+ *
+ * Some advanced properties setting the configuration that the WebRTC streams of the Connection will have in Kurento Media Server.
+ * You can adjust:
+ * - `videoMaxRecvBandwidth`: maximum number of Kbps that the Connection will be able to receive from Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
+ * the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
+ * (parameter `OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH`) for every incoming stream of the Connection.
+ * _**WARNING**: the lower value set to this property limits every other bandwidth of the WebRTC pipeline this server-to-client stream belongs to. This includes the user publishing the stream and every other user subscribed to the stream_
+ * - `videoMinRecvBandwidth`: minimum number of Kbps that the cConnection will try to receive from Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
+ * the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
+ * (parameter `OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH`) for every incoming stream of the Connection.
+ * - `videoMaxSendBandwidth`: maximum number of Kbps that the Connection will be able to send to Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
+ * the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
+ * (parameter `OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH`) for every outgoing stream of the Connection.
+ * _**WARNING**: this value limits every other bandwidth of the WebRTC pipeline this client-to-server stream belongs to. This includes every other user subscribed to the stream_
+ * - `videoMinSendBandwidth`: minimum number of Kbps that the Connection will try to send to Kurento Media Server. 0 means unconstrained. Giving a value to this property will override
+ * the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/)
+ * (parameter `OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH`) for every outgoing stream of the Connection.
+ * - `allowedFilters`: names of the filters the Connection will be able to apply. See [Voice and video filters](/en/stable/advanced-features/filters/)
+ *
+ * **Only for [[ConnectionType.WEBRTC]]**
+ */
+ kurentoOptions?: {
+ videoMaxRecvBandwidth?: number,
+ videoMinRecvBandwidth?: number,
+ videoMaxSendBandwidth?: number,
+ videoMinSendBandwidth?: number,
+ allowedFilters?: string[]
+ };
+
+ /**
+ * RTSP URI of an IP camera. For example: `rtsp://your.camera.ip:7777/path`
+ *
+ * **Only for [[ConnectionType.IPCAM]]**
+ */
+ rtspUri?: string;
+
+ /**
+ * Whether to use adaptative bitrate (and therefore adaptative quality) or not. For local network connections
+ * that do not require media transcoding this can be disabled to save CPU power. If you are not sure if transcoding
+ * might be necessary, setting this property to false **may result in media connections not being established**.
+ *
+ * **Only for [[ConnectionType.IPCAM]]**
+ *
+ * @default true
+ */
+ adaptativeBitrate?: boolean;
+
+ /**
+ * Whether to enable the IP camera stream only when some user is subscribed to it, or not. This allows you to reduce
+ * power consumption and network bandwidth in your server while nobody is asking to receive the camera's video.
+ * On the counterpart, first user subscribing to the IP camera stream will take a little longer to receive its video.
+ *
+ * **Only for [[ConnectionType.IPCAM]]**
+ *
+ * @default true
+ */
+ onlyPlayWithSubscribers?: boolean;
+
+ /**
+ * Size of the buffer of the endpoint receiving the IP camera's stream, in milliseconds. The smaller it is, the less
+ * delay the signal will have, but more problematic will be in unstable networks. Use short buffers only if there is
+ * a quality connection between the IP camera and OpenVidu Server.
+ *
+ * **Only for [[ConnectionType.IPCAM]]**
+ *
+ * @default 2000
+ */
+ networkCache?: number;
+
+}
\ No newline at end of file
diff --git a/openvidu-node-client/src/ConnectionType.ts b/openvidu-node-client/src/ConnectionType.ts
new file mode 100644
index 00000000..29268f4b
--- /dev/null
+++ b/openvidu-node-client/src/ConnectionType.ts
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * See [[Session.createConnection]]
+ */
+export enum ConnectionType {
+
+ /**
+ * WebRTC connection. This is the normal type of Connection for a regular user
+ * connecting to a session from an application.
+ */
+ WEBRTC = 'WEBRTC',
+
+ /**
+ * IP camera connection. This is the type of Connection used by IP cameras to
+ * connect to a session.
+ */
+ IPCAM = 'IPCAM'
+}
\ No newline at end of file
diff --git a/openvidu-node-client/src/OpenVidu.ts b/openvidu-node-client/src/OpenVidu.ts
index 7b14ab71..42d06120 100644
--- a/openvidu-node-client/src/OpenVidu.ts
+++ b/openvidu-node-client/src/OpenVidu.ts
@@ -17,13 +17,11 @@
import axios from 'axios';
import { Connection } from './Connection';
-import { Publisher } from './Publisher';
import { Recording } from './Recording';
import { RecordingProperties } from './RecordingProperties';
import { Session } from './Session';
import { SessionProperties } from './SessionProperties';
import { RecordingLayout } from './RecordingLayout';
-import { RecordingMode } from 'RecordingMode';
/**
* @hidden
@@ -47,29 +45,36 @@ export class OpenVidu {
/**
* @hidden
*/
- static readonly API_RECORDINGS: string = '/api/recordings';
+ static readonly API_PATH: string = '/openvidu/api';
/**
* @hidden
*/
- static readonly API_RECORDINGS_START: string = '/start';
+ static readonly API_SESSIONS = OpenVidu.API_PATH + '/sessions';
/**
* @hidden
*/
- static readonly API_RECORDINGS_STOP: string = '/stop';
+ static readonly API_TOKENS = OpenVidu.API_PATH + '/tokens';
/**
* @hidden
*/
- static readonly API_SESSIONS = '/api/sessions';
+ static readonly API_RECORDINGS: string = OpenVidu.API_PATH + '/recordings';
/**
* @hidden
*/
- static readonly API_TOKENS = '/api/tokens';
+ static readonly API_RECORDINGS_START: string = OpenVidu.API_RECORDINGS + '/start';
+ /**
+ * @hidden
+ */
+ static readonly API_RECORDINGS_STOP: string = OpenVidu.API_RECORDINGS + '/stop';
+
+
/**
* Array of active sessions. **This value will remain unchanged since the last time method [[OpenVidu.fetch]]
* was called**. Exceptions to this rule are:
*
+ * - Calling [[OpenVidu.createSession]] automatically adds the new Session object to the local collection.
* - Calling [[Session.fetch]] updates that specific Session status
* - Calling [[Session.close]] automatically removes the Session from the list of active Sessions
* - Calling [[Session.forceDisconnect]] automatically updates the inner affected connections for that specific Session
@@ -83,16 +88,18 @@ export class OpenVidu {
activeSessions: Session[] = [];
/**
- * @param urlOpenViduServer Public accessible IP where your instance of OpenVidu Server is up an running
+ * @param hostname URL where your instance of OpenVidu Server is up an running.
+ * It must be the full URL (e.g. `https://12.34.56.78:1234/`)
+ *
* @param secret Secret used on OpenVidu Server initialization
*/
- constructor(private urlOpenViduServer: string, secret: string) {
+ constructor(private hostname: string, secret: string) {
this.setHostnameAndPort();
this.basicAuth = this.getBasicAuth(secret);
}
/**
- * Creates an OpenVidu session. You can call [[Session.getSessionId]] inside the resolved promise to retrieve the `sessionId`
+ * Creates an OpenVidu session. The session identifier will be available at property [[Session.sessionId]]
*
* @returns A Promise that is resolved to the [[Session]] if success and rejected with an Error object if not.
*/
@@ -140,14 +147,16 @@ export class OpenVidu {
data = {
session: sessionId,
name: !!properties.name ? properties.name : '',
- outputMode: !!properties.outputMode ? properties.outputMode : Recording.OutputMode.COMPOSED,
- hasAudio: !!(properties.hasAudio),
- hasVideo: !!(properties.hasVideo)
+ outputMode: properties.outputMode,
+ hasAudio: properties.hasAudio != null ? properties.hasAudio : null,
+ hasVideo: properties.hasVideo != null ? properties.hasVideo : null,
+ shmSize: properties.shmSize,
+ mediaNode: properties.mediaNode
};
- if (data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
- || data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
- data.resolution = !!properties.resolution ? properties.resolution : '1920x1080';
- data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : RecordingLayout.BEST_FIT;
+ if ((data.hasVideo == null || data.hasVideo) && (data.outputMode == null || data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
+ || data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START])) {
+ data.resolution = properties.resolution;
+ data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : '';
if (data.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) {
data.customLayout = !!properties.customLayout ? properties.customLayout : '';
}
@@ -156,20 +165,18 @@ export class OpenVidu {
} else {
data = JSON.stringify({
session: sessionId,
- name: param2,
- outputMode: Recording.OutputMode.COMPOSED
+ name: param2
});
}
} else {
data = JSON.stringify({
session: sessionId,
- name: '',
- outputMode: Recording.OutputMode.COMPOSED
+ name: ''
});
}
axios.post(
- this.host + OpenVidu.API_RECORDINGS + OpenVidu.API_RECORDINGS_START,
+ this.host + OpenVidu.API_RECORDINGS_START,
data,
{
headers: {
@@ -223,7 +230,7 @@ export class OpenVidu {
return new Promise((resolve, reject) => {
axios.post(
- this.host + OpenVidu.API_RECORDINGS + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId,
+ this.host + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId,
undefined,
{
headers: {
@@ -412,7 +419,7 @@ export class OpenVidu {
public fetch(): Promise {
return new Promise((resolve, reject) => {
axios.get(
- this.host + OpenVidu.API_SESSIONS,
+ this.host + OpenVidu.API_SESSIONS + '?pendingConnections=true',
{
headers: {
Authorization: this.basicAuth
@@ -422,47 +429,44 @@ export class OpenVidu {
.then(res => {
if (res.status === 200) {
- // Array to store fetched sessionIds and later remove closed sessions
- const fetchedSessionIds: string[] = [];
// Boolean to store if any Session has changed
let hasChanged = false;
- res.data.content.forEach(session => {
- fetchedSessionIds.push(session.sessionId);
- let sessionIndex = -1;
- let storedSession = this.activeSessions.find((s, index) => {
- if (s.sessionId === session.sessionId) {
- sessionIndex = index;
- return true;
- } else {
- return false;
- }
- });
+ // 1. Array to store fetched sessionIds and later remove closed ones
+ const fetchedSessionIds: string[] = [];
+ res.data.content.forEach(jsonSession => {
+
+ const fetchedSession: Session = new Session(this, jsonSession);
+ fetchedSessionIds.push(fetchedSession.sessionId);
+ let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId);
+
if (!!storedSession) {
- const fetchedSession: Session = new Session(this).resetSessionWithJson(session);
+
+ // 2. Update existing Session
const changed: boolean = !storedSession.equalTo(fetchedSession);
- if (changed) {
- storedSession = fetchedSession;
- this.activeSessions[sessionIndex] = storedSession;
- }
+ storedSession.resetWithJson(jsonSession);
console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed);
hasChanged = hasChanged || changed;
+
} else {
- this.activeSessions.push(new Session(this, session));
- console.log("New session '" + session.sessionId + "' info fetched");
+
+ // 3. Add new Session
+ this.activeSessions.push(fetchedSession);
+ console.log("New session '" + fetchedSession.sessionId + "' info fetched");
hasChanged = true;
}
});
- // Remove closed sessions from activeSessions array
- this.activeSessions = this.activeSessions.filter(session => {
- if (fetchedSessionIds.includes(session.sessionId)) {
- return true;
- } else {
- console.log("Removing closed session '" + session.sessionId + "'");
+
+ // 4. Remove closed sessions from local collection
+ for (var i = this.activeSessions.length - 1; i >= 0; --i) {
+ let sessionId = this.activeSessions[i].sessionId;
+ if (!fetchedSessionIds.includes(sessionId)) {
+ console.log("Removing closed session '" + sessionId + "'");
hasChanged = true;
- return false;
+ this.activeSessions.splice(i, 1);
}
- });
+ }
+
console.log('Active sessions info fetched: ', fetchedSessionIds);
resolve(hasChanged);
} else {
@@ -498,26 +502,9 @@ export class OpenVidu {
const addWebRtcStatsToConnections = (connection: Connection, connectionsExtendedInfo: any) => {
const connectionExtended = connectionsExtendedInfo.find(c => c.connectionId === connection.connectionId);
if (!!connectionExtended) {
- const publisherArray = [];
connection.publishers.forEach(pub => {
const publisherExtended = connectionExtended.publishers.find(p => p.streamId === pub.streamId);
- const pubAux = {};
- // Standard properties
- pubAux['streamId'] = pub.streamId;
- pubAux['createdAt'] = pub.createdAt;
- const mediaOptions = {
- audioActive: pub.audioActive,
- videoActive: pub.videoActive,
- hasAudio: pub.hasAudio,
- hasVideo: pub.hasVideo,
- typeOfVideo: pub.typeOfVideo,
- frameRate: pub.frameRate,
- videoDimensions: pub.videoDimensions
- };
- pubAux['mediaOptions'] = mediaOptions;
- const newPublisher = new Publisher(pubAux);
- // WebRtc properties
- newPublisher['webRtc'] = {
+ pub['webRtc'] = {
kms: {
events: publisherExtended.events,
localCandidate: publisherExtended.localCandidate,
@@ -529,11 +516,10 @@ export class OpenVidu {
remoteSdp: publisherExtended.remoteSdp
}
};
- newPublisher['localCandidatePair'] = parseRemoteCandidatePair(newPublisher['webRtc'].kms.remoteCandidate);
+ pub['localCandidatePair'] = parseRemoteCandidatePair(pub['webRtc'].kms.remoteCandidate);
if (!!publisherExtended.serverStats) {
- newPublisher['webRtc'].kms.serverStats = publisherExtended.serverStats;
+ pub['webRtc'].kms.serverStats = publisherExtended.serverStats;
}
- publisherArray.push(newPublisher);
});
const subscriberArray = [];
connection.subscribers.forEach(sub => {
@@ -562,7 +548,6 @@ export class OpenVidu {
}
subscriberArray.push(subAux);
});
- connection.publishers = publisherArray;
connection.subscribers = subscriberArray;
}
};
@@ -594,68 +579,64 @@ export class OpenVidu {
.then(res => {
if (res.status === 200) {
- // Array to store fetched sessionIds and later remove closed sessions
- const fetchedSessionIds: string[] = [];
// Global changes
let globalChanges = false;
// Collection of sessionIds telling whether each one of them has changed or not
const sessionChanges: ObjMap = {};
- res.data.content.forEach(session => {
- fetchedSessionIds.push(session.sessionId);
- let sessionIndex = -1;
- let storedSession = this.activeSessions.find((s, index) => {
- if (s.sessionId === session.sessionId) {
- sessionIndex = index;
- return true;
- } else {
- return false;
- }
- });
- if (!!storedSession) {
- const fetchedSession: Session = new Session(this).resetSessionWithJson(session);
- fetchedSession.activeConnections.forEach(connection => {
- addWebRtcStatsToConnections(connection, session.connections.content);
- });
+ // 1. Array to store fetched sessionIds and later remove closed ones
+ const fetchedSessionIds: string[] = [];
+ res.data.content.forEach(jsonSession => {
+ const fetchedSession: Session = new Session(this, jsonSession);
+ fetchedSession.connections.forEach(connection => {
+ addWebRtcStatsToConnections(connection, jsonSession.connections.content);
+ });
+ fetchedSessionIds.push(fetchedSession.sessionId);
+ let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId);
+
+ if (!!storedSession) {
+
+ // 2. Update existing Session
let changed = !storedSession.equalTo(fetchedSession);
if (!changed) { // Check if server webrtc information has changed in any Publisher object (Session.equalTo does not check Publisher.webRtc auxiliary object)
- fetchedSession.activeConnections.forEach((connection, index1) => {
+ fetchedSession.connections.forEach((connection, index1) => {
for (let index2 = 0; (index2 < connection['publishers'].length && !changed); index2++) {
- changed = changed || JSON.stringify(connection['publishers'][index2]['webRtc']) !== JSON.stringify(storedSession.activeConnections[index1]['publishers'][index2]['webRtc']);
+ changed = changed || JSON.stringify(connection['publishers'][index2]['webRtc']) !== JSON.stringify(storedSession.connections[index1]['publishers'][index2]['webRtc']);
}
});
}
- if (changed) {
- storedSession = fetchedSession;
- this.activeSessions[sessionIndex] = storedSession;
- }
+ storedSession.resetWithJson(jsonSession);
+ storedSession.connections.forEach(connection => {
+ addWebRtcStatsToConnections(connection, jsonSession.connections.content);
+ });
console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed);
sessionChanges[storedSession.sessionId] = changed;
globalChanges = globalChanges || changed;
+
} else {
- const newSession = new Session(this, session);
- newSession.activeConnections.forEach(connection => {
- addWebRtcStatsToConnections(connection, session.connections.content);
- });
- this.activeSessions.push(newSession);
- console.log("New session '" + session.sessionId + "' info fetched");
- sessionChanges[session.sessionId] = true;
+
+ // 3. Add new Session
+ this.activeSessions.push(fetchedSession);
+ console.log("New session '" + fetchedSession.sessionId + "' info fetched");
+ sessionChanges[fetchedSession.sessionId] = true;
globalChanges = true;
+
}
});
- // Remove closed sessions from activeSessions array
- this.activeSessions = this.activeSessions.filter(session => {
- if (fetchedSessionIds.includes(session.sessionId)) {
- return true;
- } else {
- console.log("Removing closed session '" + session.sessionId + "'");
- sessionChanges[session.sessionId] = true;
+
+ // 4. Remove closed sessions from local collection
+ for (var i = this.activeSessions.length - 1; i >= 0; --i) {
+ let sessionId = this.activeSessions[i].sessionId;
+ if (!fetchedSessionIds.includes(sessionId)) {
+ console.log("Removing closed session '" + sessionId + "'");
+ sessionChanges[sessionId] = true;
globalChanges = true;
- return false;
+ this.activeSessions.splice(i, 1);
}
- });
+ }
+
console.log('Active sessions info fetched: ', fetchedSessionIds);
resolve({ changes: globalChanges, sessionChanges });
} else {
@@ -687,7 +668,7 @@ export class OpenVidu {
private setHostnameAndPort(): void {
let url: URL;
try {
- url = new URL(this.urlOpenViduServer);
+ url = new URL(this.hostname);
} catch (error) {
console.error('URL format incorrect', error);
throw new Error('URL format incorrect: ' + error);
diff --git a/openvidu-node-client/src/Publisher.ts b/openvidu-node-client/src/Publisher.ts
index d88a009a..fa3ce8d2 100644
--- a/openvidu-node-client/src/Publisher.ts
+++ b/openvidu-node-client/src/Publisher.ts
@@ -74,6 +74,13 @@ export class Publisher {
* @hidden
*/
constructor(json) {
+ this.resetWithJson(json);
+ }
+
+ /**
+ * @hidden
+ */
+ resetWithJson(json): Publisher {
this.streamId = json.streamId;
this.createdAt = json.createdAt;
this.hasAudio = json.mediaOptions.hasAudio;
@@ -83,6 +90,7 @@ export class Publisher {
this.frameRate = json.mediaOptions.frameRate;
this.typeOfVideo = json.mediaOptions.typeOfVideo;
this.videoDimensions = json.mediaOptions.videoDimensions;
+ return this;
}
/**
diff --git a/openvidu-node-client/src/Recording.ts b/openvidu-node-client/src/Recording.ts
index 85635e3a..cc0989a6 100644
--- a/openvidu-node-client/src/Recording.ts
+++ b/openvidu-node-client/src/Recording.ts
@@ -83,7 +83,7 @@ export class Recording {
hasVideo: !!json['hasVideo']
};
if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]
- || this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
+ || this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) {
this.properties.resolution = !!(json['resolution']) ? json['resolution'] : '1920x1080';
this.properties.recordingLayout = !!(json['recordingLayout']) ? json['recordingLayout'] : RecordingLayout.BEST_FIT;
if (this.properties.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) {
@@ -103,7 +103,7 @@ export namespace Recording {
/**
* The recording is starting (cannot be stopped). Some recording may not go
- * through this status and directly reach "started" status
+ * through this status and directly reach "started" status
*/
starting = 'starting',
@@ -113,21 +113,20 @@ export namespace Recording {
started = 'started',
/**
- * The recording has stopped and is being processed. At some point it will reach
- * "ready" status
- */
+ * The recording has stopped and is being processed. At some point it will reach
+ * "ready" status
+ */
stopped = 'stopped',
/**
- * The recording has finished OK and is available for download through OpenVidu
- * Server recordings endpoint:
- * https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
+ * The recording has finished being processed and is available for download through
+ * property [[Recording.url]]
*/
ready = 'ready',
/**
* The recording has failed. This status may be reached from "starting",
- * "started" and "stopped" status
+ * "started" and "stopped" status
*/
failed = 'failed'
}
@@ -141,24 +140,24 @@ export namespace Recording {
* Record all streams in a grid layout in a single archive
*/
COMPOSED = 'COMPOSED',
-
- /**
- * Works the same way as COMPOSED mode, but the necessary recorder
- * service module will start some time in advance and won't be terminated
- * once a specific session recording has ended. This module will remain
- * up and running as long as the session remains active.
- *
- * - **Pros vs COMPOSED**: the process of starting the recording will be noticeably
- * faster. This can be very useful in use cases where a session needs to be
- * recorded multiple times over time, when a better response time is usually
- * desirable.
- * - **Cons vs COMPOSED**: for every session initialized with COMPOSED_QUICK_START
- * recording output mode, extra CPU power will be required in OpenVidu Server.
- * The recording module will be continuously rendering all of the streams being
- * published to the session even when the session is not being recorded. And that
- * is for every session configured with COMPOSED_QUICK_START.
- */
+ /**
+ * Works the same way as COMPOSED mode, but the necessary recorder
+ * service module will start some time in advance and won't be terminated
+ * once a specific session recording has ended. This module will remain
+ * up and running as long as the session remains active.
+ *
+ * - **Pros vs COMPOSED**: the process of starting the recording will be noticeably
+ * faster. This can be very useful in use cases where a session needs to be
+ * recorded multiple times over time, when a better response time is usually
+ * desirable.
+
+ * - **Cons vs COMPOSED**: for every session initialized with COMPOSED_QUICK_START
+ * recording output mode, extra CPU power will be required in OpenVidu Server.
+ * The recording module will be continuously rendering all of the streams being
+ * published to the session even when the session is not being recorded. And that
+ * is for every session configured with COMPOSED_QUICK_START.
+ */
COMPOSED_QUICK_START = 'COMPOSED_QUICK_START',
/**
diff --git a/openvidu-node-client/src/RecordingProperties.ts b/openvidu-node-client/src/RecordingProperties.ts
index 585b53c0..3682c406 100644
--- a/openvidu-node-client/src/RecordingProperties.ts
+++ b/openvidu-node-client/src/RecordingProperties.ts
@@ -67,4 +67,22 @@ export interface RecordingProperties {
* Whether or not to record video. Cannot be set to false at the same time as [[RecordingProperties.hasAudio]]
*/
hasVideo?: boolean;
+
+ /**
+ * If COMPOSED recording, the amount of shared memory reserved for the recording process in bytes.
+ * Minimum 134217728 (128MB). Property ignored if INDIVIDUAL recording. Default to 536870912 (512 MB)
+ */
+ shmSize?: number;
+
+ /**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * The Media Node where to host the recording. The default option if this property is not defined is the same
+ * Media Node hosting the Session to record. This object defines the following properties as Media Node selector:
+ * - `id`: Media Node unique identifier
+ */
+ mediaNode?: {
+ id: string;
+ }
+
}
\ No newline at end of file
diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts
index 3eb7c3de..9e4ff374 100644
--- a/openvidu-node-client/src/Session.ts
+++ b/openvidu-node-client/src/Session.ts
@@ -15,11 +15,11 @@
*
*/
-import axios from 'axios';
+import axios, { AxiosError } from 'axios';
import { Connection } from './Connection';
+import { ConnectionProperties } from './ConnectionProperties';
import { MediaMode } from './MediaMode';
import { OpenVidu } from './OpenVidu';
-import { OpenViduRole } from './OpenViduRole';
import { Publisher } from './Publisher';
import { Recording } from './Recording';
import { RecordingLayout } from './RecordingLayout';
@@ -28,7 +28,6 @@ import { SessionProperties } from './SessionProperties';
import { TokenOptions } from './TokenOptions';
import { VideoCodec } from './VideoCodec';
-
export class Session {
/**
@@ -47,14 +46,26 @@ export class Session {
properties: SessionProperties;
/**
- * Array of active connections to the session. This property always initialize as an empty array and
- * **will remain unchanged since the last time method [[Session.fetch]] was called**. Exceptions to this rule are:
+ * Array of Connections to the Session. This property always initialize as an empty array and
+ * **will remain unchanged since the last time method [[Session.fetch]] or [[OpenVidu.fetch]] was called**.
+ * Exceptions to this rule are:
*
- * - Calling [[Session.forceUnpublish]] also automatically updates each affected Connection status
- * - Calling [[Session.forceDisconnect]] automatically updates each affected Connection status
+ * - Calling [[Session.createConnection]] automatically adds the new Connection object to the local collection.
+ * - Calling [[Session.forceUnpublish]] automatically updates each affected local Connection object.
+ * - Calling [[Session.forceDisconnect]] automatically updates each affected local Connection object.
+ * - Calling [[Session.updateConnection]] automatically updates the attributes of the affected local Connection object.
*
- * To get the array of active connections with their current actual value, you must call [[Session.fetch]] before consulting
- * property [[activeConnections]]
+ * To get the array of Connections with their current actual value, you must call [[Session.fetch]] or [[OpenVidu.fetch]]
+ * before consulting property [[connections]]
+ */
+ connections: Connection[] = [];
+
+ /**
+ * Array containing the active Connections of the Session. It is a subset of [[Session.connections]] array containing only
+ * those Connections with property [[Connection.status]] to `active`.
+ *
+ * To get the array of active Connections with their current actual value, you must call [[Session.fetch]] or [[OpenVidu.fetch]]
+ * before consulting property [[activeConnections]]
*/
activeConnections: Connection[] = [];
@@ -71,7 +82,7 @@ export class Session {
// Defined parameter
if (!!propertiesOrJson.sessionId) {
// Parameter is a JSON representation of Session ('sessionId' property always defined)
- this.resetSessionWithJson(propertiesOrJson);
+ this.resetWithJson(propertiesOrJson);
} else {
// Parameter is a SessionProperties object
this.properties = propertiesOrJson;
@@ -84,32 +95,23 @@ export class Session {
this.properties.recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
this.properties.defaultOutputMode = !!this.properties.defaultOutputMode ? this.properties.defaultOutputMode : Recording.OutputMode.COMPOSED;
this.properties.defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
- this.properties.allowTranscoding = !!this.properties.allowTranscoding ? this.properties.allowTranscoding : null;
- this.properties.forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : null;
+ this.properties.forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : undefined;
+ this.properties.allowTranscoding = this.properties.allowTranscoding != null ? this.properties.allowTranscoding : undefined;
}
/**
- * Gets the unique identifier of the Session
- */
- public getSessionId(): string {
- return this.sessionId;
- }
-
- /**
- * Gets a new token associated to Session object
+ * @deprecated Use [[Session.createConnection]] instead to get a [[Connection]] object.
*
- * @returns A Promise that is resolved to the _token_ if success and rejected with an Error object if not
+ * @returns A Promise that is resolved to the generated _token_ string if success and rejected with an Error object if not
*/
public generateToken(tokenOptions?: TokenOptions): Promise {
return new Promise((resolve, reject) => {
-
const data = JSON.stringify({
session: this.sessionId,
- role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : OpenViduRole.PUBLISHER,
- data: (!!tokenOptions && !!tokenOptions.data) ? tokenOptions.data : '',
- kurentoOptions: (!!tokenOptions && !!tokenOptions.kurentoOptions) ? tokenOptions.kurentoOptions : {},
+ role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : null,
+ data: (!!tokenOptions && !!tokenOptions.data) ? tokenOptions.data : null,
+ kurentoOptions: (!!tokenOptions && !!tokenOptions.kurentoOptions) ? tokenOptions.kurentoOptions : null
});
-
axios.post(
this.ov.host + OpenVidu.API_TOKENS,
data,
@@ -123,26 +125,57 @@ export class Session {
.then(res => {
if (res.status === 200) {
// SUCCESS response from openvidu-server. Resolve token
- resolve(res.data.id);
+ resolve(res.data.token);
} else {
// ERROR response from openvidu-server. Resolve HTTP status
reject(new Error(res.status.toString()));
}
}).catch(error => {
- if (error.response) {
- // The request was made and the server responded with a status code (not 2xx)
- reject(new Error(error.response.status.toString()));
- } else if (error.request) {
- // The request was made but no response was received
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
- // http.ClientRequest in node.js
- console.error(error.request);
- reject(new Error(error.request));
- } else {
- // Something happened in setting up the request that triggered an Error
- console.error('Error', error.message);
- reject(new Error(error.message));
+ this.handleError(error, reject);
+ });
+ });
+ }
+
+ /**
+ * Creates a new Connection object associated to Session object and configured with
+ * `connectionProperties`. Each user connecting to the Session requires a Connection.
+ * The token string value to send to the client side is available at [[Connection.token]].
+ *
+ * @returns A Promise that is resolved to the generated [[Connection]] object if success and rejected with an Error object if not
+ */
+ public createConnection(connectionProperties?: ConnectionProperties): Promise {
+ return new Promise((resolve, reject) => {
+ const data = JSON.stringify({
+ role: (!!connectionProperties && !!connectionProperties.role) ? connectionProperties.role : null,
+ data: (!!connectionProperties && !!connectionProperties.data) ? connectionProperties.data : null,
+ record: !!connectionProperties ? connectionProperties.record : null,
+ kurentoOptions: (!!connectionProperties && !!connectionProperties.kurentoOptions) ? connectionProperties.kurentoOptions : null
+ });
+ axios.post(
+ this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection',
+ data,
+ {
+ headers: {
+ 'Authorization': this.ov.basicAuth,
+ 'Content-Type': 'application/json'
}
+ }
+ )
+ .then(res => {
+ if (res.status === 200) {
+ // SUCCESS response from openvidu-server. Store and resolve Connection
+ const connection = new Connection(res.data);
+ this.connections.push(connection);
+ if (connection.status === 'active') {
+ this.activeConnections.push(connection);
+ }
+ resolve(new Connection(res.data));
+ } else {
+ // ERROR response from openvidu-server. Resolve HTTP status
+ reject(new Error(res.status.toString()));
+ }
+ }).catch(error => {
+ this.handleError(error, reject);
});
});
}
@@ -174,29 +207,17 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
- if (error.response) {
- // The request was made and the server responded with a status code (not 2xx)
- reject(new Error(error.response.status.toString()));
- } else if (error.request) {
- // The request was made but no response was received
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
- // http.ClientRequest in node.js
- console.error(error.request);
- reject(new Error(error.request));
- } else {
- // Something happened in setting up the request that triggered an Error
- console.error('Error', error.message);
- reject(new Error(error.message));
- }
+ this.handleError(error, reject);
});
});
}
/**
- * Updates every property of the Session with the current status it has in OpenVidu Server. This is especially useful for accessing the list of active
- * connections of the Session ([[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]] or [[Session.forceUnpublish]].
+ * Updates every property of the Session with the current status it has in OpenVidu Server. This is especially useful for accessing the list of
+ * Connections of the Session ([[Session.connections]], [[Session.activeConnections]]) and use those values to call [[Session.forceDisconnect]],
+ * [[Session.forceUnpublish]] or [[Session.updateConnection]].
*
- * To update every Session object owned by OpenVidu object, call [[OpenVidu.fetch]]
+ * To update all Session objects owned by OpenVidu object at once, call [[OpenVidu.fetch]]
*
* @returns A promise resolved to true if the Session status has changed with respect to the server, or to false if not.
* This applies to any property or sub-property of the Session object
@@ -205,7 +226,7 @@ export class Session {
return new Promise((resolve, reject) => {
const beforeJSON: string = JSON.stringify(this, this.removeCircularOpenViduReference);
axios.get(
- this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId,
+ this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '?pendingConnections=true',
{
headers: {
'Authorization': this.ov.basicAuth,
@@ -216,7 +237,7 @@ export class Session {
.then(res => {
if (res.status === 200) {
// SUCCESS response from openvidu-server
- this.resetSessionWithJson(res.data);
+ this.resetWithJson(res.data);
const afterJSON: string = JSON.stringify(this, this.removeCircularOpenViduReference);
const hasChanged: boolean = !(beforeJSON === afterJSON);
console.log("Session info fetched for session '" + this.sessionId + "'. Any change: " + hasChanged);
@@ -226,32 +247,26 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
- if (error.response) {
- // The request was made and the server responded with a status code (not 2xx)
- reject(new Error(error.response.status.toString()));
- } else if (error.request) {
- // The request was made but no response was received
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
- // http.ClientRequest in node.js
- console.error(error.request);
- reject(new Error(error.request));
- } else {
- // Something happened in setting up the request that triggered an Error
- console.error('Error', error.message);
- reject(new Error(error.message));
- }
+ this.handleError(error, reject);
});
});
}
/**
- * Forces the user with Connection `connectionId` to leave the session. OpenVidu Browser will trigger the proper events on the client-side
- * (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`
+ * Removes the Connection from the Session. This can translate into a forced eviction of a user from the Session if the
+ * Connection had status `active` or into a token invalidation if no user had taken the Connection yet (status `pending`).
*
- * You can get `connection` parameter from [[Session.activeConnections]] array ([[Connection.connectionId]] for getting each `connectionId` property).
- * Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
+ * In the first case, OpenVidu Browser will trigger the proper events in the client-side (`streamDestroyed`, `connectionDestroyed`,
+ * `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`.
*
- * @returns A Promise that is resolved if the user was successfully disconnected and rejected with an Error object if not
+ * In the second case, the token of the Connection will be invalidated and no user will be able to connect to the session with it.
+ *
+ * This method automatically updates the properties of the local affected objects. This means that there is no need to call
+ * [[Session.fetch]] or [[OpenVidu.fetch]]] to see the changes consequence of the execution of this method applied in the local objects.
+ *
+ * @param connection The Connection object to remove from the session, or its `connectionId` property
+ *
+ * @returns A Promise that is resolved if the Connection was successfully removed from the Session and rejected with an Error object if not
*/
public forceDisconnect(connection: string | Connection): Promise {
return new Promise((resolve, reject) => {
@@ -267,9 +282,9 @@ export class Session {
.then(res => {
if (res.status === 204) {
// SUCCESS response from openvidu-server
- // Remove connection from activeConnections array
+ // Remove connection from connections array
let connectionClosed;
- this.activeConnections = this.activeConnections.filter(con => {
+ this.connections = this.connections.filter(con => {
if (con.connectionId !== connectionId) {
return true;
} else {
@@ -280,7 +295,7 @@ export class Session {
// Remove every Publisher of the closed connection from every subscriber list of other connections
if (!!connectionClosed) {
connectionClosed.publishers.forEach(publisher => {
- this.activeConnections.forEach(con => {
+ this.connections.forEach(con => {
con.subscribers = con.subscribers.filter(subscriber => {
// tslint:disable:no-string-literal
if (!!subscriber['streamId']) {
@@ -295,8 +310,9 @@ export class Session {
});
});
} else {
- console.warn("The closed connection wasn't fetched in OpenVidu Java Client. No changes in the collection of active connections of the Session");
+ console.warn("The closed connection wasn't fetched in OpenVidu Node Client. No changes in the collection of active connections of the Session");
}
+ this.updateActiveConnectionsArray();
console.log("Connection '" + connectionId + "' closed");
resolve();
} else {
@@ -305,30 +321,22 @@ export class Session {
}
})
.catch(error => {
- if (error.response) {
- // The request was made and the server responded with a status code (not 2xx)
- reject(new Error(error.response.status.toString()));
- } else if (error.request) {
- // The request was made but no response was received
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
- // http.ClientRequest in node.js
- console.error(error.request);
- reject(new Error(error.request));
- } else {
- // Something happened in setting up the request that triggered an Error
- console.error('Error', error.message);
- reject(new Error(error.message));
- }
+ this.handleError(error, reject);
});
});
}
/**
- * Forces some user to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it).
+ * Forces some Connection to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it).
* OpenVidu Browser will trigger the proper events on the client-side (`streamDestroyed`) with reason set to `"forceUnpublishByServer"`.
*
* You can get `publisher` parameter from [[Connection.publishers]] array ([[Publisher.streamId]] for getting each `streamId` property).
- * Remember to call [[Session.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
+ * Remember to call [[Session.fetch]] or [[OpenVidu.fetch]] before to fetch the current actual properties of the Session from OpenVidu Server
+ *
+ * This method automatically updates the properties of the local affected objects. This means that there is no need to call
+ * [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects.
+ *
+ * @param publisher The Publisher object to unpublish, or its `streamId` property
*
* @returns A Promise that is resolved if the stream was successfully unpublished and rejected with an Error object if not
*/
@@ -347,7 +355,7 @@ export class Session {
.then(res => {
if (res.status === 204) {
// SUCCESS response from openvidu-server
- this.activeConnections.forEach(connection => {
+ this.connections.forEach(connection => {
// Try to remove the Publisher from the Connection publishers collection
connection.publishers = connection.publishers.filter(pub => pub.streamId !== streamId);
// Try to remove the Publisher from the Connection subscribers collection
@@ -363,6 +371,7 @@ export class Session {
}
}
});
+ this.updateActiveConnectionsArray();
console.log("Stream '" + streamId + "' unpublished");
resolve();
} else {
@@ -370,24 +379,79 @@ export class Session {
reject(new Error(res.status.toString()));
}
}).catch(error => {
- if (error.response) {
- // The request was made and the server responded with a status code (not 2xx)
- reject(new Error(error.response.status.toString()));
- } else if (error.request) {
- // The request was made but no response was received
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
- // http.ClientRequest in node.js
- console.error(error.request);
- reject(new Error(error.request));
- } else {
- // Something happened in setting up the request that triggered an Error
- console.error('Error', error.message);
- reject(new Error(error.message));
- }
+ this.handleError(error, reject);
});
});
}
+ /**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * Updates the properties of a Connection with a [[ConnectionProperties]] object.
+ * Only these properties can be updated:
+ *
+ * - [[ConnectionProperties.role]]
+ * - [[ConnectionProperties.record]]
+ *
+ * This method automatically updates the properties of the local affected objects. This means that there is no need to call
+ * [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects.
+ *
+ * The affected client will trigger one [ConnectionPropertyChangedEvent](/en/stable/api/openvidu-browser/classes/connectionpropertychangedevent.html)
+ * for each modified property.
+ *
+ * @param connectionId The [[Connection.connectionId]] of the Connection object to modify
+ * @param connectionProperties A new [[ConnectionProperties]] object with the updated values to apply
+ *
+ * @returns A Promise that is resolved to the updated [[Connection]] object if the operation was
+ * successful and rejected with an Error object if not
+ */
+ public updateConnection(connectionId: string, connectionProperties: ConnectionProperties): Promise {
+ return new Promise((resolve, reject) => {
+ axios.patch(
+ this.ov.host + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId,
+ connectionProperties,
+ {
+ headers: {
+ 'Authorization': this.ov.basicAuth,
+ 'Content-Type': 'application/json'
+ }
+ }
+ )
+ .then(res => {
+ if (res.status === 200) {
+ console.log('Connection ' + connectionId + ' updated');
+ } else {
+ // ERROR response from openvidu-server. Resolve HTTP status
+ reject(new Error(res.status.toString()));
+ return;
+ }
+ // Update the actual Connection object with the new options
+ const existingConnection: Connection = this.connections.find(con => con.connectionId === connectionId);
+ if (!existingConnection) {
+ // The updated Connection is not available in local map
+ const newConnection: Connection = new Connection(res.data);
+ this.connections.push(newConnection);
+ this.updateActiveConnectionsArray();
+ resolve(newConnection);
+ } else {
+ // The updated Connection was available in local map
+ existingConnection.overrideConnectionProperties(connectionProperties);
+ this.updateActiveConnectionsArray();
+ resolve(existingConnection);
+ }
+ }).catch(error => {
+ this.handleError(error, reject);
+ });
+ });
+ }
+
+ /**
+ * @hidden
+ */
+ public getSessionId(): string {
+ return this.sessionId;
+ }
+
/**
* @hidden
*/
@@ -399,18 +463,17 @@ export class Session {
}
const mediaMode = !!this.properties.mediaMode ? this.properties.mediaMode : MediaMode.ROUTED;
- const recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
+ const recordingMode = !!this.properties.recordingMode ? this.properties.recordingMode : RecordingMode.MANUAL;
const defaultOutputMode = !!this.properties.defaultOutputMode ? this.properties.defaultOutputMode : Recording.OutputMode.COMPOSED;
- const defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
- const defaultCustomLayout = !!this.properties.defaultCustomLayout ? this.properties.defaultCustomLayout : '';
+ const defaultRecordingLayout = !!this.properties.defaultRecordingLayout ? this.properties.defaultRecordingLayout : RecordingLayout.BEST_FIT;
+ const defaultCustomLayout = !!this.properties.defaultCustomLayout ? this.properties.defaultCustomLayout : '';
const customSessionId = !!this.properties.customSessionId ? this.properties.customSessionId : '';
- const forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : null;
- const allowTranscoding = !!this.properties.allowTranscoding ? this.properties.allowTranscoding : null;
-
- const data = JSON.stringify({ mediaMode, recordingMode,
- defaultOutputMode, defaultRecordingLayout, defaultCustomLayout,
- customSessionId, forcedVideoCodec, allowTranscoding
- });
+ const mediaNode = !!this.properties.mediaNode ? this.properties.mediaNode : undefined;
+ const forcedVideoCodec = !!this.properties.forcedVideoCodec ? this.properties.forcedVideoCodec : undefined;
+ const allowTranscoding = this.properties.allowTranscoding != null ? this.properties.allowTranscoding : undefined;
+
+ const data = JSON.stringify({mediaMode, recordingMode, defaultOutputMode, defaultRecordingLayout, defaultCustomLayout,
+ customSessionId, mediaNode, forcedVideoCodec, allowTranscoding});
axios.post(
this.ov.host + OpenVidu.API_SESSIONS,
@@ -431,7 +494,9 @@ export class Session {
this.properties.recordingMode = recordingMode;
this.properties.defaultOutputMode = defaultOutputMode;
this.properties.defaultRecordingLayout = defaultRecordingLayout;
+ this.properties.defaultCustomLayout = defaultCustomLayout;
this.properties.customSessionId = customSessionId;
+ this.properties.mediaNode = mediaNode;
this.properties.forcedVideoCodec = res.data.forcedVideoCodec;
this.properties.allowTranscoding = res.data.allowTranscoding;
resolve(this.sessionId);
@@ -467,58 +532,67 @@ export class Session {
/**
* @hidden
*/
- public resetSessionWithJson(json): Session {
+ public resetWithJson(json): Session {
this.sessionId = json.sessionId;
this.createdAt = json.createdAt;
this.recording = json.recording;
- let customSessionId: string;
- let defaultCustomLayout: string;
- if (!!this.properties) {
- customSessionId = this.properties.customSessionId;
- defaultCustomLayout = !!json.defaultCustomLayout ? json.defaultCustomLayout : this.properties.defaultCustomLayout;
- }
this.properties = {
+ customSessionId: json.customSessionId,
mediaMode: json.mediaMode,
recordingMode: json.recordingMode,
defaultOutputMode: json.defaultOutputMode,
defaultRecordingLayout: json.defaultRecordingLayout,
- forcedVideoCodec: !!json.forcedVideoCodec ? json.forcedVideoCodec : null,
- allowTranscoding: !!json.allowTranscoding ? json.allowTranscoding : null
+ defaultCustomLayout: json.defaultCustomLayout,
+ forcedVideoCodec: json.forcedVideoCodec,
+ allowTranscoding: json.allowTranscoding
};
- if (!!customSessionId) {
- this.properties.customSessionId = customSessionId;
- } else if (!!json.customSessionId) {
- this.properties.customSessionId = json.customSessionId;
+ if (json.defaultRecordingLayout == null) {
+ delete this.properties.defaultRecordingLayout;
}
- if (!!defaultCustomLayout) {
- this.properties.defaultCustomLayout = defaultCustomLayout;
+ if (json.customSessionId == null) {
+ delete this.properties.customSessionId;
+ }
+ if (json.defaultCustomLayout == null) {
+ delete this.properties.defaultCustomLayout;
+ }
+ if (json.mediaNode == null) {
+ delete this.properties.mediaNode;
+ }
+ if (json.forcedVideoCodec == null) {
+ delete this.properties.forcedVideoCodec;
+ }
+ if (json.allowTranscoding == null) {
+ delete this.properties.allowTranscoding;
}
- this.activeConnections = [];
- json.connections.content.forEach(connection => {
- const publishers: Publisher[] = [];
- connection.publishers.forEach(publisher => {
- publishers.push(new Publisher(publisher));
- });
- const subscribers: string[] = [];
- connection.subscribers.forEach(subscriber => {
- subscribers.push(subscriber.streamId);
- });
- this.activeConnections.push(
- new Connection(
- connection.connectionId,
- connection.createdAt,
- connection.role,
- connection.token,
- connection.location,
- connection.platform,
- connection.serverData,
- connection.clientData,
- publishers,
- subscribers));
+ // 1. Array to store fetched connections and later remove closed ones
+ const fetchedConnectionIds: string[] = [];
+ json.connections.content.forEach(jsonConnection => {
+
+ const connectionObj: Connection = new Connection(jsonConnection);
+ fetchedConnectionIds.push(connectionObj.connectionId);
+ let storedConnection = this.connections.find(c => c.connectionId === connectionObj.connectionId);
+
+ if (!!storedConnection) {
+ // 2. Update existing Connection
+ storedConnection.resetWithJson(jsonConnection);
+ } else {
+ // 3. Add new Connection
+ this.connections.push(connectionObj);
+ }
});
+
+ // 4. Remove closed sessions from local collection
+ for (var i = this.connections.length - 1; i >= 0; --i) {
+ if (!fetchedConnectionIds.includes(this.connections[i].connectionId)) {
+ this.connections.splice(i, 1);
+ }
+ }
+
// Order connections by time of creation
- this.activeConnections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0));
+ this.connections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0));
+ // Populate activeConnections array
+ this.updateActiveConnectionsArray();
return this;
}
@@ -530,13 +604,13 @@ export class Session {
this.sessionId === other.sessionId &&
this.createdAt === other.createdAt &&
this.recording === other.recording &&
- this.activeConnections.length === other.activeConnections.length &&
+ this.connections.length === other.connections.length &&
JSON.stringify(this.properties) === JSON.stringify(other.properties)
);
if (equals) {
let i = 0;
- while (equals && i < this.activeConnections.length) {
- equals = this.activeConnections[i].equalTo(other.activeConnections[i]);
+ while (equals && i < this.connections.length) {
+ equals = this.connections[i].equalTo(other.connections[i]);
i++;
}
return equals;
@@ -556,4 +630,36 @@ export class Session {
}
}
-}
\ No newline at end of file
+ /**
+ * @hidden
+ */
+ private updateActiveConnectionsArray() {
+ this.activeConnections = [];
+ this.connections.forEach(con => {
+ if (con.status === 'active') {
+ this.activeConnections.push(con);
+ }
+ });
+ }
+
+ /**
+ * @hidden
+ */
+ private handleError(error: AxiosError, reject: (reason?: any) => void) {
+ if (error.response) {
+ // The request was made and the server responded with a status code (not 2xx)
+ reject(new Error(error.response.status.toString()));
+ } else if (error.request) {
+ // The request was made but no response was received
+ // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
+ // http.ClientRequest in node.js
+ console.error(error.request);
+ reject(new Error(error.request));
+ } else {
+ // Something happened in setting up the request that triggered an Error
+ console.error('Error', error.message);
+ reject(new Error(error.message));
+ }
+ }
+
+}
diff --git a/openvidu-node-client/src/SessionProperties.ts b/openvidu-node-client/src/SessionProperties.ts
index 91e182c8..9115a284 100644
--- a/openvidu-node-client/src/SessionProperties.ts
+++ b/openvidu-node-client/src/SessionProperties.ts
@@ -65,21 +65,31 @@ export interface SessionProperties {
* If this parameter is undefined or an empty string, OpenVidu Server will generate a random sessionId for you.
*/
customSessionId?: string;
-
+
+ /**
+ * **This feature is part of OpenVidu Pro tier** PRO
+ *
+ * The Media Node where to host the session. The default option if this property is not defined is the less loaded
+ * Media Node at the moment the first user joins the session. This object defines the following properties as Media Node selector:
+ * - `id`: Media Node unique identifier
+ */
+ mediaNode?: {
+ id: string;
+ }
+
/**
* It defines which video codec do you want to be forcibly used for this session.
* This allows browsers/clients to use the same codec avoiding transcoding in the media server.
- * If the browser/client is not compatible with the specified codec and [[allowTranscoding]]
+ * If the browser/client is not compatible with the specified codec and [[allowTranscoding]]
* is false
and exception will occur.
- *
- * If forcedVideoCodec is set to NONE, no codec will be forced.
+ *
+ * If forcedVideoCodec is set to NONE, no codec will be forced.
*/
- forcedVideoCodec?: VideoCodec;
-
+ forcedVideoCodec?: string;
+
/**
* It defines if you want to allow transcoding in the media server or not
* when [[forcedVideoCodec]] is not compatible with the browser/client.
*/
allowTranscoding?: boolean;
-
}
diff --git a/openvidu-node-client/src/TokenOptions.ts b/openvidu-node-client/src/TokenOptions.ts
index 910cd657..5dd41539 100644
--- a/openvidu-node-client/src/TokenOptions.ts
+++ b/openvidu-node-client/src/TokenOptions.ts
@@ -18,10 +18,17 @@
import { OpenViduRole } from './OpenViduRole';
/**
- * See [[Session.generateToken]]
+ * @deprecated Use [[ConnectionProperties]] instead
*/
export interface TokenOptions {
+ /**
+ * The role assigned to this token
+ *
+ * @default PUBLISHER
+ */
+ role?: OpenViduRole;
+
/**
* Secure (server-side) data associated to this token. Every client will receive this data in property `Connection.data`. Object `Connection` can be retrieved by subscribing to event `connectionCreated` of Session object.
* - If you have provided no data in your clients when calling method `Session.connect(TOKEN, DATA)` (`DATA` not defined), then `Connection.data` will only have this [[TokenOptions.data]] property.
@@ -30,11 +37,6 @@ export interface TokenOptions {
*/
data?: string;
- /**
- * The role assigned to this token
- */
- role?: OpenViduRole;
-
/**
* **WARNING**: experimental option. This interface may change in the near future
*
diff --git a/openvidu-node-client/src/index.ts b/openvidu-node-client/src/index.ts
index 4113ac84..669b4e90 100644
--- a/openvidu-node-client/src/index.ts
+++ b/openvidu-node-client/src/index.ts
@@ -3,6 +3,8 @@ export * from './OpenViduRole';
export * from './Session';
export * from './SessionProperties';
export * from './TokenOptions';
+export * from './ConnectionProperties';
+export * from './ConnectionType';
export * from './MediaMode';
export * from './RecordingLayout';
export * from './RecordingMode';
diff --git a/openvidu-server/deployments/ce/aws/CF-OpenVidu.yaml.template b/openvidu-server/deployments/ce/aws/CF-OpenVidu.yaml.template
index f6607d9c..caedbb64 100644
--- a/openvidu-server/deployments/ce/aws/CF-OpenVidu.yaml.template
+++ b/openvidu-server/deployments/ce/aws/CF-OpenVidu.yaml.template
@@ -44,18 +44,18 @@ Parameters:
# OpenVidu configuration
OpenViduSecret:
- Description: "Secret to connect to this OpenVidu Platform. No whitespaces or quotations allowed"
+ Description: "Secret to connect to this OpenVidu Platform. Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
Type: String
- AllowedPattern: ^((?!")(?! ).)+$
+ AllowedPattern: ^[a-zA-Z0-9_-]+$
NoEcho: true
- ConstraintDescription: OpenVidu Secret is mandatory
+ ConstraintDescription: "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
# EC2 Instance configuration
InstanceType:
Description: "Specifies the EC2 instance type for your OpenVidu instance"
Type: String
- Default: t2.xlarge
+ Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@@ -115,10 +115,12 @@ Parameters:
- false
Default: true
+#start_mappings
Mappings:
OVAMIMAP:
eu-west-1:
AMI: OV_AMI_ID
+#end_mappings
Metadata:
'AWS::CloudFormation::Interface':
@@ -272,6 +274,10 @@ Resources:
# Restart all services
pushd /opt/openvidu
+ docker-compose up -d kms
+ until docker-compose ps | grep kms | grep healthy; do
+ echo "Waiting kms..."
+ done
docker-compose up -d
popd
mode: "000755"
@@ -293,6 +299,8 @@ Resources:
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource OpenviduServer
+ export HOME="/root"
+
# Replace .env variables
/usr/local/bin/feedGroupVars.sh || { echo "[Openvidu] Parameters incorrect/insufficient"; exit 1; }
@@ -306,6 +314,10 @@ Resources:
# Start openvidu application
pushd /opt/openvidu
+ docker-compose up -d kms
+ until docker-compose ps | grep kms | grep healthy; do
+ echo "Waiting kms..."
+ done
docker-compose up -d
popd
@@ -327,10 +339,9 @@ Resources:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
- VolumeType: io1
- Iops: 200
+ VolumeType: gp2
DeleteOnTermination: true
- VolumeSize: 100
+ VolumeSize: 200
MyEIP:
Type: 'AWS::EC2::EIPAssociation'
@@ -373,11 +384,11 @@ Resources:
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 40000
- ToPort: 65535
+ ToPort: 57000
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 40000
- ToPort: 65535
+ ToPort: 57000
CidrIp: 0.0.0.0/0
Outputs:
diff --git a/openvidu-server/deployments/ce/aws/cfn-mkt-ov-ce-ami.yaml.template b/openvidu-server/deployments/ce/aws/cfn-mkt-ov-ce-ami.yaml.template
index c1a951f9..7223023f 100644
--- a/openvidu-server/deployments/ce/aws/cfn-mkt-ov-ce-ami.yaml.template
+++ b/openvidu-server/deployments/ce/aws/cfn-mkt-ov-ce-ami.yaml.template
@@ -141,5 +141,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
- Timeout: PT10M
+ Timeout: PT20M
Count: 1
diff --git a/openvidu-server/deployments/ce/aws/createAMI.sh b/openvidu-server/deployments/ce/aws/createAMI.sh
index adeb10d7..c65f4af8 100755
--- a/openvidu-server/deployments/ce/aws/createAMI.sh
+++ b/openvidu-server/deployments/ce/aws/createAMI.sh
@@ -1,20 +1,15 @@
#!/bin/bash -x
set -eu -o pipefail
-CF_OVP_TARGET=${CF_OVP_TARGET:-nomarket}
CF_RELEASE=${CF_RELEASE:-false}
+AWS_KEY_NAME=${AWS_KEY_NAME:-}
if [[ $CF_RELEASE == "true" ]]; then
git checkout v$OPENVIDU_VERSION
fi
-if [ ${CF_OVP_TARGET} == "market" ]; then
- export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
- export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
- export AWS_DEFAULT_REGION=us-east-1
-else
- export AWS_DEFAULT_REGION=eu-west-1
-fi
+export AWS_DEFAULT_REGION=eu-west-1
+
DATESTAMP=$(date +%s)
TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
@@ -22,13 +17,13 @@ TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
# Get Latest Ubuntu AMI id from specified region
# Parameters
# $1 Aws region
+
getUbuntuAmiId() {
local AMI_ID=$(
aws --region ${1} ec2 describe-images \
- --filters Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64* \
- --query 'Images[*].[ImageId,CreationDate]' \
- --output text \
- | sort -k2 -r | head -n1 | cut -d$'\t' -f1
+ --filters "Name=name,Values=*ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*" \
+ --query "sort_by(Images, &CreationDate)" \
+ | jq -r 'del(.[] | select(.ImageOwnerAlias != null)) | .[-1].ImageId'
)
echo $AMI_ID
}
@@ -59,6 +54,16 @@ TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-ov-ce-am
# Update installation script
if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
+ # Avoid overriding existing versions
+ # Only master and non existing versions can be overriden
+ if [[ ${OPENVIDU_VERSION} != "master" ]]; then
+ INSTALL_SCRIPT_EXISTS=true
+ aws s3api head-object --bucket aws.openvidu.io --key install_openvidu_$OPENVIDU_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
+ if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
+ echo "Aborting updating s3://aws.openvidu.io/install_openvidu_${OPENVIDU_VERSION}.sh. File actually exists."
+ exit 1
+ fi
+ fi
aws s3 cp ../docker-compose/install_openvidu.sh s3://aws.openvidu.io/install_openvidu_$OPENVIDU_VERSION.sh --acl public-read
fi
@@ -85,11 +90,34 @@ echo "Cleaning up"
aws cloudformation delete-stack --stack-name openvidu-ce-${DATESTAMP}
# Wait for the instance
-aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID}
+# Unfortunately, aws cli does not have a way to increase timeout
+WAIT_RETRIES=0
+WAIT_MAX_RETRIES=3
+until [ "${WAIT_RETRIES}" -ge "${WAIT_MAX_RETRIES}" ]
+do
+ aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID} && break
+ WAIT_RETRIES=$((WAIT_RETRIES+1))
+ sleep 5
+done
# Updating the template
sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" CF-OpenVidu.yaml.template > CF-OpenVidu-${OPENVIDU_VERSION}.yaml
sed -i "s/OPENVIDU_VERSION/${OPENVIDU_VERSION}/g" CF-OpenVidu-${OPENVIDU_VERSION}.yaml
+# Update CF template
+if [[ ${UPDATE_CF} == "true" ]]; then
+ # Avoid overriding existing versions
+ # Only master and non existing versions can be overriden
+ if [[ ${OPENVIDU_VERSION} != "master" ]]; then
+ CF_EXIST=true
+ aws s3api head-object --bucket aws.openvidu.io --key CF-OpenVidu-${OPENVIDU_VERSION}.yaml || CF_EXIST=false
+ if [[ ${CF_EXIST} == "true" ]]; then
+ echo "Aborting updating s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml. File actually exists."
+ exit 1
+ fi
+ fi
+ aws s3 cp CF-OpenVidu-${OPENVIDU_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml --acl public-read
+fi
+
rm $TEMPJSON
rm cfn-mkt-ov-ce-ami.yaml
diff --git a/openvidu-server/deployments/ce/aws/replicate_amis.sh b/openvidu-server/deployments/ce/aws/replicate_amis.sh
index 838da435..f2708121 100755
--- a/openvidu-server/deployments/ce/aws/replicate_amis.sh
+++ b/openvidu-server/deployments/ce/aws/replicate_amis.sh
@@ -5,14 +5,12 @@ set -eu -o pipefail
#
# Input parameters:
#
-# OV_AMI_NAME OpenVidu AMI Name
-# OV_AMI_ID OpenVidu AMI ID
+# OV_AMI_NAME OpenVidu AMI Name
+# OV_AMI_ID OpenVidu AMI ID
+# UPDATE_CF Boolean, true if you want to update CF template by OPENVIDU_VERSION
+# OPENVIDU_VERSION OpenVidu Version of the CF you want to update. It will update CF-OpenVidu-OPENVIDU_VERSION
export AWS_DEFAULT_REGION=eu-west-1
-if [ ${CF_OVP_TARGET} == "market" ]; then
- export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
- export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
-fi
echo "Making original AMI public"
aws ec2 wait image-exists --image-ids ${OV_AMI_ID}
@@ -71,13 +69,54 @@ do
ITER=$(expr $ITER + 1)
done
+# Print and generate replicated AMIS
+REPLICATED_AMIS_FILE="replicated_amis.yaml"
echo "OV IDs"
+{
+ echo "#start_mappings"
+ echo "Mappings:"
+ echo " OVAMIMAP:"
+ ITER=0
+ for i in "${AMI_IDS[@]}"
+ do
+ AMI_ID=${AMI_IDS[$ITER]}
+ REGION=${REGIONS[$ITER]}
+ echo " ${REGION}:"
+ echo " AMI: ${AMI_ID}"
+ ITER=$(expr $ITER + 1)
+ done
+ echo "#end_mappings"
+ echo ""
+} > "${REPLICATED_AMIS_FILE}" 2>&1
+
+# Print replicated AMIs
+cat "${REPLICATED_AMIS_FILE}"
+
+if [[ ${UPDATE_CF} == "true" ]]; then
+ if [[ ! -z ${OPENVIDU_VERSION} ]]; then
+ # Download s3 file
+ aws s3 cp s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml CF-OpenVidu-${OPENVIDU_VERSION}.yaml
+ sed -e "/^#end_mappings/r ${REPLICATED_AMIS_FILE}" -e '/^#start_mappings/,/^#end_mappings/d' -i CF-OpenVidu-${OPENVIDU_VERSION}.yaml
+ aws s3 cp CF-OpenVidu-${OPENVIDU_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-${OPENVIDU_VERSION}.yaml --acl public-read
+ fi
+fi
+
+# Print AMI_LIST for delete_amis.sh
+AMI_LIST=""
ITER=0
for i in "${AMI_IDS[@]}"
do
AMI_ID=${AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
- echo " ${REGION}:"
- echo " AMI: ${AMI_ID}"
+ if [[ ${ITER} -eq 0 ]]; then
+ AMI_LIST="${REGION}:${AMI_ID}"
+ else
+ AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
+ fi
ITER=$(expr $ITER + 1)
-done
\ No newline at end of file
+done
+echo "AMI_LIST: ${AMI_LIST}"
+
+# Cleaning the house
+rm "${REPLICATED_AMIS_FILE}"
+rm CF-OpenVidu-${OPENVIDU_VERSION}.yaml
\ No newline at end of file
diff --git a/openvidu-server/deployments/ce/docker-compose/.env b/openvidu-server/deployments/ce/docker-compose/.env
index fe1e7693..ac5e69ab 100644
--- a/openvidu-server/deployments/ce/docker-compose/.env
+++ b/openvidu-server/deployments/ce/docker-compose/.env
@@ -39,6 +39,22 @@ LETSENCRYPT_EMAIL=user@example.com
# SDKs, REST clients and browsers will have to connect to this port
# HTTPS_PORT=443
+# Old paths are considered now deprecated, but still supported by default.
+# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
+# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
+# to stop allowing the use of old paths.
+# Default value is true
+# SUPPORT_DEPRECATED_API=true
+
+# If true request to with www will be redirected to non-www requests
+# Default value is false
+# REDIRECT_WWW=false
+
+# How many workers to configure in nginx proxy.
+# The more workers, the more requests will be handled
+# Default value is 10240
+# WORKER_CONNECTIONS=10240
+
# Access restrictions
# In this section you will be able to restrict the IPs from which you can access to
# Openvidu API and the Administration Panel
@@ -110,6 +126,15 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
+# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
+# when a codec can not be forced, transcoding will be allowed
+# Default value is VP8
+# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
+
+# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
+# Default value is false
+# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
+
# true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false
OPENVIDU_WEBHOOK=false
@@ -138,10 +163,10 @@ OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true
-# when a codec can not be forced, transcoding will be allowed
+# when a codec can not be forced, transcoding will be allowed
# OPENVIDU_FORCED_CODEC=VP8
-# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
+# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
# OPENVIDU_ALLOW_TRANSCODING=false
# Call Detail Record enabled
@@ -154,16 +179,16 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr
# Kurento Media Server image
# --------------------------
-# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server-dev
+# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
# Uncomment the next line and define this variable with KMS image that you want use
-# KMS_IMAGE=kurento/kurento-media-server:6.14.0
+# KMS_IMAGE=kurento/kurento-media-server:6.15.0
# Kurento Media Server Level logs
# -------------------------------
# Uncomment the next line and define this variable to change
# the verbosity level of the logs of KMS
# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
-# KMS_DEBUG_LEVEL=3,Kurento*:4,kms*:4,sdp*:4,webrtc*:4,*rtpendpoint:4,rtp*handler:4,rtpsynchronizer:4,agnosticbin:4
+# KMS_DOCKER_ENV_GST_DEBUG=
# Openvidu Server Level logs
# --------------------------
diff --git a/openvidu-server/deployments/ce/docker-compose/docker-compose.override.yml b/openvidu-server/deployments/ce/docker-compose/docker-compose.override.yml
index c52129f9..d4796d20 100644
--- a/openvidu-server/deployments/ce/docker-compose/docker-compose.override.yml
+++ b/openvidu-server/deployments/ce/docker-compose/docker-compose.override.yml
@@ -9,11 +9,11 @@ services:
#
# Default Application
#
- # Openvidu-Call Version: 2.15.0
+ # Openvidu-Call Version: 2.16.0
#
# --------------------------------------------------------------
app:
- image: openvidu/openvidu-call:2.15.0
+ image: openvidu/openvidu-call:2.16.0
restart: on-failure
network_mode: host
environment:
@@ -21,3 +21,6 @@ services:
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/ce/docker-compose/docker-compose.yml b/openvidu-server/deployments/ce/docker-compose/docker-compose.yml
index 299b62d2..452d0968 100644
--- a/openvidu-server/deployments/ce/docker-compose/docker-compose.yml
+++ b/openvidu-server/deployments/ce/docker-compose/docker-compose.yml
@@ -7,11 +7,11 @@
# Application based on OpenVidu should be specified in
# docker-compose.override.yml file
#
-# This docker-compose file coordinates all services of OpenVidu CE Platform.
+# This docker-compose file coordinates all services of OpenVidu CE Platform
#
# This file will be overridden when update OpenVidu Platform
#
-# Openvidu Version: 2.15.0
+# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@@ -22,10 +22,10 @@ version: '3.1'
services:
openvidu-server:
- image: openvidu/openvidu-server:2.15.0
+ image: openvidu/openvidu-server:2.17.0-dev2
restart: on-failure
network_mode: host
- entrypoint: ['/bin/bash', '-c', 'export COTURN_IP=`/usr/local/bin/discover_my_public_ip.sh`; /usr/local/bin/entrypoint.sh']
+ entrypoint: ['/usr/local/bin/entrypoint.sh']
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
@@ -39,9 +39,13 @@ services:
- KMS_URIS=["ws://localhost:8888/kurento"]
- COTURN_REDIS_IP=127.0.0.1
- COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
+ - COTURN_IP=${COTURN_IP:-auto-ipv4}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
kms:
- image: ${KMS_IMAGE:-kurento/kurento-media-server:6.14.0}
+ image: ${KMS_IMAGE:-kurento/kurento-media-server:6.15.0}
restart: always
network_mode: host
ulimits:
@@ -49,20 +53,29 @@ services:
volumes:
- /opt/openvidu/kms-crashes:/opt/openvidu/kms-crashes
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
+ - /opt/openvidu/kurento-logs:/opt/openvidu/kurento-logs
environment:
- KMS_MIN_PORT=40000
- KMS_MAX_PORT=57000
- - GST_DEBUG=${KMS_DEBUG_LEVEL:-}
+ - GST_DEBUG=${KMS_DOCKER_ENV_GST_DEBUG:-}
+ - KURENTO_LOG_FILE_SIZE=${KMS_DOCKER_ENV_KURENTO_LOG_FILE_SIZE:-100}
+ - KURENTO_LOGS_PATH=/opt/openvidu/kurento-logs
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
redis:
- image: openvidu/openvidu-redis:1.0.0
+ image: openvidu/openvidu-redis:2.0.0-dev2
restart: always
network_mode: host
environment:
- REDIS_PASSWORD=${OPENVIDU_SECRET}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
coturn:
- image: openvidu/openvidu-coturn:1.0.0
+ image: openvidu/openvidu-coturn:3.0.0-dev2
restart: on-failure
network_mode: host
environment:
@@ -72,14 +85,19 @@ services:
- DB_PASSWORD=${OPENVIDU_SECRET}
- MIN_PORT=57001
- MAX_PORT=65535
+ - ENABLE_COTURN_LOGS=true
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
nginx:
- image: openvidu/openvidu-proxy:3.0.0
+ image: openvidu/openvidu-proxy:5.0.0-dev2
restart: on-failure
network_mode: host
volumes:
- ./certificates:/etc/letsencrypt
- ./owncert:/owncert
+ - ./custom-nginx-vhosts:/etc/nginx/vhost.d/
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:/opt/openvidu/custom-layout
environment:
- DOMAIN_OR_PUBLIC_IP=${DOMAIN_OR_PUBLIC_IP}
@@ -91,3 +109,10 @@ services:
- ALLOWED_ACCESS_TO_RESTAPI=${ALLOWED_ACCESS_TO_RESTAPI:-}
- PROXY_MODE=CE
- WITH_APP=true
+ - SUPPORT_DEPRECATED_API=${SUPPORT_DEPRECATED_API:-true}
+ - REDIRECT_WWW=${REDIRECT_WWW:-false}
+ - WORKER_CONNECTIONS=${WORKER_CONNECTIONS:-10240}
+ - PUBLIC_IP=${PROXY_PUBLIC_IP:-auto-ipv4}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/ce/docker-compose/install_openvidu.sh b/openvidu-server/deployments/ce/docker-compose/install_openvidu.sh
index 634d104d..48a18790 100755
--- a/openvidu-server/deployments/ce/docker-compose/install_openvidu.sh
+++ b/openvidu-server/deployments/ce/docker-compose/install_openvidu.sh
@@ -3,6 +3,7 @@
# Global variables
OPENVIDU_FOLDER=openvidu
OPENVIDU_VERSION=master
+OPENVIDU_UPGRADABLE_VERSION="2.16"
DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${OPENVIDU_VERSION}
fatal_error() {
@@ -42,10 +43,6 @@ new_ov_installation() {
--output "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/ce/docker-compose/readme.md \
- --output "${OPENVIDU_FOLDER}/readme.md" || fatal_error "Error when downloading the file 'readme.md'"
- printf '\n - readme.md'
-
# Add execution permissions
printf "\n => Adding permission to 'openvidu' program..."
chmod +x "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
@@ -54,6 +51,10 @@ new_ov_installation() {
printf "\n => Creating folder 'owncert'..."
mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
+ # Create vhost nginx folder
+ printf "\n => Creating folder 'custom-nginx-vhosts'..."
+ mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
+
# Ready to use
printf '\n'
printf '\n'
@@ -105,10 +106,13 @@ upgrade_ov() {
# Uppgrade Openvidu
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
- [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.13.0
+ [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
+ if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
+ fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
+ fi
printf '\n'
printf '\n ======================================='
@@ -146,10 +150,6 @@ upgrade_ov() {
--output "${TMP_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
printf '\n - openvidu'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/ce/docker-compose/readme.md \
- --output "${TMP_FOLDER}/readme.md" || fatal_error "Error when downloading the file 'readme.md'"
- printf '\n - readme.md'
-
# Dowloading new images and stoped actual Openvidu
printf '\n => Dowloading new images...'
printf '\n'
@@ -187,12 +187,14 @@ upgrade_ov() {
mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - openvidu'
- mv "${OPENVIDU_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
- printf '\n - readme.md'
-
cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
+ if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
+ printf '\n - custom-nginx-vhosts'
+ fi
+
# Move tmp files to Openvidu
printf '\n => Updating files:'
@@ -213,9 +215,6 @@ upgrade_ov() {
mv "${TMP_FOLDER}/openvidu" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'openvidu'"
printf '\n - openvidu'
- mv "${TMP_FOLDER}/readme.md" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'readme.md'"
- printf '\n - readme.md'
-
printf "\n => Deleting 'tmp' folder"
rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
@@ -249,7 +248,9 @@ upgrade_ov() {
printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
- printf '\n For more information, check readme.md'
+ printf '\n For more information, check:'
+ printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/deployment/deploying-on-premises/"
+ printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/deployment/upgrading/"
printf '\n'
printf '\n'
}
diff --git a/openvidu-server/deployments/ce/docker-compose/openvidu b/openvidu-server/deployments/ce/docker-compose/openvidu
index 64a67c36..3e607918 100755
--- a/openvidu-server/deployments/ce/docker-compose/openvidu
+++ b/openvidu-server/deployments/ce/docker-compose/openvidu
@@ -159,6 +159,16 @@ generate_report() {
printf '\n'
done
+ printf '\n'
+ printf "\n ---------------------------------------"
+ printf "\n KMS"
+ printf "\n ---------------------------------------"
+ printf '\n'
+ kurento_logs
+ printf "\n ---------------------------------------"
+ printf '\n'
+ printf '\n'
+
printf "\n ======================================="
printf "\n = CONTAINER ENVS VARIABLES ="
printf "\n ======================================="
@@ -189,7 +199,8 @@ usage() {
printf "\n\tstart\t\t\tStart all services"
printf "\n\tstop\t\t\tStop all services"
printf "\n\trestart\t\t\tRestart all stoped and running services"
- printf "\n\tlogs\t\t\tShow openvidu-server logs"
+ printf "\n\tlogs [-f]\t\tShow openvidu logs."
+ printf "\n\tkms-logs [-f]\t\tShow kms logs"
printf "\n\tupgrade\t\t\tUpgrade to the lastest Openvidu version"
printf "\n\tupgrade [version]\tUpgrade to the specific Openvidu version"
printf "\n\tversion\t\t\tShow version of Openvidu Server"
@@ -198,6 +209,14 @@ usage() {
printf "\n"
}
+kurento_logs() {
+ if [[ "$1" == "-f" ]]; then
+ tail -f /opt/openvidu/kurento-logs/*.log
+ else
+ cat /opt/openvidu/kurento-logs/*.log
+ fi
+}
+
case $1 in
start)
docker-compose up -d
@@ -215,7 +234,18 @@ case $1 in
;;
logs)
- docker-compose logs -f openvidu-server
+ case $2 in
+ "-f")
+ docker-compose logs -f openvidu-server
+ ;;
+ *)
+ docker-compose logs openvidu-server
+ ;;
+ esac
+ ;;
+
+ kms-logs)
+ kurento_logs $2
;;
upgrade)
diff --git a/openvidu-server/deployments/ce/taskcat/taskcat.yml b/openvidu-server/deployments/ce/taskcat/taskcat.yml
index 67169614..f43a41f7 100644
--- a/openvidu-server/deployments/ce/taskcat/taskcat.yml
+++ b/openvidu-server/deployments/ce/taskcat/taskcat.yml
@@ -1,6 +1,6 @@
---
global:
- owner: openvidu@gmail.com
+ owner: info@openvidu.io
qsname: openvidu-ce
regions:
- us-east-1
diff --git a/openvidu-server/deployments/pro/aws/cfn-mkt-kms-ami.yaml.template b/openvidu-server/deployments/pro/aws/cfn-mkt-kms-ami.yaml.template
index 8cfb9169..d1ee758d 100644
--- a/openvidu-server/deployments/pro/aws/cfn-mkt-kms-ami.yaml.template
+++ b/openvidu-server/deployments/pro/aws/cfn-mkt-kms-ami.yaml.template
@@ -142,5 +142,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
- Timeout: PT10M
+ Timeout: PT20M
Count: 1
diff --git a/openvidu-server/deployments/pro/aws/cfn-mkt-ov-ami.yaml.template b/openvidu-server/deployments/pro/aws/cfn-mkt-ov-ami.yaml.template
index e2a5fda5..d27c1e1b 100644
--- a/openvidu-server/deployments/pro/aws/cfn-mkt-ov-ami.yaml.template
+++ b/openvidu-server/deployments/pro/aws/cfn-mkt-ov-ami.yaml.template
@@ -104,6 +104,12 @@ Resources:
Tags:
- Key: Name
Value: !Ref AWS::StackName
+ BlockDeviceMappings:
+ - DeviceName: /dev/sda1
+ Ebs:
+ VolumeType: gp2
+ DeleteOnTermination: true
+ VolumeSize: 10
UserData:
"Fn::Base64":
!Sub |
@@ -138,5 +144,5 @@ Resources:
Type: AWS::CloudFormation::WaitCondition
CreationPolicy:
ResourceSignal:
- Timeout: PT10M
+ Timeout: PT20M
Count: 1
diff --git a/openvidu-server/deployments/pro/aws/cfn-openvidu-server-pro-no-market.yaml.template b/openvidu-server/deployments/pro/aws/cfn-openvidu-server-pro-no-market.yaml.template
index 6d60034f..939fb6cd 100644
--- a/openvidu-server/deployments/pro/aws/cfn-openvidu-server-pro-no-market.yaml.template
+++ b/openvidu-server/deployments/pro/aws/cfn-openvidu-server-pro-no-market.yaml.template
@@ -41,6 +41,22 @@ Parameters:
LetsEncryptEmail:
Description: "If certificate type is 'letsencrypt', this email will be used for Let's Encrypt notifications"
Type: String
+
+ Recording:
+ Description: |
+ If 'disabled', recordings will not be active.
+ If 'local' recordings will be saved in EC2 instance locally.
+ If 's3', recordings will be stored in a S3 bucket"
+ Type: String
+ AllowedValues:
+ - disabled
+ - local
+ - s3
+ Default: local
+
+ S3RecordingsBucketName:
+ Description: "S3 Bucket Name"
+ Type: String
# OpenVidu Configuration
@@ -52,39 +68,51 @@ Parameters:
ConstraintDescription: OpenVidu Pro License is mandatory
OpenViduSecret:
- Description: "Secret to connect to this OpenVidu Platform. No whitespaces or quotations allowed"
+ Description: "Secret to connect to this OpenVidu Platform. Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
Type: String
- AllowedPattern: ^((?!")(?! ).)+$
+ AllowedPattern: ^[a-zA-Z0-9_-]+$
NoEcho: true
- ConstraintDescription: OpenVidu Secret is mandatory (no whitespaces or quotations allowed)
+ ConstraintDescription: "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ('-') and underscores ('_')"
MediaNodesStartNumber:
Description: "How many Media Nodes do you want on startup (EC2 instances will be launched)"
Type: Number
Default: 1
- # Kibana configuration
-
- KibanaUser:
- Description: "Username for Kibana Dashboard"
+ # Elasticsearch configuration
+ ElasticsearchUser:
+ Description: "Username for Elasticsearch and Kibana"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
- ConstraintDescription: Kibana user is mandatory (no whitespaces or quotations allowed)
- Default: kibanaadmin
+ ConstraintDescription: Elasticsearch user is mandatory (no whitespaces or quotations allowed)
+ Default: elasticadmin
- KibanaPassword:
- Description: "Password for Kibana Dashboard"
+ ElasticsearchPassword:
+ Description: "Password for Elasticsearch and Kibana"
Type: String
AllowedPattern: ^((?!")(?! ).)+$
NoEcho: true
- ConstraintDescription: Kibana password is mandatory (no whitespaces or quotations allowed)
+ ConstraintDescription: Elasticsearch password is mandatory (no whitespaces or quotations allowed)
+
+ # Elasticsearch configuration
+ ElasticsearchUrl:
+ Description: "If you have an external Elasticsearch service running, put here the url to the service. If empty, an Elasticsearch service will be deployed next to OpenVidu."
+ Type: String
+ AllowedPattern: (^(http|https):\/\/.*:[1-9]{1,5}+.*$|^$)
+ ConstraintDescription: "It is very important to specify the Elasticsearch URL with the port used by this service. For example: https://es-example:443"
+
+ KibanaUrl:
+ Description: "If you have an external Kibana service running, put here the url to the service. If empty, a Kibana service will be deployed next to OpenVidu."
+ Type: String
+ AllowedPattern: (^(http|https):\/\/.*:[1-9]{1,5}+.*$|^$)
+ ConstraintDescription: "It is very important to specify the url with port used by this service. For example: https://kibana-example:443"
# EC2 Instance configuration
AwsInstanceTypeOV:
Description: "Specifies the EC2 instance type for your OpenVidu Server Pro Node"
Type: String
- Default: t2.xlarge
+ Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@@ -124,7 +152,7 @@ Parameters:
AwsInstanceTypeKMS:
Description: "Specifies the EC2 instance type for your Media Nodes"
Type: String
- Default: t2.xlarge
+ Default: c5.xlarge
AllowedValues:
- t2.large
- t2.xlarge
@@ -186,6 +214,7 @@ Parameters:
- false
Default: true
+#start_mappings
Mappings:
OVAMIMAP:
eu-west-1:
@@ -194,6 +223,7 @@ Mappings:
KMSAMIMAP:
eu-west-1:
AMI: KMS_AMI_ID
+#end_mappings
Metadata:
'AWS::CloudFormation::Interface':
@@ -214,10 +244,17 @@ Metadata:
- OpenViduSecret
- MediaNodesStartNumber
- Label:
- default: Kibana configuration
+ default: OpenVidu Recording Configuration
Parameters:
- - KibanaUser
- - KibanaPassword
+ - Recording
+ - S3RecordingsBucketName
+ - Label:
+ default: Elasticsearch and Kibana configuration
+ Parameters:
+ - ElasticsearchUrl
+ - KibanaUrl
+ - ElasticsearchUser
+ - ElasticsearchPassword
- Label:
default: EC2 Instance configuration
Parameters:
@@ -248,6 +285,10 @@ Metadata:
default: "URL to the key file (owncert)"
LetsEncryptEmail:
default: "Email for Let's Encrypt (letsencrypt)"
+ Recording:
+ default: "OpenVidu Recording"
+ S3RecordingsBucketName:
+ default: "S3 Bucket where recordings will be stored"
# OpenVidu configuration
OpenViduLicense:
default: "OpenVidu Pro License key"
@@ -256,10 +297,14 @@ Metadata:
OpenViduSecret:
default: "Openvidu Secret"
# Kibana configuration
- KibanaUser:
- default: "Kibana username"
- KibanaPassword:
- default: "Kibana password"
+ ElasticsearchUrl:
+ default: "Elasticsearch URL"
+ KibanaUrl:
+ default: "Kibana URL"
+ ElasticsearchUser:
+ default: "Elasticsearch and Kibana username"
+ ElasticsearchPassword:
+ default: "Elasticsearch and Kibana password"
# EC2 instance configuration
AwsInstanceTypeOV:
default: "Instance type for Openvidu Server Pro Node"
@@ -277,8 +322,22 @@ Metadata:
default: "Deploy OpenVidu Call application"
Conditions:
- WhichCertPresent: !Not [ !Equals [!Ref WhichCert, ""] ]
- PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ]
+ WhichCertPresent: !Not [ !Equals [!Ref WhichCert, ''] ]
+ PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ''] ]
+ RecordingStorageIsS3: !Equals [ !Ref Recording, 's3' ]
+ CreateS3Bucket: !And
+ - !Equals [!Ref Recording, 's3' ]
+ - !Equals [!Ref S3RecordingsBucketName, '']
+
+Rules:
+
+ RecordingValidation:
+ RuleCondition:
+ Fn::Or: [ !Equals [!Ref Recording, 'disabled' ], !Equals [!Ref Recording, 'local' ] ]
+ Assertions:
+ - AssertDescription: If recording Storage is 'disabled' or 'local', you don't need to specify a S3 bucket.
+ Assert:
+ Fn::Equals: [ !Ref S3RecordingsBucketName, "" ]
Resources:
@@ -313,6 +372,45 @@ Resources:
- 'route53:ChangeResourceRecordSets'
- 'route53:ListHostedZones'
Resource: '*'
+ - Fn::If:
+ # Only apply this policy if S3 is configured
+ - RecordingStorageIsS3
+ - Effect: Allow
+ Action:
+ - 's3:DeleteObject'
+ - 's3:GetObject'
+ - 's3:PutObject'
+ Resource:
+ - Fn::If:
+ # Get bucket name depending if the user defines a bucket name or not
+ - CreateS3Bucket
+ ### Unique bucket name using Stack ID
+ - !Join [ "", [ 'arn:aws:s3:::', 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]], "/*" ]]
+ - !Join [ "", [ 'arn:aws:s3:::', !Ref S3RecordingsBucketName, '/*'] ]
+ - Ref: AWS::NoValue
+ - Fn::If:
+ # Only apply this policy if S3 is configured
+ - RecordingStorageIsS3
+ - Effect: Allow
+ Action:
+ - 's3:ListBucket'
+ - 's3:GetBucketLocation'
+ Resource:
+ - Fn::If:
+ # Get bucket name depending if the user defines a bucket name or not
+ - CreateS3Bucket
+ ### Unique bucket name using Stack ID
+ - !Join [ "", [ 'arn:aws:s3:::', 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
+ - !Join [ "", [ 'arn:aws:s3:::', !Ref S3RecordingsBucketName ] ]
+ - Ref: AWS::NoValue
+ - Fn::If:
+ # Only apply this policy if S3 is configured
+ - RecordingStorageIsS3
+ - Effect: Allow
+ Action:
+ - s3:ListAllMyBuckets
+ Resource: 'arn:aws:s3:::'
+ - Ref: AWS::NoValue
RoleName: !Join [ "-", [ OpenViduManageEC2Role, !Ref 'AWS::StackName', !Ref 'AWS::Region'] ]
OpenviduInstancesProfile:
@@ -325,6 +423,21 @@ Resources:
DependsOn:
- OpenViduManageEC2Role
+ S3bucket:
+ Type: 'AWS::S3::Bucket'
+ Properties:
+ ### Unique bucket name using Stack ID
+ BucketName: !Join ["-" , [ 'openvidu-recordings', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
+ AccessControl: Private
+ PublicAccessBlockConfiguration:
+ BlockPublicAcls: true
+ BlockPublicPolicy: true
+ IgnorePublicAcls : true
+ RestrictPublicBuckets: true
+ DeletionPolicy: Retain
+ UpdateReplacePolicy: Retain
+ Condition: CreateS3Bucket
+
OpenViduServer:
Type: AWS::EC2::Instance
Metadata:
@@ -383,9 +496,15 @@ Resources:
sed -i "s/CERTIFICATE_TYPE=selfsigned/CERTIFICATE_TYPE=${WhichCert}/" $WORKINGDIR/.env
sed -i "s/LETSENCRYPT_EMAIL=user@example.com/LETSENCRYPT_EMAIL=${LetsEncryptEmail}/" $WORKINGDIR/.env
- # Replace Kibana Conf
- sed -i "s/KIBANA_USER=kibanaadmin/KIBANA_USER=${KibanaUser}/" $WORKINGDIR/.env
- sed -i "s/KIBANA_PASSWORD=/KIBANA_PASSWORD=${KibanaPassword}/" $WORKINGDIR/.env
+ # Replace Elastic Search Conf
+ if [[ ! -z "${ElasticsearchUrl}" ]]; then
+ sed -i "s,#OPENVIDU_PRO_ELASTICSEARCH_HOST=,OPENVIDU_PRO_ELASTICSEARCH_HOST=${ElasticsearchUrl}," $WORKINGDIR/.env
+ fi
+ if [[ ! -z "${KibanaUrl}" ]]; then
+ sed -i "s,#OPENVIDU_PRO_KIBANA_HOST=,OPENVIDU_PRO_KIBANA_HOST=${KibanaUrl}," $WORKINGDIR/.env
+ fi
+ sed -i "s/ELASTICSEARCH_USERNAME=elasticadmin/ELASTICSEARCH_USERNAME=${ElasticsearchUser}/" $WORKINGDIR/.env
+ sed -i "s/ELASTICSEARCH_PASSWORD=/ELASTICSEARCH_PASSWORD=${ElasticsearchPassword}/" $WORKINGDIR/.env
# Replace vars AWS
sed -i "s/#AWS_DEFAULT_REGION=/AWS_DEFAULT_REGION=${AWS::Region}/" $WORKINGDIR/.env
@@ -395,6 +514,8 @@ Resources:
sed -i "s/#AWS_SUBNET_ID=/AWS_SUBNET_ID=${OpenViduSubnet}/" $WORKINGDIR/.env
sed -i "s/#AWS_STACK_ID=/AWS_STACK_ID=$(echo ${AWS::StackId} | sed 's#/#\\/#g')/" $WORKINGDIR/.env
sed -i "s/#AWS_STACK_NAME=/AWS_STACK_NAME=${AWS::StackName}/" $WORKINGDIR/.env
+ sed -i "s/#AWS_CLI_DOCKER_TAG=/AWS_CLI_DOCKER_TAG=AWS_DOCKER_TAG/" $WORKINGDIR/.env
+ sed -i "s/#AWS_VOLUME_SIZE=/AWS_VOLUME_SIZE=50/" $WORKINGDIR/.env
# Get security group id of kms and use it as env variable
SECGRPIDKMS=$(/usr/local/bin/getSecurityGroupKms.sh)
@@ -405,7 +526,20 @@ Resources:
sed -i "s/WITH_APP=true/WITH_APP=false/" $WORKINGDIR/docker-compose.yml
rm $WORKINGDIR/docker-compose.override.yml
fi
+
+ # Recording Configuration
+ if [ "${Recording}" != "disabled" ]; then
+ sed -i "s/OPENVIDU_RECORDING=false/OPENVIDU_RECORDING=true/" $WORKINGDIR/.env
+ sed -i "s/#OPENVIDU_PRO_RECORDING_STORAGE=/OPENVIDU_PRO_RECORDING_STORAGE=${Recording}/" $WORKINGDIR/.env
+ if [ ! -z "${S3RecordingsBucketName}" ]; then
+ sed -i "s/#OPENVIDU_PRO_AWS_S3_BUCKET=/OPENVIDU_PRO_AWS_S3_BUCKET=${S3RecordingsBucketName}/" $WORKINGDIR/.env
+ elif [ "${Recording}" == "s3" ]; then
+ sed -i "s/#OPENVIDU_PRO_AWS_S3_BUCKET=/OPENVIDU_PRO_AWS_S3_BUCKET=${s3BucketName}/" $WORKINGDIR/.env
+ fi
+ fi
- kmsAmi: !FindInMap [KMSAMIMAP, !Ref 'AWS::Region', AMI]
+ ### Unique bucket name using Stack ID
+ s3BucketName: !Join ["" , [ 'openvidu-recordings-', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
mode: "000755"
owner: "root"
group: "root"
@@ -421,7 +555,7 @@ Resources:
'/usr/local/bin/getSecurityGroupKms.sh':
content: !Sub |
#!/bin/bash -x
- docker run --rm amazon/aws-cli:2.0.7 ec2 describe-security-groups \
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-security-groups \
--region ${AWS::Region} \
--output text \
--filters "Name=tag:aws:cloudformation:logical-id,Values=KMSSecurityGroup" \
@@ -433,7 +567,7 @@ Resources:
'/usr/local/bin/getSecurityGroupOpenVidu.sh':
content: !Sub |
#!/bin/bash -x
- docker run --rm amazon/aws-cli:2.0.7 ec2 describe-security-groups \
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-security-groups \
--region ${AWS::Region} \
--output text \
--filters "Name=tag:aws:cloudformation:logical-id,Values=OpenViduSecurityGroup" \
@@ -445,7 +579,7 @@ Resources:
'/usr/local/bin/getCidrBlocKSubnet.sh':
content: !Sub |
#!/bin/bash -x
- docker run --rm amazon/aws-cli:2.0.7 ec2 describe-subnets \
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 describe-subnets \
--region ${AWS::Region} \
--output text \
--filters "Name=subnet-id,Values=${OpenViduSubnet}" \
@@ -454,25 +588,25 @@ Resources:
owner: "root"
group: "root"
'/usr/local/bin/create_security_group_rules.sh':
- content: !Sub |
+ content: |
#!/bin/bash -x
SECGRPIDKMS=$(/usr/local/bin/getSecurityGroupKms.sh)
SECGRPIDOV=$(/usr/local/bin/getSecurityGroupOpenVidu.sh)
SUBNET_CIDR=$(/usr/local/bin/getCidrBlocKSubnet.sh)
# Create Security group rules OpenVidu
- docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 5044 --cidr $SUBNET_CIDR
- docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 9200 --cidr $SUBNET_CIDR
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 5044 --cidr $SUBNET_CIDR
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDOV --protocol tcp --port 9200 --cidr $SUBNET_CIDR
# Create security group rules for KMS
- docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 8888 --cidr $SUBNET_CIDR
- docker run --rm amazon/aws-cli:2.0.7 ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 3000 --cidr $SUBNET_CIDR
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 8888 --cidr $SUBNET_CIDR
+ docker run --rm amazon/aws-cli:AWS_DOCKER_TAG ec2 authorize-security-group-ingress --group-id $SECGRPIDKMS --protocol tcp --port 3000 --cidr $SUBNET_CIDR
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/restartPRO.sh':
- content: !Sub |
+ content: |
#!/bin/bash -x
WORKINGDIR=/opt/openvidu
@@ -484,7 +618,8 @@ Resources:
# Restart all services
pushd /opt/openvidu
- docker-compose up -d
+ export FOLLOW_OPENVIDU_LOGS=false
+ ./openvidu start
popd
mode: "000755"
owner: "root"
@@ -509,6 +644,8 @@ Resources:
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource OpenViduServer
+ export HOME="/root"
+
# Replace .env variables
/usr/local/bin/feedGroupVars.sh || { echo "[Openvidu] Parameters incorrect/insufficient"; exit 1; }
@@ -525,7 +662,8 @@ Resources:
# Start openvidu application
pushd /opt/openvidu
- docker-compose up -d
+ export FOLLOW_OPENVIDU_LOGS=false
+ ./openvidu start
popd
# Wait for the app
@@ -537,10 +675,9 @@ Resources:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
- VolumeType: io1
- Iops: 200
+ VolumeType: gp2
DeleteOnTermination: true
- VolumeSize: 50
+ VolumeSize: 200
KMSSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
diff --git a/openvidu-server/deployments/pro/aws/createAMIs.sh b/openvidu-server/deployments/pro/aws/createAMIs.sh
index 09e7bc84..81e06087 100755
--- a/openvidu-server/deployments/pro/aws/createAMIs.sh
+++ b/openvidu-server/deployments/pro/aws/createAMIs.sh
@@ -1,18 +1,14 @@
#!/bin/bash -x
+set -eu -o pipefail
-CF_OVP_TARGET=${CF_OVP_TARGET:-nomarket}
+CF_RELEASE=${CF_RELEASE:-false}
+AWS_KEY_NAME=${AWS_KEY_NAME:-}
-if [ ${CF_OVP_TARGET} == "market" ]; then
- export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
- export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
- export AWS_DEFAULT_REGION=us-east-1
-else
- export AWS_DEFAULT_REGION=eu-west-1
+if [[ $CF_RELEASE == "true" ]]; then
+ git checkout v$OPENVIDU_PRO_VERSION
fi
-if [ "${OPENVIDU_PRO_IS_SNAPSHOT}" == "true" ]; then
- OPENVIDU_PRO_VERSION=${OPENVIDU_PRO_VERSION}-SNAPSHOT
-fi
+export AWS_DEFAULT_REGION=eu-west-1
DATESTAMP=$(date +%s)
TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
@@ -23,10 +19,9 @@ TEMPJSON=$(mktemp -t cloudformation-XXX --suffix .json)
getUbuntuAmiId() {
local AMI_ID=$(
aws --region ${1} ec2 describe-images \
- --filters Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64* \
- --query 'Images[*].[ImageId,CreationDate]' \
- --output text \
- | sort -k2 -r | head -n1 | cut -d$'\t' -f1
+ --filters "Name=name,Values=*ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*" \
+ --query "sort_by(Images, &CreationDate)" \
+ | jq -r 'del(.[] | select(.ImageOwnerAlias != null)) | .[-1].ImageId'
)
echo $AMI_ID
}
@@ -61,12 +56,22 @@ sed -i "s/AMIUSEAST1/${AMIUSEAST1}/g" cfn-mkt-kms-ami.yaml
## KMS AMI
# Copy template to S3
-if [ ${CF_OVP_TARGET} == "market" ]; then
- aws s3 cp cfn-mkt-kms-ami.yaml s3://naeva-openvidu-pro
- TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/naeva-openvidu-pro/cfn-mkt-kms-ami.yaml
-else
- aws s3 cp cfn-mkt-kms-ami.yaml s3://aws.openvidu.io
- TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-kms-ami.yaml
+aws s3 cp cfn-mkt-kms-ami.yaml s3://aws.openvidu.io
+TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-kms-ami.yaml
+
+# Update installation script
+if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
+ # Avoid overriding existing versions
+ # Only master and non existing versions can be overriden
+ if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
+ INSTALL_SCRIPT_EXISTS=true
+ aws s3api head-object --bucket aws.openvidu.io --key install_media_node_$OPENVIDU_PRO_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
+ if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
+ echo "Aborting updating s3://aws.openvidu.io/install_media_node_${OPENVIDU_PRO_VERSION}.sh. File actually exists."
+ exit 1
+ fi
+ fi
+ aws s3 cp ../docker-compose/media-node/install_media_node.sh s3://aws.openvidu.io/install_media_node_$OPENVIDU_PRO_VERSION.sh --acl public-read
fi
aws cloudformation create-stack \
@@ -94,12 +99,22 @@ aws cloudformation delete-stack --stack-name kms-${DATESTAMP}
## OpenVidu AMI
# Copy template to S3
-if [ ${CF_OVP_TARGET} == "market" ]; then
- aws s3 cp cfn-mkt-ov-ami.yaml s3://naeva-openvidu-pro
- TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/naeva-openvidu-pro/cfn-mkt-ov-ami.yaml
-else
aws s3 cp cfn-mkt-ov-ami.yaml s3://aws.openvidu.io
TEMPLATE_URL=https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/cfn-mkt-ov-ami.yaml
+
+# Update installation script
+if [[ ${UPDATE_INSTALLATION_SCRIPT} == "true" ]]; then
+ # Avoid overriding existing versions
+ # Only master and non existing versions can be overriden
+ if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
+ INSTALL_SCRIPT_EXISTS=true
+ aws s3api head-object --bucket aws.openvidu.io --key install_openvidu_pro_$OPENVIDU_PRO_VERSION.sh || INSTALL_SCRIPT_EXISTS=false
+ if [[ ${INSTALL_SCRIPT_EXISTS} == "true" ]]; then
+ echo "Aborting updating s3://aws.openvidu.io/install_openvidu_pro_${OPENVIDU_PRO_VERSION}.sh. File actually exists."
+ exit 1
+ fi
+ fi
+ aws s3 cp ../docker-compose/openvidu-server-pro/install_openvidu_pro.sh s3://aws.openvidu.io/install_openvidu_pro_$OPENVIDU_PRO_VERSION.sh --acl public-read
fi
aws cloudformation create-stack \
@@ -125,15 +140,35 @@ echo "Cleaning up"
aws cloudformation delete-stack --stack-name openvidu-${DATESTAMP}
# Wait for the instance
-aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID}
+# Unfortunately, aws cli does not have a way to increase timeout
+WAIT_RETRIES=0
+WAIT_MAX_RETRIES=3
+until [ "${WAIT_RETRIES}" -ge "${WAIT_MAX_RETRIES}" ]
+do
+ aws ec2 wait image-available --image-ids ${OV_RAW_AMI_ID} && break
+ WAIT_RETRIES=$((WAIT_RETRIES+1))
+ sleep 5
+done
+
# Updating the template
-if [ ${CF_OVP_TARGET} == "market" ]; then
- sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-mkt-openvidu-server-pro.yaml.template > cfn-mkt-openvidu-server-pro-${OPENVIDU_PRO_VERSION}.yaml
- sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-mkt-openvidu-server-pro-${OPENVIDU_PRO_VERSION}.yaml
-else
- sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-openvidu-server-pro-no-market.yaml.template > cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
- sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
+sed "s/OV_AMI_ID/${OV_RAW_AMI_ID}/" cfn-openvidu-server-pro-no-market.yaml.template > cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
+sed -i "s/KMS_AMI_ID/${KMS_RAW_AMI_ID}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
+sed -i "s/AWS_DOCKER_TAG/${AWS_DOCKER_TAG}/g" cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml
+
+# Update CF template
+if [[ ${UPDATE_CF} == "true" ]]; then
+ # Avoid overriding existing versions
+ # Only master and non existing versions can be overriden
+ if [[ ${OPENVIDU_PRO_VERSION} != "master" ]]; then
+ CF_EXIST=true
+ aws s3api head-object --bucket aws.openvidu.io --key CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml || CF_EXIST=false
+ if [[ ${CF_EXIST} == "true" ]]; then
+ echo "Aborting updating s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml. File actually exists."
+ exit 1
+ fi
+ fi
+ aws s3 cp cfn-openvidu-server-pro-no-market-${OPENVIDU_PRO_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml --acl public-read
fi
rm $TEMPJSON
diff --git a/openvidu-server/deployments/pro/aws/delete_amis.sh b/openvidu-server/deployments/pro/aws/delete_amis.sh
index 123de55f..dbfd3a1c 100755
--- a/openvidu-server/deployments/pro/aws/delete_amis.sh
+++ b/openvidu-server/deployments/pro/aws/delete_amis.sh
@@ -1,47 +1,24 @@
#!/bin/bash -x
set -eu -o pipefail
+# Process AMI_LIST
+AMI_LIST=($(echo "$AMI_LIST" | tr ',' '\n'))
+
# Remove the list of AMIs in each region
-
-export AWS_ACCESS_KEY_ID=
-export AWS_SECRET_ACCESS_KEY=
-
-LIST="us-east-2:ami-0b779580e2c11e904
- us-west-1:ami-085b7176f53c6d7fe
- us-west-2:ami-029d0ac01cf0f56be
- ap-south-1:ami-044a9335de8413f90
- ap-northeast-2:ami-031f6637449d2821d
- ap-southeast-1:ami-0aba433c88526cc8a
- ap-southeast-2:ami-0ee526f6103ac2bd9
- ap-northeast-1:ami-03b3cc03809d43b36
- ca-central-1:ami-071388f538500db04
- eu-central-1:ami-080a9cbd1d3e64583
- eu-west-1:ami-05e6bc185f28b6338
- eu-west-2:ami-0f06e2c003eef90f1
- eu-west-3:ami-0fd9b5eaf08fc0936
- eu-north-1:ami-0e7717a400ba2f1c1
- sa-east-1:ami-0cd51a71e9791197a
- us-east-2:ami-0064508c09a32a93f
- us-west-1:ami-088d1a7099fa57038
- us-west-2:ami-080d4d462cff92974
- ap-south-1:ami-00a60b079166e7dd4
- ap-northeast-2:ami-0f4edc3faf639e044
- ap-southeast-1:ami-0235dbfa3662608a0
- ap-southeast-2:ami-0f5f46178512e6e07
- ap-northeast-1:ami-047b086aa0745ce19
- ca-central-1:ami-0777b151c44ef9944
- eu-central-1:ami-00dd31f7b896f233f
- eu-west-1:ami-0fb9f924ede905546
- eu-west-2:ami-0defcea3b8c198e1e
- eu-west-3:ami-0c56da0b482bdf48e
- eu-north-1:ami-054fc49723d3d516a
- sa-east-1:ami-0dca8c31325d33c72"
-
-for line in ${LIST}
+for line in "${AMI_LIST[@]}"
do
- REGION=$(echo ${line} | cut -d":" -f1)
- AMI_ID=$(echo ${line} | cut -d":" -f2)
- export AWS_DEFAULT_REGION=${REGION}
- aws ec2 deregister-image --image-id $AMI_ID
- sleep 1
+ REGION=$(echo "${line}" | cut -d":" -f1)
+ AMI_ID=$(echo "${line}" | cut -d":" -f2)
+ export AWS_DEFAULT_REGION=${REGION}
+
+ mapfile -t SNAPSHOTS < <(aws ec2 describe-images --image-ids "$AMI_ID" --output text --query 'Images[*].BlockDeviceMappings[*].Ebs.SnapshotId')
+ echo "Deregistering $AMI_ID"
+ aws ec2 deregister-image --image-id "${AMI_ID}"
+ sleep 1
+ for snapshot in "${SNAPSHOTS[@]}";
+ do
+ echo "Deleting Snapshot $snapshot from $AMI_ID"
+ aws ec2 delete-snapshot --snapshot-id "${snapshot}"
+ sleep 1
+ done
done
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/aws/replicate_amis.sh b/openvidu-server/deployments/pro/aws/replicate_amis.sh
index 1838573f..3069849f 100755
--- a/openvidu-server/deployments/pro/aws/replicate_amis.sh
+++ b/openvidu-server/deployments/pro/aws/replicate_amis.sh
@@ -1,4 +1,4 @@
-#!/bin/bash -x
+#!/bin/bash
set -eu -o pipefail
# Replicate AMIs in all regions
@@ -10,12 +10,11 @@ set -eu -o pipefail
#
# OV_AMI_NAME OpenVidu AMI Name
# OV_AMI_ID OpenVidu AMI ID
+#
+# UPDATE_CF Boolean, true if you want to update CF template by OPENVIDU_PRO_VERSION
+# OPENVIDU_VERSION OpenVidu Version of the CF you want to update. It will update CF-OpenVidu-Pro-OPENVIDU_PRO_VERSION
export AWS_DEFAULT_REGION=eu-west-1
-if [ ${CF_OVP_TARGET} == "market" ]; then
- export AWS_ACCESS_KEY_ID=${NAEVA_AWS_ACCESS_KEY_ID}
- export AWS_SECRET_ACCESS_KEY=${NAEVA_AWS_SECRET_ACCESS_KEY}
-fi
echo "Making original AMIs public"
@@ -96,26 +95,82 @@ do
ITER=$(expr $ITER + 1)
done
-echo
-echo "OpenVidu Server Pro Node AMI IDs"
+
+# Print and generate replicated AMIS
+REPLICATED_AMIS_FILE="replicated_amis.yaml"
+echo "OV AMIs and KMS AMIs replication:"
+{
+ echo "#start_mappings"
+ echo "Mappings:"
+ echo " OVAMIMAP:"
+ ITER=0
+ for i in "${OPENVIDU_SERVER_PRO_AMI_IDS[@]}"
+ do
+ AMI_ID=${OPENVIDU_SERVER_PRO_AMI_IDS[$ITER]}
+ REGION=${REGIONS[$ITER]}
+ echo " ${REGION}:"
+ echo " AMI: ${AMI_ID}"
+ ITER=$(expr $ITER + 1)
+ done
+ echo ""
+ echo " KMSAMIMAP:"
+ ITER=0
+ for i in "${MEDIA_NODE_AMI_IDS[@]}"
+ do
+ AMI_ID=${MEDIA_NODE_AMI_IDS[$ITER]}
+ REGION=${REGIONS[$ITER]}
+ echo " ${REGION}:"
+ echo " AMI: ${AMI_ID}"
+ ITER=$(expr $ITER + 1)
+ done
+ echo "#end_mappings"
+ echo ""
+} > "${REPLICATED_AMIS_FILE}" 2>&1
+
+# Print replicated AMIs
+cat "${REPLICATED_AMIS_FILE}"
+
+if [[ ${UPDATE_CF} == "true" ]]; then
+ if [[ ! -z ${OPENVIDU_PRO_VERSION} ]]; then
+ # Download s3 file
+ aws s3 cp s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml
+ sed -e "/^#end_mappings/r ${REPLICATED_AMIS_FILE}" -e '/^#start_mappings/,/^#end_mappings/d' -i CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml
+ aws s3 cp CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml s3://aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml --acl public-read
+ fi
+fi
+
+# Print AMI_LIST for delete_amis.sh
+AMI_LIST=""
ITER=0
for i in "${OPENVIDU_SERVER_PRO_AMI_IDS[@]}"
do
AMI_ID=${OPENVIDU_SERVER_PRO_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
- echo " ${REGION}:"
- echo " AMI: ${AMI_ID}"
+ if [[ ${ITER} -eq 0 ]]; then
+ AMI_LIST="${REGION}:${AMI_ID}"
+ else
+ AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
+ fi
ITER=$(expr $ITER + 1)
done
+echo "AMI_LIST_OV: ${AMI_LIST}"
-echo
-echo "Media Node AMI IDs"
+# Print AMI_LIST for delete_amis.sh
+AMI_LIST=""
ITER=0
for i in "${MEDIA_NODE_AMI_IDS[@]}"
do
AMI_ID=${MEDIA_NODE_AMI_IDS[$ITER]}
REGION=${REGIONS[$ITER]}
- echo " ${REGION}:"
- echo " AMI: ${AMI_ID}"
+ if [[ ${ITER} -eq 0 ]]; then
+ AMI_LIST="${REGION}:${AMI_ID}"
+ else
+ AMI_LIST="${AMI_LIST},${REGION}:${AMI_ID}"
+ fi
ITER=$(expr $ITER + 1)
-done
\ No newline at end of file
+done
+echo "AMI_LIST_KMS: ${AMI_LIST}"
+
+# Cleaning the house
+rm "${REPLICATED_AMIS_FILE}"
+rm CF-OpenVidu-Pro-${OPENVIDU_PRO_VERSION}.yaml
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env
new file mode 100644
index 00000000..59371b17
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env
@@ -0,0 +1,352 @@
+# OpenVidu configuration
+# ----------------------
+# Documentation: https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/
+
+# NOTE: This file doesn't need to quote assignment values, like most shells do.
+# All values are stored as-is, even if they contain spaces, so don't quote them.
+
+# Domain name. If you do not have one, the public IP of the machine.
+# For example: 198.51.100.1, or openvidu.example.com
+DOMAIN_OR_PUBLIC_IP=
+
+# OpenVidu PRO License
+OPENVIDU_PRO_LICENSE=
+
+# OpenVidu SECRET used for apps to connect to OpenVidu server and users to access to OpenVidu Dashboard
+OPENVIDU_SECRET=
+
+# Certificate type:
+# - selfsigned: Self signed certificate. Not recommended for production use.
+# Users will see an ERROR when connected to web page.
+# - owncert: Valid certificate purchased in a Internet services company.
+# Please put the certificates files inside folder ./owncert
+# with names certificate.key and certificate.cert
+# - letsencrypt: Generate a new certificate using letsencrypt. Please set the
+# required contact email for Let's Encrypt in LETSENCRYPT_EMAIL
+# variable.
+CERTIFICATE_TYPE=selfsigned
+
+# If CERTIFICATE_TYPE=letsencrypt, you need to configure a valid email for notifications
+LETSENCRYPT_EMAIL=user@example.com
+
+# Proxy configuration
+# If you want to change the ports on which openvidu listens, uncomment the following lines
+
+# Allows any request to http://DOMAIN_OR_PUBLIC_IP:HTTP_PORT/ to be automatically
+# redirected to https://DOMAIN_OR_PUBLIC_IP:HTTPS_PORT/.
+# WARNING: the default port 80 cannot be changed during the first boot
+# if you have chosen to deploy with the option CERTIFICATE_TYPE=letsencrypt
+# HTTP_PORT=80
+
+# Changes the port of all services exposed by OpenVidu.
+# SDKs, REST clients and browsers will have to connect to this port
+# HTTPS_PORT=443
+
+# Old paths are considered now deprecated, but still supported by default.
+# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
+# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
+# to stop allowing the use of old paths.
+# Default value is true
+# SUPPORT_DEPRECATED_API=true
+
+# If true request to with www will be redirected to non-www requests
+# Default value is false
+# REDIRECT_WWW=false
+
+# How many workers to configure in nginx proxy.
+# The more workers, the more requests will be handled
+# Default value is 10240
+# WORKER_CONNECTIONS=10240
+
+# Access restrictions
+# In this section you will be able to restrict the IPs from which you can access to
+# Openvidu API and the Administration Panel
+# WARNING! If you touch this configuration you can lose access to the platform from some IPs.
+# Use it carefully.
+
+# This section limits access to the /dashboard (OpenVidu CE) and /inspector (OpenVidu Pro) pages.
+# The form for a single IP or an IP range is:
+# ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.1 and ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.0/24
+# To limit multiple IPs or IP ranges, separate by commas like this:
+# ALLOWED_ACCESS_TO_DASHBOARD=198.51.100.1, 198.51.100.0/24
+# ALLOWED_ACCESS_TO_DASHBOARD=
+
+# This section limits access to the Openvidu REST API.
+# The form for a single IP or an IP range is:
+# ALLOWED_ACCESS_TO_RESTAPI=198.51.100.1 and ALLOWED_ACCESS_TO_RESTAPI=198.51.100.0/24
+# To limit multiple IPs or or IP ranges, separate by commas like this:
+# ALLOWED_ACCESS_TO_RESTAPI=198.51.100.1, 198.51.100.0/24
+# ALLOWED_ACCESS_TO_RESTAPI=
+
+# Mode of cluster management. Can be auto (OpenVidu manages Media Nodes on its own.
+# Parameter KMS_URIS is ignored) or manual (user must manage Media Nodes. Parameter
+# KMS_URIS is used: if any uri is provided it must be valid)
+OPENVIDU_PRO_CLUSTER_MODE=manual
+
+# Which environment are you using
+# Possibles values: aws, on_premise
+OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
+
+# Unique identifier of your cluster. Each OpenVidu Server Pro instance corresponds to one cluster.
+# You can launch as many clusters as you want with your license key.
+# Cluster ID will always be stored to disk so restarting OpenVidu Server Pro will keep the same previous cluster ID
+# if this configuration parameter is not given a distinct value.
+# OPENVIDU_PRO_CLUSTER_ID=
+
+# The desired number of Media Nodes on startup. First the autodiscovery process is performed.
+# If there are too many Media Nodes after that, they will be dropped until this number is reached.
+# If there are not enough, more will be launched.
+# This only takes place if OPENVIDU_PRO_CLUSTER_MODE is set to auto
+# If set to zero no media servers will be lauched.
+# Type: number >= 0
+#OPENVIDU_PRO_CLUSTER_MEDIA_NODES=
+
+# How often each running Media Node will send OpenVidu Server Pro Node load metrics, in seconds.
+# This property is only used when OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY is 'cpu'. Other load strategies
+# gather information synchronously when required
+# Type: number >= 0
+# OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL=
+
+# Whether to enable or disable autoscaling. With autoscaling the number of Media Nodes will
+# be automatically adjusted according to existing load
+# Values: true | false
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING=false
+
+# How often the autoscaling algorithm runs, in seconds
+# Type number >= 0
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING_INTERVAL=
+
+# If autoscaling is enabled, the upper limit of Media Nodes that can be reached.
+# Even when the average load exceeds the threshold, no more Media Nodes will be added to cluster
+# Type number >= 0
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MAX_NODES=
+
+# If autoscaling is enabled, the lower limit of Media Nodes that can be reached.
+# Even when the average load is inferior to the threshold, no more Media Nodes will
+# be removed from the cluster
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MIN_NODES=
+
+# If autoscaling is enabled, the upper average load threshold that will trigger the addition
+# of a new Media Node.
+# Percentage value (0 min, 100 max)
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MAX_LOAD=
+
+# If autoscaling is enabled, the lower average load threshold that will trigger the removal
+# of an existing Media Node.
+# Percentage value (0 min, 100 max)
+# OPENVIDU_PRO_CLUSTER_AUTOSCALING_MIN_LOAD=
+
+# What parameter should be used to distribute the creation of new sessions
+# (and therefore distribution of load) among all available Media Nodes
+OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY=streams
+
+# Whether to enable or disable Network Quality API. You can monitor and
+# warn users about the quality of their networks with this feature
+# OPENVIDU_PRO_NETWORK_QUALITY=false
+
+# If OPENVIDU_PRO_NETWORK_QUALITY=true, how often the network quality
+# algorithm will be invoked for each user, in seconds
+# OPENVIDU_PRO_NETWORK_QUALITY_INTERVAL=5
+
+# Max days until delete indexes in state of rollover on Elasticsearch
+# Type number >= 0
+# Default Value is 15
+# OPENVIDU_PRO_ELASTICSEARCH_MAX_DAYS_DELETE=
+
+# If you have an external Elasticsearch and Kibana already running, put here the url to elasticsearch and kibana services.
+# It is very important that both url have the port specified in the url.
+# If you want to use the deployed Elasticsearch and Kibana locally, keep these variables commented.
+#OPENVIDU_PRO_ELASTICSEARCH_HOST=
+#OPENVIDU_PRO_KIBANA_HOST=
+
+# Where to store recording files. Can be 'local' (local storage) or 's3' (AWS bucket).
+# You will need to define a OPENVIDU_PRO_AWS_S3_BUCKET if you use it.
+#OPENVIDU_PRO_RECORDING_STORAGE=
+
+# S3 Bucket where to store recording files. May include paths to allow navigating
+# folder structures inside the bucket. This property is only taken into account
+# if OPENVIDU_PRO_RECORDING_STORAGE=s3
+#OPENVIDU_PRO_AWS_S3_BUCKET=
+
+# If OPENVIDU_PRO_RECORDING_STORAGE=s3, the collection of HTTP header values that the internal AWS client will use during
+# the upload process. The property is a key-value map of strings, following the format of a JSON object. For example, for applying
+# server-side encryption with AES-256, this header is mandatory: {"x-amz-server-side-encryption":"AES256"}.
+# The list of available headers can be found here: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/Headers.html
+# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+#OPENVIDU_PRO_AWS_S3_HEADERS=
+
+# If you're instance has a role which has access to read
+# and write into the s3 bucket, you don't need this parameter
+# OPENVIDU_PRO_AWS_ACCESS_KEY=
+
+# AWS credentials secret key from OPENVIDU_PRO_AWS_ACCESS_KEY. This property is only
+# taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+# If you're instance has a role which has access to read
+# and write into the s3 bucket, you don't need this parameter
+# OPENVIDU_PRO_AWS_SECRET_KEY=
+
+# AWS region in which the S3 bucket is located (e.g. eu-west-1). If not provided,
+# the region will try to be discovered automatically, although this is not always possible.
+# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+# OPENVIDU_PRO_AWS_REGION=
+
+# Whether to enable recording module or not
+OPENVIDU_RECORDING=false
+
+# Use recording module with debug mode.
+OPENVIDU_RECORDING_DEBUG=false
+
+# Openvidu Folder Record used for save the openvidu recording videos. Change it
+# with the folder you want to use from your host.
+OPENVIDU_RECORDING_PATH=/opt/openvidu/recordings
+
+# System path where OpenVidu Server should look for custom recording layouts
+OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/custom-layout
+
+# if true any client can connect to
+# https://OPENVIDU_SERVER_IP:OPENVIDU_PORT/recordings/any_session_file.mp4
+# and access any recorded video file. If false this path will be secured with
+# OPENVIDU_SECRET param just as OpenVidu Server dashboard at
+# https://OPENVIDU_SERVER_IP:OPENVIDU_PORT
+# Values: true | false
+OPENVIDU_RECORDING_PUBLIC_ACCESS=false
+
+# Which users should receive the recording events in the client side
+# (recordingStarted, recordingStopped). Can be all (every user connected to
+# the session), publisher_moderator (users with role 'PUBLISHER' or
+# 'MODERATOR'), moderator (only users with role 'MODERATOR') or none
+# (no user will receive these events)
+OPENVIDU_RECORDING_NOTIFICATION=publisher_moderator
+
+# Timeout in seconds for recordings to automatically stop (and the session involved to be closed)
+# when conditions are met: a session recording is started but no user is publishing to it or a session
+# is being recorded and last user disconnects. If a user publishes within the timeout in either case,
+# the automatic stop of the recording is cancelled
+# 0 means no timeout
+OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120
+
+# Maximum video bandwidth sent from clients to OpenVidu Server, in kbps.
+# 0 means unconstrained
+OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
+
+# Minimum video bandwidth sent from clients to OpenVidu Server, in kbps.
+# 0 means unconstrained
+OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
+
+# Maximum video bandwidth sent from OpenVidu Server to clients, in kbps.
+# 0 means unconstrained
+OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
+
+# Minimum video bandwidth sent from OpenVidu Server to clients, in kbps.
+# 0 means unconstrained
+OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
+
+# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
+# when a codec can not be forced, transcoding will be allowed
+# Default value is VP8
+# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
+
+# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
+# Default value is false
+# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
+
+# true to enable OpenVidu Webhook service. false' otherwise
+# Values: true | false
+OPENVIDU_WEBHOOK=false
+
+# HTTP endpoint where OpenVidu Server will send Webhook HTTP POST messages
+# Must be a valid URL: http(s)://ENDPOINT
+#OPENVIDU_WEBHOOK_ENDPOINT=
+
+# List of headers that OpenVidu Webhook service will attach to HTTP POST messages
+#OPENVIDU_WEBHOOK_HEADERS=
+
+# List of events that will be sent by OpenVidu Webhook service
+# Default value is all available events
+OPENVIDU_WEBHOOK_EVENTS=[sessionCreated,sessionDestroyed,participantJoined,participantLeft,webrtcConnectionCreated,webrtcConnectionDestroyed,recordingStatusChanged,filterEventDispatched,mediaNodeStatusChanged]
+
+# How often the garbage collector of non active sessions runs.
+# This helps cleaning up sessions that have been initialized through
+# REST API (and maybe tokens have been created for them) but have had no users connected.
+# Default to 900s (15 mins). 0 to disable non active sessions garbage collector
+OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
+
+# Minimum time in seconds that a non active session must have been in existence
+# for the garbage collector of non active sessions to remove it. Default to 3600s (1 hour).
+# If non active sessions garbage collector is disabled
+# (property 'OPENVIDU_SESSIONS_GARBAGE_INTERVAL' to 0) this property is ignored
+OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
+
+# Call Detail Record enabled
+# Whether to enable Call Detail Record or not
+# Values: true | false
+OPENVIDU_CDR=false
+
+# Path where the cdr log files are hosted
+OPENVIDU_CDR_PATH=/opt/openvidu/cdr
+
+# Openvidu Server Level logs
+# --------------------------
+# Uncomment the next line and define this variable to change
+# the verbosity level of the logs of Openvidu Service
+# RECOMENDED VALUES: INFO for normal logs DEBUG for more verbose logs
+# OV_CE_DEBUG_LEVEL=INFO
+
+# OpenVidu Java Options
+# --------------------------
+# Uncomment the next line and define this to add options to java command
+# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
+# JAVA_OPTIONS=-Xms2048m -Xmx4096m
+
+# ElasticSearch Java Options
+# --------------------------
+# Uncomment the next line and define this to add options to java command of Elasticsearch
+# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
+# By default ElasticSearch is configured to use "-Xms2g -Xmx2g" as Java Min and Max memory heap allocation
+# ES_JAVA_OPTS=-Xms2048m -Xmx4096m
+
+# Kibana And ElasticSearch Credentials Configuration
+# --------------------------
+# Kibana And ElasticSearch Basic Auth configuration (Credentials)
+# This credentials will aso be valid for Kibana dashboard
+ELASTICSEARCH_USERNAME=elasticadmin
+ELASTICSEARCH_PASSWORD=
+
+# Media Node Configuration
+# --------------------------
+# You can add any KMS environment variable as described in the
+# documentation of the docker image: https://hub.docker.com/r/kurento/kurento-media-server
+# If you want to add an environment variable to KMS, you must add a variable using this prefix: 'KMS_DOCKER_ENV_',
+# followed by the environment variable you want to setup.
+# For example if you want to setup KMS_MIN_PORT to 50000, it would be KMS_DOCKER_ENV_KMS_MIN_PORT=50000
+
+# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
+# Uncomment the next line and define this variable with KMS image that you want use
+# By default, KMS_IMAGE is defined in media nodes and it does not need to be specified unless
+# you want to use a specific version of KMS
+# KMS_IMAGE=kurento/kurento-media-server:6.15.0
+
+# Uncomment the next line and define this variable to change
+# the verbosity level of the logs of KMS
+# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
+# KMS_DOCKER_ENV_GST_DEBUG=
+
+# Cloudformation configuration
+# --------------------------
+# If you're working outside AWS ignore this section
+#AWS_DEFAULT_REGION=
+#AWS_IMAGE_ID=
+#AWS_INSTANCE_TYPE=
+#AWS_KEY_NAME=
+#AWS_SUBNET_ID=
+#AWS_SECURITY_GROUP=
+#AWS_STACK_ID=
+#AWS_STACK_NAME=
+#AWS_CLI_DOCKER_TAG=
+#AWS_VOLUME_SIZE=
+
+# AWS ASG configuration
+# --------------------------
+RM_REDIS_IP=
+RM_REDIS_PORT=
+RM_SQS_QUEUE=
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml
new file mode 100644
index 00000000..369daa45
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml
@@ -0,0 +1,48 @@
+filebeat.inputs:
+ - type: container
+ paths:
+ - '/var/lib/docker/containers/*/*.log'
+
+processors:
+ - add_docker_metadata:
+ host: "unix:///var/run/docker.sock"
+ - add_host_metadata:
+ netinfo.enabled: true
+
+ - decode_json_fields:
+ fields: ["message"]
+ target: "json"
+ overwrite_keys: true
+ - drop_event:
+ when.or:
+ - contains:
+ container.image.name: openvidu/openvidu-coturn
+ - contains:
+ container.image.name: docker.elastic.co/elasticsearch/elasticsearch
+ - contains:
+ container.image.name: docker.elastic.co/kibana/kibana
+ - contains:
+ container.image.name: docker.elastic.co/beats/filebeat-oss
+ - contains:
+ container.image.name: docker.elastic.co/beats/metricbeat-oss
+ - add_fields:
+ fields:
+ cluster-id: ${OPENVIDU_PRO_CLUSTER_ID:undefined}
+
+output:
+ elasticsearch:
+ hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
+ indices:
+ - index: "filebeat-redis-%{+yyyy.MM.dd}"
+ when.or:
+ - contains:
+ container.image.name: openvidu/openvidu-redis
+ - index: "filebeat-nginx-%{+yyyy.MM.dd}"
+ when.or:
+ - contains:
+ container.image.name: openvidu/openvidu-proxy
+
+
+logging.json: true
+logging.metrics.enabled: false
+setup.ilm.enabled: false
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml
new file mode 100644
index 00000000..09a68d9a
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml
@@ -0,0 +1,10 @@
+metricbeat.modules:
+- module: nginx
+ metricsets: ["stubstatus"]
+ enabled: true
+ period: 10s
+ hosts: ["http://127.0.0.1"]
+ server_status_path: "nginx_status"
+output:
+ elasticsearch:
+ hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh
new file mode 100644
index 00000000..8689f3b7
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -eu -o pipefail
+
+# Set debug mode
+DEBUG=${DEBUG:-false}
+[ "$DEBUG" == "true" ] && set -x
+
+OUTPUT=$(mktemp -t openvidu-autodiscover-XXX --suffix .json)
+
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 describe-instances \
+ --output text \
+ --filters "Name=instance-state-name,Values=running" \
+ "Name=tag:ov-cluster-member,Values=kms" \
+ "Name=tag:ov-stack-name,Values=${AWS_STACK_NAME}" \
+ "Name=tag:ov-stack-region,Values=${AWS_DEFAULT_REGION}" \
+ --query 'Reservations[*].Instances[*].{id:InstanceId,ip:PrivateIpAddress}' > ${OUTPUT}
+
+cat ${OUTPUT} | jq --raw-input --slurp 'split("\n") | map(split("\t")) | .[0:-1] | map( { "id": .[0], "ip": .[1] } )'
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh
new file mode 100644
index 00000000..8d585480
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -e -o pipefail
+
+# Set debug mode
+DEBUG=${DEBUG:-false}
+[ "$DEBUG" == "true" ] && set -x
+
+ID=$1
+[ -z "${ID}" ] && { echo "Must provide instance ID"; exit 1; }
+
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 terminate-instances --instance-ids ${ID} --output json
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh
new file mode 100644
index 00000000..48b92bf7
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+set -e -o pipefail
+
+# Set debug mode
+DEBUG=${DEBUG:-false}
+[ "$DEBUG" == "true" ] && set -x
+
+TMPFILE=$(mktemp -t openvidu-userdata-XXX --suffix .txt)
+OUTPUT=$(mktemp -t openvidu-launch-kms-XXX --suffix .json)
+ERROUTPUT=$(mktemp -t openvidu-launch-kms-XXX --suffix .err)
+
+trap exit_on_error ERR
+
+exit_on_error () {
+ ERROR_TYPE=$(cat ${ERROUTPUT} | awk '{ print $4 }' | sed -r 's/\(|\)//g' | tr -d '\n')
+
+ case ${ERROR_TYPE}
+ in
+ "InvalidParameterValue")
+ echo -e "Parameter invalid " $(cat ${ERROUTPUT}) >&2
+ exit 1
+ ;;
+
+ "UnauthorizedOperation")
+ MSG_COD=$(cat ${ERROUTPUT} | awk -F: '{ print $3 }')
+ MSG_DEC=$(docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} sts decode-authorization-message --encoded-message ${MSG_COD})
+
+ echo -e "Unauthorized " $(cat ${MSG_DEC}) >&2
+ exit 1
+ ;;
+ *)
+ echo -e "Unknown error " $(cat ${ERROUTPUT}) >&2
+ exit 1
+ ;;
+ esac
+}
+
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 run-instances \
+ --image-id ${AWS_IMAGE_ID} --count 1 \
+ --instance-type ${AWS_INSTANCE_TYPE} \
+ --key-name ${AWS_KEY_NAME} \
+ --subnet-id ${AWS_SUBNET_ID} \
+ --tag-specifications "ResourceType=instance,Tags=[{Key='Name',Value='Kurento Media Server'},{Key='ov-cluster-member',Value='kms'},{Key='ov-stack-name',Value='${AWS_STACK_NAME}'},{Key='ov-stack-region',Value='${AWS_DEFAULT_REGION}'}]" \
+ --iam-instance-profile Name="OpenViduInstanceProfile-${AWS_STACK_NAME}-${AWS_DEFAULT_REGION}" \
+ --block-device-mappings "DeviceName=/dev/sda1,Ebs={DeleteOnTermination=True,VolumeType='gp2',VolumeSize='${AWS_VOLUME_SIZE}'}" \
+ --security-group-ids ${AWS_SECURITY_GROUP} > ${OUTPUT} 2> ${ERROUTPUT}
+
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
+
+# Generating the output
+KMS_IP=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .NetworkInterfaces[0] | .PrivateIpAddress')
+KMS_ID=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
+
+# Wait media-node controller
+attempt_counter=0
+max_attempts=10
+
+until $(curl --output /dev/null --silent --head --fail -u OPENVIDUAPP:${OPENVIDU_SECRET} http://${KMS_IP}:3000/media-node/status); do
+ if [ ${attempt_counter} -eq ${max_attempts} ];then
+ exit 1
+ fi
+ attempt_counter=$(($attempt_counter+1))
+ sleep 5
+done
+
+jq -n \
+ --arg id "${KMS_ID}" \
+ --arg ip "${KMS_IP}" \
+ '{ id: $id, ip: $ip }'
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml
new file mode 100644
index 00000000..d4796d20
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml
@@ -0,0 +1,26 @@
+version: '3.1'
+
+services:
+ # --------------------------------------------------------------
+ #
+ # Change this if your want use your own application.
+ # It's very important expose your application in port 5442
+ # and use the http protocol.
+ #
+ # Default Application
+ #
+ # Openvidu-Call Version: 2.16.0
+ #
+ # --------------------------------------------------------------
+ app:
+ image: openvidu/openvidu-call:2.16.0
+ restart: on-failure
+ network_mode: host
+ environment:
+ - SERVER_PORT=5442
+ - OPENVIDU_URL=http://localhost:5443
+ - OPENVIDU_SECRET=${OPENVIDU_SECRET}
+ - CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml
new file mode 100644
index 00000000..3174c3a6
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml
@@ -0,0 +1,141 @@
+# ------------------------------------------------------------------------------
+#
+# DO NOT MODIFY THIS FILE !!!
+#
+# Configuration properties should be specified in .env file
+#
+# Application based on OpenVidu should be specified in
+# docker-compose.override.yml file
+#
+# This docker-compose file coordinates all services of OpenVidu Pro Platform
+#
+# This file will be overridden when update OpenVidu Platform
+#
+# Openvidu Version: 2.16.0
+#
+# Installation Mode: On Premises
+#
+# ------------------------------------------------------------------------------
+
+version: '3.1'
+
+services:
+
+ openvidu-server:
+ image: openvidu/openvidu-server-pro:2.17.0-dev5
+ restart: on-failure
+ network_mode: host
+ entrypoint: ['/usr/local/bin/entrypoint.sh']
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
+ - ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:${OPENVIDU_RECORDING_CUSTOM_LAYOUT}
+ - ${OPENVIDU_CDR_PATH}:${OPENVIDU_CDR_PATH}
+ - ./cluster:/opt/openvidu/cluster
+ - .env:${PWD}/.env
+ env_file:
+ - .env
+ environment:
+ - SERVER_SSL_ENABLED=false
+ - SERVER_PORT=5443
+ - KMS_URIS=[]
+ - OPENVIDU_WEBHOOK=false
+ - OPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook
+ - OPENVIDU_PRO_REPLICATION_MANAGER_WEBHOOK=http://127.0.0.1:4443/openvidu/replication-manager-webhook?OPENVIDU_SECRET=${OPENVIDU_SECRET}
+ - COTURN_REDIS_IP=127.0.0.1
+ - COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
+ - COTURN_IP=${COTURN_IP:-auto-ipv4}
+ - OPENVIDU_PRO_CLUSTER=true
+ - OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
+ - WAIT_KIBANA_URL=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
+ - DOTENV_PATH=${PWD}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ replication-manager:
+ image: openvidu/replication-manager:1.0.0-dev5
+ restart: on-failure
+ network_mode: host
+ environment:
+ - SERVER_PORT=4443
+ - SERVER_SSL_ENABLED=false
+ - OPENVIDU_SECRET=${OPENVIDU_SECRET}
+ - LOCAL_OPENVIDU_SERVER_URI=http://127.0.0.1:5443/
+ - REDIS_HOST=${RM_REDIS_IP}
+ - REDIS_PORT=${RM_REDIS_PORT}
+ - REDIS_PASS=${OPENVIDU_SECRET}
+ - REDIS_TIMEOUT=5
+ - REDIS_DB=replicationmanager
+ - MEDIANODE_AS_NOTIFICATION_QUEUE=${RM_SQS_QUEUE}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ redis:
+ image: openvidu/openvidu-redis:2.0.0-dev2
+ restart: always
+ network_mode: host
+ environment:
+ - REDIS_PASSWORD=${OPENVIDU_SECRET}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ coturn:
+ image: openvidu/openvidu-coturn:3.0.0-dev2
+ restart: on-failure
+ network_mode: host
+ environment:
+ - REDIS_IP=127.0.0.1
+ - TURN_LISTEN_PORT=3478
+ - DB_NAME=0
+ - DB_PASSWORD=${OPENVIDU_SECRET}
+ - MIN_PORT=40000
+ - MAX_PORT=65535
+ - TURN_PUBLIC_IP=${TURN_PUBLIC_IP:-auto-ipv4}
+ - ENABLE_COTURN_LOGS=true
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ metricbeat:
+ image: docker.elastic.co/beats/metricbeat-oss:7.8.0
+ network_mode: host
+ restart: always
+ user: root
+ env_file:
+ - .env
+ environment:
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
+ volumes:
+ - ./beats/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
+ command: >
+ /bin/bash -c "metricbeat -e -strict.perms=false
+ `if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
+ `if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ filebeat:
+ image: docker.elastic.co/beats/filebeat-oss:7.8.0
+ network_mode: host
+ restart: always
+ user: root
+ env_file:
+ - .env
+ environment:
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
+ volumes:
+ - ./beats/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
+ - /var/lib/docker:/var/lib/docker:ro
+ - /var/run/docker.sock:/var/run/docker.sock
+ command: >
+ /bin/bash -c "filebeat -e -strict.perms=false
+ `if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
+ `if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/install_openvidu_pro.sh b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/install_openvidu_pro.sh
new file mode 100755
index 00000000..ec895dfb
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/install_openvidu_pro.sh
@@ -0,0 +1,398 @@
+#!/usr/bin/env bash
+
+# Global variables
+OPENVIDU_FOLDER=openvidu
+OPENVIDU_VERSION=master
+OPENVIDU_UPGRADABLE_VERSION="master"
+AWS_SCRIPTS_FOLDER=${OPENVIDU_FOLDER}/cluster/aws
+ELASTICSEARCH_FOLDER=${OPENVIDU_FOLDER}/elasticsearch
+BEATS_FOLDER=${OPENVIDU_FOLDER}/beats
+DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${OPENVIDU_VERSION}
+
+fatal_error() {
+ printf "\n =======¡ERROR!======="
+ printf "\n %s" "$1"
+ printf "\n"
+ exit 0
+}
+
+new_ov_installation() {
+ printf '\n'
+ printf '\n ======================================='
+ printf '\n Install Openvidu PRO %s' "${OPENVIDU_VERSION}"
+ printf '\n ======================================='
+ printf '\n'
+
+ # Create folder openvidu-docker-compose
+ printf '\n => Creating folder '%s'...' "${OPENVIDU_FOLDER}"
+ mkdir "${OPENVIDU_FOLDER}" || fatal_error "Error while creating the folder '${OPENVIDU_FOLDER}'"
+
+ # Create aws scripts folder
+ printf "\n => Creating folder 'cluster/aws'..."
+ mkdir -p "${AWS_SCRIPTS_FOLDER}" || fatal_error "Error while creating the folder 'cluster/aws'"
+
+ # Create beats folder
+ printf "\n => Creating folder 'beats'..."
+ mkdir -p "${BEATS_FOLDER}" || fatal_error "Error while creating the folder 'beats'"
+
+ # Create elasticsearch folder
+ printf "\n => Creating folder 'elasticsearch'..."
+ mkdir -p "${ELASTICSEARCH_FOLDER}" || fatal_error "Error while creating the folder 'elasticsearch'"
+
+ printf "\n => Changing permission to 'elasticsearch' folder..."
+ chown 1000:1000 "${ELASTICSEARCH_FOLDER}" || fatal_error "Error while changing permission to 'elasticsearch' folder"
+
+ # Download necessary files
+ printf '\n => Downloading Openvidu PRO files:'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh \
+ --output "${AWS_SCRIPTS_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error when downloading the file 'openvidu_autodiscover.sh'"
+ printf '\n - openvidu_autodiscover.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh \
+ --output "${AWS_SCRIPTS_FOLDER}/openvidu_drop.sh" || fatal_error "Error when downloading the file 'openvidu_drop.sh'"
+ printf '\n - openvidu_drop.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh \
+ --output "${AWS_SCRIPTS_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error when downloading the file 'openvidu_launch_kms.sh'"
+ printf '\n - openvidu_launch_kms.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml \
+ --output "${BEATS_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
+ printf '\n - filebeat.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml \
+ --output "${BEATS_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env \
+ --output "${OPENVIDU_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
+ printf '\n - .env'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml \
+ --output "${OPENVIDU_FOLDER}/docker-compose.override.yml" || fatal_error "Error when downloading the file 'docker-compose.override.yml'"
+ printf '\n - docker-compose.override.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml \
+ --output "${OPENVIDU_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
+ printf '\n - docker-compose.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu \
+ --output "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
+ printf '\n - openvidu'
+
+ # Add execution permissions
+ printf "\n => Adding permission:"
+
+ chmod +x "${OPENVIDU_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
+ printf '\n - openvidu'
+
+ chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error while adding permission to 'openvidu_autodiscover.sh' program"
+ printf '\n - openvidu_autodiscover.sh'
+
+ chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_drop.sh" || fatal_error "Error while adding permission to 'openvidu' openvidu_drop.sh"
+ printf '\n - openvidu_drop.sh'
+
+ chmod +x "${AWS_SCRIPTS_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error while adding permission to 'openvidu_launch_kms.sh' program"
+ printf '\n - openvidu_launch_kms.sh'
+
+ # Create own certificated folder
+ printf "\n => Creating folder 'owncert'..."
+ mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
+
+ # Create vhost nginx folder
+ printf "\n => Creating folder 'custom-nginx-vhosts'..."
+ mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
+
+ # Ready to use
+ printf '\n'
+ printf '\n'
+ printf '\n ======================================='
+ printf '\n Openvidu PRO successfully installed.'
+ printf '\n ======================================='
+ printf '\n'
+ printf '\n 1. Go to openvidu folder:'
+ printf '\n $ cd openvidu'
+ printf '\n'
+ printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, '
+ printf '\n OPENVIDU_SECRET, and ELASTICSEARCH_PASSWORD in .env file:'
+ printf '\n $ nano .env'
+ printf '\n'
+ printf '\n 3. Start OpenVidu'
+ printf '\n $ ./openvidu start'
+ printf '\n'
+ printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. "
+ printf "\n This folder is necessary for store elasticsearch data."
+ printf "\n For more information, check:"
+ printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
+ printf '\n'
+ printf '\n'
+ exit 0
+}
+
+upgrade_ov() {
+ # Search local Openvidu installation
+ printf '\n'
+ printf '\n ============================================'
+ printf '\n Search Previous Installation of Openvidu'
+ printf '\n ============================================'
+ printf '\n'
+
+ SEARCH_IN_FOLDERS=(
+ "${PWD}"
+ "/opt/${OPENVIDU_FOLDER}"
+ )
+
+ for folder in "${SEARCH_IN_FOLDERS[@]}"; do
+ printf "\n => Searching in '%s' folder..." "${folder}"
+
+ if [ -f "${folder}/docker-compose.yml" ]; then
+ OPENVIDU_PREVIOUS_FOLDER="${folder}"
+
+ printf "\n => Found installation in folder '%s'" "${folder}"
+ break
+ fi
+ done
+
+ [ -z "${OPENVIDU_PREVIOUS_FOLDER}" ] && fatal_error "No previous Openvidu installation found"
+
+ # Uppgrade Openvidu
+ OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
+ [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
+
+ # In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
+ # posible or not. If it is not posible launch a warning and stop the upgrade.
+ if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
+ fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
+ fi
+
+ printf '\n'
+ printf '\n ======================================='
+ printf '\n Upgrade Openvidu PRO %s to %s' "${OPENVIDU_PREVIOUS_VERSION}" "${OPENVIDU_VERSION}"
+ printf '\n ======================================='
+ printf '\n'
+
+ ROLL_BACK_FOLDER="${OPENVIDU_PREVIOUS_FOLDER}/.old-${OPENVIDU_PREVIOUS_VERSION}"
+ TMP_FOLDER="${OPENVIDU_PREVIOUS_FOLDER}/tmp"
+ ACTUAL_FOLDER="${PWD}"
+ USE_OV_CALL=$(grep -E '^ image: openvidu/openvidu-call:.*$' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml" | tr -d '[:space:]')
+
+ printf "\n Creating rollback folder '%s'..." ".old-${OPENVIDU_PREVIOUS_VERSION}"
+ mkdir "${ROLL_BACK_FOLDER}" || fatal_error "Error while creating the folder '.old-${OPENVIDU_PREVIOUS_VERSION}'"
+
+ printf "\n Creating temporal folder 'tmp'..."
+ mkdir "${TMP_FOLDER}" || fatal_error "Error while creating the folder 'temporal'"
+
+ # Download necessary files
+ printf '\n => Downloading new Openvidu PRO files:'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh \
+ --output "${TMP_FOLDER}/openvidu_autodiscover.sh" || fatal_error "Error when downloading the file 'openvidu_autodiscover.sh'"
+ printf '\n - openvidu_autodiscover.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_drop.sh \
+ --output "${TMP_FOLDER}/openvidu_drop.sh" || fatal_error "Error when downloading the file 'openvidu_drop.sh'"
+ printf '\n - openvidu_drop.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh \
+ --output "${TMP_FOLDER}/openvidu_launch_kms.sh" || fatal_error "Error when downloading the file 'openvidu_launch_kms.sh'"
+ printf '\n - openvidu_launch_kms.sh'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/filebeat.yml \
+ --output "${TMP_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
+ printf '\n - filebeat.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/beats/metricbeat.yml \
+ --output "${TMP_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/.env \
+ --output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
+ printf '\n - .env'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.override.yml \
+ --output "${TMP_FOLDER}/docker-compose.override.yml" || fatal_error "Error when downloading the file 'docker-compose.override.yml'"
+ printf '\n - docker-compose.override.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/docker-compose.yml \
+ --output "${TMP_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
+ printf '\n - docker-compose.yml'
+
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu \
+ --output "${TMP_FOLDER}/openvidu" || fatal_error "Error when downloading the file 'openvidu'"
+ printf '\n - openvidu'
+
+ # Dowloading new images and stoped actual Openvidu
+ printf '\n => Dowloading new images...'
+ printf '\n'
+ sleep 1
+
+ printf "\n => Moving to 'tmp' folder..."
+ printf '\n'
+ cd "${TMP_FOLDER}" || fatal_error "Error when moving to 'tmp' folder"
+ printf '\n'
+ docker-compose pull | true
+
+ printf '\n => Stoping Openvidu...'
+ printf '\n'
+ sleep 1
+
+ printf "\n => Moving to 'openvidu' folder..."
+ printf '\n'
+ cd "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error when moving to 'openvidu' folder"
+ printf '\n'
+ docker-compose down | true
+
+ printf '\n'
+ printf '\n => Moving to working dir...'
+ cd "${ACTUAL_FOLDER}" || fatal_error "Error when moving to working dir"
+
+ # Move old files to rollback folder
+ printf '\n => Moving previous installation files to rollback folder:'
+
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'docker-compose.yml'"
+ printf '\n - docker-compose.yml'
+
+ if [ ! -z "${USE_OV_CALL}" ]; then
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'docker-compose.override.yml'"
+ printf '\n - docker-compose.override.yml'
+ fi
+
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
+ printf '\n - openvidu'
+
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'cluster/aws'"
+ printf '\n - cluster/aws'
+
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats'"
+ printf '\n - beats'
+
+ cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
+ printf '\n - .env'
+
+ if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
+ printf '\n - custom-nginx-vhosts'
+ fi
+
+ # Move tmp files to Openvidu
+ printf '\n => Updating files:'
+
+ mv "${TMP_FOLDER}/docker-compose.yml" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.yml'"
+ printf '\n - docker-compose.yml'
+
+ if [ ! -z "${USE_OV_CALL}" ]; then
+ mv "${TMP_FOLDER}/docker-compose.override.yml" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.override.yml'"
+ printf '\n - docker-compose.override.yml'
+ else
+ mv "${TMP_FOLDER}/docker-compose.override.yml" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.override.yml-${OPENVIDU_VERSION}" || fatal_error "Error while updating 'docker-compose.override.yml'"
+ printf '\n - docker-compose.override.yml-%s' "${OPENVIDU_VERSION}"
+ fi
+
+ mv "${TMP_FOLDER}/.env" "${OPENVIDU_PREVIOUS_FOLDER}/.env-${OPENVIDU_VERSION}" || fatal_error "Error while moving previous '.env'"
+ printf '\n - .env-%s' "${OPENVIDU_VERSION}"
+
+ mv "${TMP_FOLDER}/openvidu" "${OPENVIDU_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'openvidu'"
+ printf '\n - openvidu'
+
+ mkdir "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while creating the folder 'cluster/aws'"
+
+ mkdir "${OPENVIDU_PREVIOUS_FOLDER}/beats" || fatal_error "Error while creating the folder 'beats'"
+
+ mv "${TMP_FOLDER}/openvidu_autodiscover.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_autodiscover.sh'"
+ printf '\n - openvidu_autodiscover.sh'
+
+ mv "${TMP_FOLDER}/openvidu_drop.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_drop.sh'"
+ printf '\n - openvidu_drop.sh'
+
+ mv "${TMP_FOLDER}/openvidu_launch_kms.sh" "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" || fatal_error "Error while updating 'openvidu_launch_kms.sh'"
+ printf '\n - openvidu_launch_kms.sh'
+
+ mv "${TMP_FOLDER}/filebeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/filebeat.yml" || fatal_error "Error while updating 'filebeat.yml'"
+ printf '\n - filebeat.yml'
+
+ mv "${TMP_FOLDER}/metricbeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/metricbeat.yml" || fatal_error "Error while updating 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
+ printf "\n => Deleting 'tmp' folder"
+ rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
+
+ # Add execution permissions
+ printf "\n => Adding permission to 'openvidu' program..."
+
+ chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" || fatal_error "Error while adding permission to 'openvidu' program"
+ printf '\n - openvidu'
+
+ chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_autodiscover.sh" || fatal_error "Error while adding permission to 'openvidu_autodiscover.sh' program"
+ printf '\n - openvidu_autodiscover.sh'
+
+ chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_drop.sh" || fatal_error "Error while adding permission to 'openvidu' openvidu_drop.sh"
+ printf '\n - openvidu_drop.sh'
+
+ chmod +x "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws/openvidu_launch_kms.sh" || fatal_error "Error while adding permission to 'openvidu_launch_kms.sh' program"
+ printf '\n - openvidu_launch_kms.sh'
+
+ # Define old mode: On Premise or Cloud Formation
+ OLD_MODE=$(grep -E "Installation Mode:.*$" "${ROLL_BACK_FOLDER}/docker-compose.yml" | awk '{ print $4,$5 }')
+ [ ! -z "${OLD_MODE}" ] && sed -i -r "s/Installation Mode:.+/Installation Mode: ${OLD_MODE}/" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml"
+
+ # In Aws, update AMI ID
+ AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
+ if [[ ! -z ${AWS_REGION} ]]; then
+ NEW_AMI_ID=$(curl https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_VERSION//v}.yaml --silent |
+ sed -n -e '/KMSAMIMAP:/,/Metadata:/ p' |
+ grep -A 1 ${AWS_REGION} | grep AMI | tr -d " " | cut -d":" -f2)
+ [[ -z ${NEW_AMI_ID} ]] && fatal_error "Error while getting new AWS_IMAGE_ID for Media Nodes"
+ sed -i "s/.*AWS_IMAGE_ID=.*/AWS_IMAGE_ID=${NEW_AMI_ID}/" "${OPENVIDU_PREVIOUS_FOLDER}/.env" || fatal_error "Error while updating new AWS_IMAGE_ID for Media Nodes"
+ fi
+
+
+ # Ready to use
+ printf '\n'
+ printf '\n'
+ printf '\n ================================================'
+ printf "\n Openvidu successfully upgraded to version %s" "${OPENVIDU_VERSION}"
+ printf '\n ================================================'
+ printf '\n'
+ printf "\n 1. A new file 'docker-compose.yml' has been created with the new OpenVidu %s services" "${OPENVIDU_VERSION}"
+ printf '\n'
+ printf "\n 2. The previous file '.env' remains intact, but a new file '.env-%s' has been created." "${OPENVIDU_VERSION}"
+ printf "\n Transfer any configuration you wish to keep in the upgraded version from '.env' to '.env-%s'." "${OPENVIDU_VERSION}"
+ printf "\n When you are OK with it, rename and leave as the only '.env' file of the folder the new '.env-%s'." "${OPENVIDU_VERSION}"
+ printf '\n'
+ printf "\n 3. If you were using Openvidu Call application, it has been automatically updated in file 'docker-compose.override.yml'."
+ printf "\n However, if you were using your own application, a file called 'docker-compose.override.yml-%s'" "${OPENVIDU_VERSION}"
+ printf "\n has been created with the latest version of Openvidu Call. If you don't plan to use it you can delete it."
+ printf '\n'
+ printf '\n 4. Start new version of Openvidu'
+ printf '\n $ ./openvidu start'
+ printf '\n'
+ printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
+ printf '\n'
+ printf '\n'
+ printf '\n'
+}
+
+# Check docker and docker-compose installation
+if ! command -v docker > /dev/null; then
+ echo "You don't have docker installed, please install it and re-run the command"
+ exit 0
+fi
+
+if ! command -v docker-compose > /dev/null; then
+ echo "You don't have docker-compose installed, please install it and re-run the command"
+ exit 0
+else
+ COMPOSE_VERSION=$(docker-compose version --short | sed "s/-rc[0-9]*//")
+ if ! printf '%s\n%s\n' "1.24" "$COMPOSE_VERSION" | sort -V -C; then
+ echo "You need a docker-compose version equal or higher than 1.24, please update your docker-compose and re-run the command"; \
+ exit 0
+ fi
+fi
+
+# Check type of installation
+if [[ ! -z "$1" && "$1" == "upgrade" ]]; then
+ upgrade_ov
+else
+ new_ov_installation
+fi
diff --git a/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu
new file mode 100755
index 00000000..b4229d5a
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/aws-asg-openvidu-server-pro/openvidu
@@ -0,0 +1,301 @@
+#!/bin/bash
+
+upgrade_ov() {
+ UPGRADE_SCRIPT_URL="https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/install_openvidu_pro_OVVERSION.sh"
+ HTTP_STATUS=$(curl -s -o /dev/null -I -w "%{http_code}" ${UPGRADE_SCRIPT_URL//OVVERSION/$1})
+
+ printf " => Upgrading Openvidu PRO to '%s' version" "$1"
+
+ if [ "$HTTP_STATUS" == "200" ]; then
+ printf "\n => Downloading and upgrading new version"
+ printf "\n"
+
+ curl --silent ${UPGRADE_SCRIPT_URL//OVVERSION/$1} | bash -s upgrade
+ else
+ printf "\n =======¡ERROR!======="
+ printf "\n Openvidu PRO Version '%s' not exist" "$1"
+ printf "\n"
+ exit 0
+ fi
+}
+
+collect_basic_information() {
+ LINUX_VERSION=$(lsb_release -d)
+ DOCKER_PS=$(docker ps)
+ DOCKER_VERSION=$(docker version --format '{{.Server.Version}}')
+ DOCKER_COMPOSE_VERSION=$(docker-compose version --short)
+ OV_FOLDER="${PWD}"
+ OV_VERSION=$(grep 'Openvidu Version:' "${OV_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
+ CONTAINERS=$(docker ps | awk '{if(NR>1) print $NF}')
+
+ if [ ! -z "$(grep -E '^ image: openvidu/openvidu-call:.*$' "${OV_FOLDER}/docker-compose.override.yml" | tr -d '[:space:]')" ]; then
+ OV_CALL_VERSION=$(grep -E 'Openvidu-Call Version:' "${OV_FOLDER}/docker-compose.override.yml" | awk '{ print $4 }')
+ fi
+ [ -z "${OV_CALL_VERSION}" ] && OV_CALL_VERSION="No present"
+
+ OV_TYPE_INSTALLATION=$(grep 'Installation Mode:' "${OV_FOLDER}/docker-compose.yml" | awk '{ print $4,$5 }')
+ TREE_OV_DIRECTORY=$(find "." ! -path '*/0/*' | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/")
+}
+
+version_ov() {
+ collect_basic_information
+
+ printf '\nOpenvidu Information:'
+ printf '\n'
+ printf '\n Installation Type: %s' "${OV_TYPE_INSTALLATION}"
+ printf '\n Openvidu Version: %s' "${OV_VERSION}"
+ printf '\n Openvidu Call Version: %s' "${OV_CALL_VERSION}"
+ printf '\n'
+ printf '\nSystem Information:'
+ printf '\n'
+ printf '\n Linux Version:'
+ printf '\n - %s' "${LINUX_VERSION}"
+ printf '\n Docker Version: %s' "${DOCKER_VERSION}"
+ printf '\n Docker Compose Version: %s' "${DOCKER_COMPOSE_VERSION}"
+ printf '\n'
+ printf '\nInstallation Information:'
+ printf '\n'
+ printf '\n Installation Folder: %s' "${OV_FOLDER}"
+ printf '\n Installation Folder Tree:'
+ printf '\n%s' "$(echo "${TREE_OV_DIRECTORY}" | sed -e 's/.//' -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
+ printf '\n'
+ printf '\nDocker Running Services:'
+ printf '\n'
+ printf '\n %s' "$(echo "${DOCKER_PS}" | sed -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
+ printf '\n'
+}
+
+generate_report() {
+ collect_basic_information
+
+ REPORT_CREATION_DATE=$(date +"%d-%m-%Y")
+ REPORT_CREATION_TIME=$(date +"%H:%M:%S")
+ REPORT_NAME="openvidu-report-${REPORT_CREATION_DATE}-$(date +"%H-%M").txt"
+ REPORT_OUPUT="${OV_FOLDER}/${REPORT_NAME}"
+
+ {
+ printf "\n ======================================="
+ printf "\n = REPORT INFORMATION ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n Creation Date: %s' "${REPORT_CREATION_DATE}"
+ printf '\n Creation Time: %s' "${REPORT_CREATION_TIME}"
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = OPENVIDU INFORMATION ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n Installation Type: %s' "${OV_TYPE_INSTALLATION}"
+ printf '\n Openvidu Version: %s' "${OV_VERSION}"
+ printf '\n Openvidu Call Version: %s' "${OV_CALL_VERSION}"
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = SYSTEM INFORMATION ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n Linux Version:'
+ printf '\n - %s' "${LINUX_VERSION}"
+ printf '\n Docker Version: %s' "${DOCKER_VERSION}"
+ printf '\n Docker Compose Version: %s' "${DOCKER_COMPOSE_VERSION}"
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = INSTALLATION INFORMATION ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n Installation Folder: %s' "${OV_FOLDER}"
+ printf '\n Installation Folder Tree:'
+ printf '\n%s' "$(echo "${TREE_OV_DIRECTORY}" | sed -e 's/.//' -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = DOCKER RUNNING SERVICES ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n %s' "$(echo "${DOCKER_PS}" | sed -e ':a' -e 'N;$!ba' -e 's/\n/\n\t/g')"
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = CONFIGURATION FILES ="
+ printf "\n ======================================="
+ printf '\n'
+ printf '\n ================ .env ================='
+ printf '\n'
+ printf '\n'
+
+ cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/ELASTICSEARCH_PASSWORD=.+/ELASTICSEARCH_PASSWORD=****/"
+
+ printf '\n'
+ printf '\n ========= docker-compose.yml =========='
+ printf '\n'
+ printf '\n'
+
+ cat "${OV_FOLDER}/docker-compose.yml"
+
+ printf '\n'
+ printf '\n ==== docker-compose.override.yml ===='
+ printf '\n'
+ printf '\n'
+
+ if [ -f "${OV_FOLDER}/docker-compose.override.yml" ]; then
+ cat < "${OV_FOLDER}/docker-compose.override.yml"
+ else
+ printf '\n The docker-compose.override.yml file is not present'
+ fi
+
+ printf '\n'
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n = LOGS ="
+ printf "\n ======================================="
+
+ for CONTAINER in $CONTAINERS
+ do
+ printf '\n'
+ printf "\n ---------------------------------------"
+ printf "\n %s" $CONTAINER
+ printf "\n ---------------------------------------"
+ printf '\n'
+ docker logs $CONTAINER
+ printf "\n ---------------------------------------"
+ printf '\n'
+ printf '\n'
+ done
+
+ printf "\n ======================================="
+ printf "\n = CONTAINER ENVS VARIABLES ="
+ printf "\n ======================================="
+
+ for CONTAINER in $CONTAINERS
+ do
+ printf '\n'
+ printf "\n ======================================="
+ printf "\n %s" $CONTAINER
+ printf "\n ---------------------------------------"
+ printf '\n'
+ docker exec $CONTAINER env
+ printf "\n ---------------------------------------"
+ printf '\n'
+ printf '\n'
+ done
+
+ } >> "${REPORT_OUPUT}" 2>&1
+
+ printf "\n Generation of the report completed with success"
+ printf "\n You can get your report at path '%s'" "${REPORT_OUPUT}"
+ printf "\n"
+}
+
+is_external_url() {
+ local URL=$1
+ if [[ -z "$URL" ]]; then
+ return 1
+ fi
+ if [[ "${URL}" == *"localhost"* ]] || [[ "${URL}" == *"127.0.0.1"* ]] || [[ "${URL}" == *"::1"* ]]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+start_openvidu() {
+ local RUN_LOCAL_ES
+ local RUN_LOCAL_KIBANA
+ local CONFIGURED_ELASTICSEARCH_HOST
+ local CONFIGURED_KIBANA_HOST
+ CONFIGURED_ELASTICSEARCH_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_ELASTICSEARCH_HOST | cut -d '=' -f2)
+ CONFIGURED_KIBANA_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_KIBANA_HOST | cut -d '=' -f2)
+ RUN_LOCAL_ES=true
+ RUN_LOCAL_KIBANA=true
+ if is_external_url "${CONFIGURED_ELASTICSEARCH_HOST}"; then
+ printf "Configured external elasticsearch: %s" "${CONFIGURED_ELASTICSEARCH_HOST}"
+ printf "\n"
+ RUN_LOCAL_ES=false
+ fi
+ if is_external_url "${CONFIGURED_KIBANA_HOST}"; then
+ printf "Configured external kibana: %s" "${CONFIGURED_KIBANA_HOST}"
+ printf "\n"
+ RUN_LOCAL_KIBANA=false
+ fi
+ docker-compose up -d \
+ $(if [ "${RUN_LOCAL_ES}" == "false" ]; then echo '--scale elasticsearch=0'; fi) \
+ $(if [ "${RUN_LOCAL_KIBANA}" == "false" ]; then echo '--scale kibana=0'; fi)
+}
+
+usage() {
+ printf "Usage: \n\t openvidu [command]"
+ printf "\n\nAvailable Commands:"
+ printf "\n\tstart\t\t\tStart all services"
+ printf "\n\tstop\t\t\tStop all services"
+ printf "\n\trestart\t\t\tRestart all stoped and running services"
+ printf "\n\tlogs\t\t\tShow openvidu-server logs"
+ printf "\n\tupgrade\t\t\tUpgrade to the lastest Openvidu version"
+ printf "\n\tupgrade [version]\tUpgrade to the specific Openvidu version"
+ printf "\n\tversion\t\t\tShow version of Openvidu Server"
+ printf "\n\treport\t\t\tGenerate a report with the current status of Openvidu"
+ printf "\n\thelp\t\t\tShow help for openvidu command"
+ printf "\n"
+}
+
+[[ -z "${FOLLOW_OPENVIDU_LOGS}" ]] && FOLLOW_OPENVIDU_LOGS=true
+
+case $1 in
+
+ start)
+ start_openvidu
+ if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
+ docker-compose logs -f openvidu-server
+ fi
+ ;;
+
+ stop)
+ docker-compose down
+ ;;
+
+ restart)
+ docker-compose down
+ start_openvidu
+ if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
+ docker-compose logs -f openvidu-server
+ fi
+ ;;
+
+ logs)
+ docker-compose logs -f openvidu-server
+ ;;
+
+ upgrade)
+ if [ -z "$2" ]; then
+ UPGRADE_VERSION="latest"
+ else
+ UPGRADE_VERSION="$2"
+ fi
+
+ read -r -p " You're about to update Openvidu PRO to '${UPGRADE_VERSION}' version. Are you sure? [y/N]: " response
+ case "$response" in
+ [yY][eE][sS]|[yY])
+ upgrade_ov "${UPGRADE_VERSION}"
+ ;;
+ *)
+ exit 0
+ ;;
+ esac
+ ;;
+
+ version)
+ version_ov
+ ;;
+
+ report)
+ read -r -p " You are about to generate a report on the current status of Openvidu, this may take some time. Do you want to continue? [y/N]: " response
+ case "$response" in
+ [yY][eE][sS]|[yY])
+ generate_report
+ ;;
+ *)
+ exit 0
+ ;;
+ esac
+ ;;
+
+ *)
+ usage
+ ;;
+esac
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/.env b/openvidu-server/deployments/pro/docker-compose/media-node/.env
deleted file mode 100644
index 8108613b..00000000
--- a/openvidu-server/deployments/pro/docker-compose/media-node/.env
+++ /dev/null
@@ -1,30 +0,0 @@
-# KMS Configuration
-# --------------------------
-
-# NOTE: This file doesn't need to quote assignment values, like most shells do.
-# All values are stored as-is, even if they contain spaces, so don't quote them.
-
-# Kurento Media Server image
-# --------------------------
-# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server-dev
-# Uncomment the next line and define this variable with KMS image that you want use
-# KMS_IMAGE=kurento/kurento-media-server:6.14.0
-
-# Kurento Media Server Level logs
-# -------------------------------
-# Uncomment the next line and define this variable to change
-# the verbosity level of the logs of KMS
-# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
-# KMS_DEBUG_LEVEL=3,Kurento*:4,kms*:4,sdp*:4,webrtc*:4,*rtpendpoint:4,rtp*handler:4,rtpsynchronizer:4,agnosticbin:4
-
-# Metricbeat ElasticSearch Image
-# -------------------------------
-# Docker hub kurento media server: https://www.docker.elastic.co/
-# Uncomment the next line and define this variable with METRICBEAT_IMAGE image that you want use
-# METRICBEAT_IMAGE=docker.elastic.co/beats/metricbeat:7.8.0
-
-# FileBeat ElasticSearch Image
-# -------------------------------
-# Docker hub kurento media server: https://www.docker.elastic.co/
-# Uncomment the next line and define this variable with FILEBEAT_IMAGE image that you want use
-# FILEBEAT_IMAGE=docker.elastic.co/beats/filebeat:7.8.0
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh b/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh
index 1dfaf54d..57c71e1a 100644
--- a/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh
@@ -3,5 +3,4 @@ echo "Creating dir for beats"
mkdir -p /opt/openvidu/beats
echo "Copying beat config files"
cp /beats/filebeat.yml /opt/openvidu/beats/filebeat.yml
-cp /beats/metricbeat-elasticsearch.yml /opt/openvidu/beats/metricbeat-elasticsearch.yml
-cp /beats/metricbeat-openvidu.yml /opt/openvidu/beats/metricbeat-openvidu.yml
\ No newline at end of file
+cp /beats/metricbeat-elasticsearch.yml /opt/openvidu/beats/metricbeat-elasticsearch.yml
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/beats/filebeat.yml b/openvidu-server/deployments/pro/docker-compose/media-node/beats/filebeat.yml
index a6e22122..7a54ffaf 100644
--- a/openvidu-server/deployments/pro/docker-compose/media-node/beats/filebeat.yml
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/beats/filebeat.yml
@@ -5,6 +5,14 @@ filebeat.inputs:
multiline.pattern: '^\d*:\d*:\d*'
multiline.negate: true
multiline.match: after
+ - type: log
+ paths:
+ - /opt/openvidu/kurento-logs/*.log
+ fields:
+ kurento-media-server: true
+ ip: ${MEDIA_NODE_IP}
+ cluster-id: ${CLUSTER_ID}
+ fields_under_root: true
processors:
- add_docker_metadata:
@@ -19,23 +27,21 @@ processors:
- add_fields:
fields:
ip: ${MEDIA_NODE_IP}
-
+ cluster-id: ${CLUSTER_ID}
output:
elasticsearch:
- hosts: ["${OPENVIDU_SERVER_PRO_IP}:9200"]
indices:
- index: "filebeat-kurento-%{+yyyy.MM.dd}"
when.or:
- - contains:
- container.image.name: kurento/kurento-media-server
+ - equals:
+ kurento-media-server: true
pipelines:
- pipeline: kurento-pipeline
when.or:
- - contains:
- container.image.name: kurento/kurento-media-server
+ - equals:
+ kurento-media-server: true
logging.json: true
logging.metrics.enabled: false
-setup.ilm.enabled: true
-setup.ilm.policy_name: "openvidu_cleanup_policy"
\ No newline at end of file
+setup.ilm.enabled: false
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-elasticsearch.yml b/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-elasticsearch.yml
index 0ddb27e9..5a2309d9 100644
--- a/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-elasticsearch.yml
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-elasticsearch.yml
@@ -1,14 +1,34 @@
-output:
- elasticsearch:
- hosts: ["${OPENVIDU_SERVER_PRO_IP}:9200"]
metricbeat.modules:
- module: system
- metricsets: [cpu]
- enabled: true
- period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}s
+ metricsets:
+ - cpu
+ #- diskio
+ - memory
+ - network
+ - filesystem
+ - fsstat
+ #- process
+ - process_summary
+ - uptime
+ filesystem.ignore_types: [nfs, smbfs, autofs, devtmpfs, devpts, hugetlbfs, tmpfs, sysfs, securityfs, cgroup2, cgroup, pstore, debugfs, configfs, fusectl, proc, fuse.lxcfs, squashfs]
processes: ['.*']
+ # process.include_top_n:
+ # by_cpu: 2
+ # by_memory: 2
+ processors:
+ - drop_event:
+ when:
+ or:
+ - regexp:
+ system.network.name: '^(veth|lo|docker|br-)($|)'
+ - regexp:
+ system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host)($|/)'
+ - regexp:
+ system.filesystem.mount_point: '^/hostfs/(sys|cgroup|proc|dev|etc|host)($|/)'
+ enabled: true
+ period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}0s
cpu.metrics: [normalized_percentages]
-fields: {ip: "${MEDIA_NODE_IP}"}
+fields: {ip: "${MEDIA_NODE_IP}", cluster-id: "${CLUSTER_ID}"}
pipeline:
queue.mem.events: 0
-setup.ilm.enabled: false
\ No newline at end of file
+setup.ilm.enabled: false
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml b/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml
deleted file mode 100644
index bbd5f726..00000000
--- a/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-output:
- logstash.hosts: ["${OPENVIDU_SERVER_PRO_IP}:5044"]
-metricbeat.modules:
- - module: system
- metricsets: [cpu]
- enabled: true
- period: ${OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL}s
- processes: ['.*']
- cpu.metrics: [normalized_percentages]
-fields: {ip: "${MEDIA_NODE_IP}"}
-pipeline:
- queue.mem.events: 0
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/docker-compose.yml b/openvidu-server/deployments/pro/docker-compose/media-node/docker-compose.yml
index fe9ab5b4..4f1f4119 100644
--- a/openvidu-server/deployments/pro/docker-compose/media-node/docker-compose.yml
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/docker-compose.yml
@@ -6,7 +6,7 @@
#
# This docker-compose file coordinates all services of OpenVidu CE Platform.
#
-# Openvidu Version: 2.15.0
+# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@@ -16,20 +16,24 @@ version: '3.1'
services:
media-node-controller:
- image: openvidu/media-node-controller:1.0.0
+ image: openvidu/media-node-controller:3.0.0-dev4
restart: always
ulimits:
core: -1
entrypoint: ['/bin/sh', '-c', '/beats/copy_config_files.sh && /usr/local/bin/entrypoint.sh']
environment:
- - KMS_IMAGE=${KMS_IMAGE:-kurento/kurento-media-server:6.14.0}
- - KMS_DEBUG_LEVEL=${KMS_DEBUG_LEVEL:-}
- - METRICBEAT_IMAGE=${METRICBEAT_IMAGE:-docker.elastic.co/beats/metricbeat:7.8.0}
- - FILEBEAT_IMAGE=${FILEBEAT_IMAGE:-docker.elastic.co/beats/filebeat:7.8.0}
+ - KMS_IMAGE=kurento/kurento-media-server:6.15.0
+ - METRICBEAT_IMAGE=docker.elastic.co/beats/metricbeat-oss:7.8.0
+ - FILEBEAT_IMAGE=docker.elastic.co/beats/filebeat-oss:7.8.0
+ - OPENVIDU_RECORDING_IMAGE=openvidu/openvidu-recording:2.17.0-dev1
ports:
- 3000:3000
volumes:
- /opt/openvidu/recordings:/opt/openvidu/recordings
- /opt/openvidu/beats:/opt/openvidu/beats
- /var/run/docker.sock:/var/run/docker.sock
+ - /opt/openvidu/kurento-logs:/opt/openvidu/kurento-logs
- ./beats:/beats
+ logging:
+ options:
+ max-size: "100M"
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/install_media_node.sh b/openvidu-server/deployments/pro/docker-compose/media-node/install_media_node.sh
index 45e0eb89..403b801e 100755
--- a/openvidu-server/deployments/pro/docker-compose/media-node/install_media_node.sh
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/install_media_node.sh
@@ -2,8 +2,16 @@
MEDIA_NODE_FOLDER=kms
MEDIA_NODE_VERSION=master
+OPENVIDU_UPGRADABLE_VERSION="2.16"
BEATS_FOLDER=${MEDIA_NODE_FOLDER}/beats
DOWNLOAD_URL=https://raw.githubusercontent.com/OpenVidu/openvidu/${MEDIA_NODE_VERSION}
+IMAGES=(
+ "kurento-media-server"
+ "docker.elastic.co/beats/filebeat"
+ "docker.elastic.co/beats/metricbeat"
+ "openvidu/media-node-controller"
+)
+
fatal_error() {
printf "\n =======¡ERROR!======="
printf "\n %s" "$1"
@@ -11,6 +19,27 @@ fatal_error() {
exit 0
}
+docker_command_by_container_image() {
+ IMAGE_NAME=$1
+ COMMAND=$2
+ if [[ ! -z "${IMAGE_NAME}" ]]; then
+ CONTAINERS=$(docker ps -a | grep "${IMAGE_NAME}" | awk '{print $1}')
+ for CONTAINER_ID in ${CONTAINERS[@]}; do
+ if [[ ! -z "${CONTAINER_ID}" ]] && [[ ! -z "${COMMAND}" ]]; then
+ bash -c "docker ${COMMAND} ${CONTAINER_ID}"
+ fi
+ done
+ fi
+}
+
+
+stop_containers() {
+ printf "Stopping containers..."
+ for IMAGE in ${IMAGES[@]}; do
+ docker_command_by_container_image "${IMAGE}" "rm -f"
+ done
+}
+
new_media_node_installation() {
printf '\n'
printf '\n ======================================='
@@ -33,10 +62,6 @@ new_media_node_installation() {
--output "${MEDIA_NODE_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/.env \
- --output "${MEDIA_NODE_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
- printf '\n - .env'
-
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/media_node \
--output "${MEDIA_NODE_FOLDER}/media_node" || fatal_error "Error when downloading the file 'media_node'"
printf '\n - media_node'
@@ -49,10 +74,6 @@ new_media_node_installation() {
--output "${BEATS_FOLDER}/metricbeat-elasticsearch.yml" || fatal_error "Error when downloading the file 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml \
- --output "${BEATS_FOLDER}/metricbeat-openvidu.yml" || fatal_error "Error when downloading the file 'metricbeat-openvidu.yml'"
- printf '\n - metricbeat-openvidu.yml'
-
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh \
--output "${BEATS_FOLDER}/copy_config_files.sh" || fatal_error "Error when downloading the file 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@@ -68,12 +89,14 @@ new_media_node_installation() {
# Pull images
printf "\n => Pulling images...\n"
cd "${MEDIA_NODE_FOLDER}" || fatal_error "Error when moving to '${MEDIA_NODE_FOLDER}' folder"
- KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | sed 's/\(^.*KMS_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
- METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | sed 's/\(^.*METRICBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
- FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | sed 's/\(^.*FILEBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
+ KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | cut -d"=" -f2)
+ METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | cut -d"=" -f2)
+ FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | cut -d"=" -f2)
+ OPENVIDU_RECORDING_IMAGE=$(cat docker-compose.yml | grep OPENVIDU_RECORDING_IMAGE | cut -d"=" -f2)
docker pull $KMS_IMAGE || fatal "Error while pulling docker image: $KMS_IMAGE"
docker pull $METRICBEAT_IMAGE || fatal "Error while pulling docker image: $METRICBEAT_IMAGE"
docker pull $FILEBEAT_IMAGE || fatal "Error while pulling docker image: $FILEBEAT_IMAGE"
+ docker pull $OPENVIDU_RECORDING_IMAGE || fatal "Error while pulling docker image: $OPENVIDU_RECORDING_IMAGE"
docker-compose pull | true
# Ready to use
@@ -81,16 +104,28 @@ new_media_node_installation() {
printf '\n ======================================='
printf "\n Media Node successfully installed."
printf '\n ======================================='
- printf "\n"
+ printf '\n'
printf '\n 1. Go to kms folder:'
printf '\n $ cd kms'
- printf "\n"
+ printf '\n'
printf '\n 2. Start Media Node Controller'
printf '\n $ ./media_node start'
printf '\n'
- printf "\n For more information, check:"
- printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
+ printf '\n 3. This will run a service at port 3000 which OpenVidu will use to deploy necessary containers.'
+ printf '\n Add the private ip of this media node in "KMS_URIS=[]" in OpenVidu Pro machine'
+ printf '\n in file located at "/opt/openvidu/.env" with this format:'
+ printf '\n ...'
+ printf '\n KMS_URIS=["ws://:8888/kurento"]'
+ printf '\n ...'
+ printf '\n You can also add this node from inspector'
printf '\n'
+ printf '\n 4. Start or restart OpenVidu Pro and all containers will be provisioned'
+ printf '\n automatically to all the media nodes configured in "KMS_URIS"'
+ printf '\n More info about Media Nodes deployment here:'
+ printf "\n --> https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#set-the-number-of-media-nodes-on-startup"
+ printf '\n'
+ printf '\n'
+ printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
exit 0
}
@@ -123,10 +158,13 @@ upgrade_media_node() {
# Uppgrade Media Node
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${MEDIA_NODE_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
- [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.14.0
+ [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
+ if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
+ fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
+ fi
printf '\n'
printf '\n ======================================='
@@ -151,10 +189,6 @@ upgrade_media_node() {
--output "${TMP_FOLDER}/docker-compose.yml" || fatal_error "Error when downloading the file 'docker-compose.yml'"
printf '\n - docker-compose.yml'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/.env \
- --output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
- printf '\n - .env'
-
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/media_node \
--output "${TMP_FOLDER}/media_node" || fatal_error "Error when downloading the file 'media_node'"
printf '\n - media_node'
@@ -167,10 +201,6 @@ upgrade_media_node() {
--output "${TMP_FOLDER}/metricbeat-elasticsearch.yml" || fatal_error "Error when downloading the file 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
- curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/metricbeat-openvidu.yml \
- --output "${TMP_FOLDER}/metricbeat-openvidu.yml" || fatal_error "Error when downloading the file 'metricbeat-openvidu.yml'"
- printf '\n - metricbeat-openvidu.yml'
-
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/media-node/beats/copy_config_files.sh \
--output "${TMP_FOLDER}/copy_config_files.sh" || fatal_error "Error when downloading the file 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@@ -182,16 +212,24 @@ upgrade_media_node() {
printf "\n => Moving to 'tmp' folder..."
printf '\n'
+ cd "${TMP_FOLDER}" || fatal_error "Error when moving to '${TMP_FOLDER}' folder"
+
+ printf '\n => Stoping Media Node containers...'
+ printf '\n'
+ sleep 1
+
+ stop_containers
# Pull images
printf "\n => Pulling images...\n"
- cd "${TMP_FOLDER}" || fatal_error "Error when moving to '${TMP_FOLDER}' folder"
- KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | sed 's/\(^.*KMS_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
- METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | sed 's/\(^.*METRICBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
- FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | sed 's/\(^.*FILEBEAT_IMAGE:-\)\(.*\)\(\}.*$\)/\2/')
+ KMS_IMAGE=$(cat docker-compose.yml | grep KMS_IMAGE | cut -d"=" -f2)
+ METRICBEAT_IMAGE=$(cat docker-compose.yml | grep METRICBEAT_IMAGE | cut -d"=" -f2)
+ FILEBEAT_IMAGE=$(cat docker-compose.yml | grep FILEBEAT_IMAGE | cut -d"=" -f2)
+ OPENVIDU_RECORDING_IMAGE=$(cat docker-compose.yml | grep OPENVIDU_RECORDING_IMAGE | cut -d"=" -f2)
docker pull $KMS_IMAGE || fatal "Error while pulling docker image: $KMS_IMAGE"
docker pull $METRICBEAT_IMAGE || fatal "Error while pulling docker image: $METRICBEAT_IMAGE"
docker pull $FILEBEAT_IMAGE || fatal "Error while pulling docker image: $FILEBEAT_IMAGE"
+ docker pull $OPENVIDU_RECORDING_IMAGE || fatal "Error while pulling docker image: $OPENVIDU_RECORDING_IMAGE"
docker-compose pull | true
printf '\n => Stoping Media Node...'
@@ -216,14 +254,8 @@ upgrade_media_node() {
mv "${MEDIA_NODE_PREVIOUS_FOLDER}/media_node" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - media_node'
- mv "${MEDIA_NODE_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
- printf '\n - readme.md'
-
- mv "${MEDIA_NODE_PREVIOUS_FOLDER}/nginx_conf" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'nginx_conf'"
- printf '\n - nginx_conf'
-
- cp "${MEDIA_NODE_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
- printf '\n - .env'
+ mv "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats' folder"
+ printf '\n - beats'
# Move tmp files to Openvidu
printf '\n => Updating files:'
@@ -231,9 +263,6 @@ upgrade_media_node() {
mv "${TMP_FOLDER}/docker-compose.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'docker-compose.yml'"
printf '\n - docker-compose.yml'
- mv "${TMP_FOLDER}/.env" "${MEDIA_NODE_PREVIOUS_FOLDER}/.env-${MEDIA_NODE_VERSION}" || fatal_error "Error while moving previous '.env'"
- printf '\n - .env-%s' "${MEDIA_NODE_VERSION}"
-
mv "${TMP_FOLDER}/media_node" "${MEDIA_NODE_PREVIOUS_FOLDER}" || fatal_error "Error while updating 'media_node'"
printf '\n - media_node'
@@ -245,9 +274,6 @@ upgrade_media_node() {
mv "${TMP_FOLDER}/metricbeat-elasticsearch.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'metricbeat-elasticsearch.yml'"
printf '\n - metricbeat-elasticsearch.yml'
- mv "${TMP_FOLDER}/metricbeat-openvidu.yml" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'metricbeat-openvidu.yml'"
- printf '\n - metricbeat-openvidu.yml'
-
mv "${TMP_FOLDER}/copy_config_files.sh" "${MEDIA_NODE_PREVIOUS_FOLDER}/beats" || fatal_error "Error while updating 'copy_config_files.sh'"
printf '\n - copy_config_files.sh'
@@ -275,17 +301,27 @@ upgrade_media_node() {
printf '\n'
printf "\n 1. A new file 'docker-compose.yml' has been created with the new OpenVidu %s services" "${OPENVIDU_VERSION}"
printf '\n'
- printf "\n 2. The previous file '.env' remains intact, but a new file '.env-%s' has been created." "${OPENVIDU_VERSION}"
- printf "\n Transfer any configuration you wish to keep in the upgraded version from '.env' to '.env-%s'." "${OPENVIDU_VERSION}"
- printf "\n When you are OK with it, rename and leave as the only '.env' file of the folder the new '.env-%s'." "${OPENVIDU_VERSION}"
+ printf "\n 2. This new version %s does not need any .env file. Everything is configured from OpenVidu Pro" "${OPENVIDU_VERSION}"
printf '\n'
printf '\n 3. Start new version of Media Node'
printf '\n $ ./media_node start'
printf '\n'
+ printf '\n 4. This will run a service at port 3000 which OpenVidu will use to deploy necessary containers.'
+ printf '\n Add the private ip of this media node in "KMS_URIS=[]" in OpenVidu Pro machine'
+ printf '\n in file located at "/opt/openvidu/.env" with this format:'
+ printf '\n ...'
+ printf '\n KMS_URIS=["ws://:8888/kurento"]'
+ printf '\n ...'
+ printf '\n You can also add Media Nodes from inspector'
+ printf '\n'
+ printf '\n 5. Start or restart OpenVidu Pro and all containers will be provisioned'
+ printf '\n automatically to all the media nodes configured in "KMS_URIS"'
+ printf '\n More info about Media Nodes deployment here:'
+ printf "\n --> https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#set-the-number-of-media-nodes-on-startup"
+ printf '\n'
+ printf '\n'
printf "\n If you want to rollback, all the files from the previous installation have been copied to folder '.old-%s'" "${OPENVIDU_PREVIOUS_VERSION}"
printf '\n'
- printf '\n'
- printf '\n'
}
# Check docker and docker-compose installation
diff --git a/openvidu-server/deployments/pro/docker-compose/media-node/media_node b/openvidu-server/deployments/pro/docker-compose/media-node/media_node
index b55a4122..8b142871 100755
--- a/openvidu-server/deployments/pro/docker-compose/media-node/media_node
+++ b/openvidu-server/deployments/pro/docker-compose/media-node/media_node
@@ -1,5 +1,41 @@
#!/bin/bash
+# Deployed images in media-node
+IMAGES=(
+ "kurento-media-server"
+ "docker.elastic.co/beats/filebeat"
+ "docker.elastic.co/beats/metricbeat"
+ "openvidu/media-node-controller"
+)
+
+docker_command_by_container_image() {
+ IMAGE_NAME=$1
+ COMMAND=$2
+ if [[ ! -z "${IMAGE_NAME}" ]]; then
+ CONTAINERS=$(docker ps -a | grep "${IMAGE_NAME}" | awk '{print $1}')
+ for CONTAINER_ID in ${CONTAINERS[@]}; do
+ if [[ ! -z "${CONTAINER_ID}" ]] && [[ ! -z "${COMMAND}" ]]; then
+ bash -c "docker ${COMMAND} ${CONTAINER_ID}"
+ fi
+ done
+ fi
+}
+
+
+stop_containers() {
+ printf "Stopping containers..."
+ for IMAGE in ${IMAGES[@]}; do
+ docker_command_by_container_image "${IMAGE}" "rm -f"
+ done
+}
+kurento_logs() {
+ if [[ "$1" == "-f" ]]; then
+ tail -f /opt/openvidu/kurento-logs/*.log
+ else
+ cat /opt/openvidu/kurento-logs/*.log
+ fi
+}
+
upgrade_media_node() {
UPGRADE_SCRIPT_URL="https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/install_media_node_OVVERSION.sh"
HTTP_STATUS=$(curl -s -o /dev/null -I -w "%{http_code}" ${UPGRADE_SCRIPT_URL//OVVERSION/$1})
@@ -140,6 +176,16 @@ generate_report() {
printf '\n'
done
+ printf '\n'
+ printf "\n ---------------------------------------"
+ printf "\n KMS"
+ printf "\n ---------------------------------------"
+ printf '\n'
+ kurento_logs
+ printf "\n ---------------------------------------"
+ printf '\n'
+ printf '\n'
+
printf "\n ======================================="
printf "\n = CONTAINER ENVS VARIABLES ="
printf "\n ======================================="
@@ -170,7 +216,8 @@ usage() {
printf "\n\tstart\t\t\tStart media node service"
printf "\n\tstop\t\t\tStop media node service"
printf "\n\trestart\t\t\tRestart media node service"
- printf "\n\tlogs\t\t\tShow media node logs"
+ printf "\n\tlogs [-f]\t\tShow media-node-controller logs."
+ printf "\n\tkms-logs [-f]\t\tShow kms logs"
printf "\n\tupgrade\t\t\tUpgrade to the lastest Media Node version"
printf "\n\tupgrade [version]\tUpgrade to the specific Media Node version"
printf "\n\tversion\t\t\tShow version of Media Node"
@@ -179,15 +226,6 @@ usage() {
printf "\n"
}
-stop_containers() {
- CONTAINERS=$(docker ps | awk '{if(NR>1) print $NF}')
- for CONTAINER in $CONTAINERS
- do
- [ "$(docker ps -a | grep ${CONTAINER})" ] && docker stop ${CONTAINER}
- done
-
-}
-
case $1 in
start)
@@ -208,7 +246,18 @@ case $1 in
;;
logs)
- docker-compose logs -f media-node-controller
+ case $2 in
+ "-f")
+ docker-compose logs -f media-node-controller
+ ;;
+ *)
+ docker-compose logs media-node-controller
+ ;;
+ esac
+ ;;
+
+ kms-logs)
+ kurento_logs $2
;;
upgrade)
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env
index 458697fa..911977b2 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env
@@ -42,6 +42,22 @@ LETSENCRYPT_EMAIL=user@example.com
# SDKs, REST clients and browsers will have to connect to this port
# HTTPS_PORT=443
+# Old paths are considered now deprecated, but still supported by default.
+# OpenVidu Server will log a WARN message every time a deprecated path is called, indicating
+# the new path that should be used instead. You can set property SUPPORT_DEPRECATED_API=false
+# to stop allowing the use of old paths.
+# Default value is true
+# SUPPORT_DEPRECATED_API=true
+
+# If true request to with www will be redirected to non-www requests
+# Default value is false
+# REDIRECT_WWW=false
+
+# How many workers to configure in nginx proxy.
+# The more workers, the more requests will be handled
+# Default value is 10240
+# WORKER_CONNECTIONS=10240
+
# Access restrictions
# In this section you will be able to restrict the IPs from which you can access to
# Openvidu API and the Administration Panel
@@ -71,6 +87,12 @@ OPENVIDU_PRO_CLUSTER_MODE=manual
# Possibles values: aws, on_premise
OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
+# Unique identifier of your cluster. Each OpenVidu Server Pro instance corresponds to one cluster.
+# You can launch as many clusters as you want with your license key.
+# Cluster ID will always be stored to disk so restarting OpenVidu Server Pro will keep the same previous cluster ID
+# if this configuration parameter is not given a distinct value.
+# OPENVIDU_PRO_CLUSTER_ID=
+
# The desired number of Media Nodes on startup. First the autodiscovery process is performed.
# If there are too many Media Nodes after that, they will be dropped until this number is reached.
# If there are not enough, more will be launched.
@@ -85,7 +107,6 @@ OPENVIDU_PRO_CLUSTER_ENVIRONMENT=on_premise
# Type: number >= 0
# OPENVIDU_PRO_CLUSTER_LOAD_INTERVAL=
-
# Whether to enable or disable autoscaling. With autoscaling the number of Media Nodes will
# be automatically adjusted according to existing load
# Values: true | false
@@ -119,13 +140,55 @@ OPENVIDU_PRO_CLUSTER_AUTOSCALING=false
# (and therefore distribution of load) among all available Media Nodes
OPENVIDU_PRO_CLUSTER_LOAD_STRATEGY=streams
+# Whether to enable or disable Network Quality API. You can monitor and
+# warn users about the quality of their networks with this feature
+# OPENVIDU_PRO_NETWORK_QUALITY=false
+
+# If OPENVIDU_PRO_NETWORK_QUALITY=true, how often the network quality
+# algorithm will be invoked for each user, in seconds
+# OPENVIDU_PRO_NETWORK_QUALITY_INTERVAL=5
+
# Max days until delete indexes in state of rollover on Elasticsearch
# Type number >= 0
+# Default Value is 15
# OPENVIDU_PRO_ELASTICSEARCH_MAX_DAYS_DELETE=
-# Private IP of OpenVidu Server Pro
-# For example 192.168.1.101
-# OPENVIDU_PRO_PRIVATE_IP=
+# If you have an external Elasticsearch and Kibana already running, put here the url to elasticsearch and kibana services.
+# It is very important that both url have the port specified in the url.
+# If you want to use the deployed Elasticsearch and Kibana locally, keep these variables commented.
+#OPENVIDU_PRO_ELASTICSEARCH_HOST=
+#OPENVIDU_PRO_KIBANA_HOST=
+
+# Where to store recording files. Can be 'local' (local storage) or 's3' (AWS bucket).
+# You will need to define a OPENVIDU_PRO_AWS_S3_BUCKET if you use it.
+#OPENVIDU_PRO_RECORDING_STORAGE=
+
+# S3 Bucket where to store recording files. May include paths to allow navigating
+# folder structures inside the bucket. This property is only taken into account
+# if OPENVIDU_PRO_RECORDING_STORAGE=s3
+#OPENVIDU_PRO_AWS_S3_BUCKET=
+
+# If OPENVIDU_PRO_RECORDING_STORAGE=s3, the collection of HTTP header values that the internal AWS client will use during
+# the upload process. The property is a key-value map of strings, following the format of a JSON object. For example, for applying
+# server-side encryption with AES-256, this header is mandatory: {"x-amz-server-side-encryption":"AES256"}.
+# The list of available headers can be found here: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/Headers.html
+# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+#OPENVIDU_PRO_AWS_S3_HEADERS=
+
+# If you're instance has a role which has access to read
+# and write into the s3 bucket, you don't need this parameter
+# OPENVIDU_PRO_AWS_ACCESS_KEY=
+
+# AWS credentials secret key from OPENVIDU_PRO_AWS_ACCESS_KEY. This property is only
+# taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+# If you're instance has a role which has access to read
+# and write into the s3 bucket, you don't need this parameter
+# OPENVIDU_PRO_AWS_SECRET_KEY=
+
+# AWS region in which the S3 bucket is located (e.g. eu-west-1). If not provided,
+# the region will try to be discovered automatically, although this is not always possible.
+# This property is only taken into account if OPENVIDU_PRO_RECORDING_STORAGE=s3
+# OPENVIDU_PRO_AWS_REGION=
# Whether to enable recording module or not
OPENVIDU_RECORDING=false
@@ -178,12 +241,14 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
-# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true
-# when a codec can not be forced, transcoding will be allowed
-# OPENVIDU_FORCED_CODEC=VP8
+# All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true
+# when a codec can not be forced, transcoding will be allowed
+# Default value is VP8
+# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
-# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied
-# OPENVIDU_ALLOW_TRANSCODING=false
+# Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied
+# Default value is false
+# OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
# true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false
@@ -227,17 +292,44 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr
# RECOMENDED VALUES: INFO for normal logs DEBUG for more verbose logs
# OV_CE_DEBUG_LEVEL=INFO
-# Java Options
+# OpenVidu Java Options
# --------------------------
# Uncomment the next line and define this to add options to java command
# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
# JAVA_OPTIONS=-Xms2048m -Xmx4096m
-# Kibana And ElasticSearch Configuration
+# ElasticSearch Java Options
# --------------------------
-# Kibana dashboard configuration (Credentials)
-KIBANA_USER=kibanaadmin
-KIBANA_PASSWORD=
+# Uncomment the next line and define this to add options to java command of Elasticsearch
+# Documentation: https://docs.oracle.com/cd/E37116_01/install.111210/e23737/configuring_jvm.htm#OUDIG00058
+# By default ElasticSearch is configured to use "-Xms2g -Xmx2g" as Java Min and Max memory heap allocation
+# ES_JAVA_OPTS=-Xms2048m -Xmx4096m
+
+# Kibana And ElasticSearch Credentials Configuration
+# --------------------------
+# Kibana And ElasticSearch Basic Auth configuration (Credentials)
+# This credentials will aso be valid for Kibana dashboard
+ELASTICSEARCH_USERNAME=elasticadmin
+ELASTICSEARCH_PASSWORD=
+
+# Media Node Configuration
+# --------------------------
+# You can add any KMS environment variable as described in the
+# documentation of the docker image: https://hub.docker.com/r/kurento/kurento-media-server
+# If you want to add an environment variable to KMS, you must add a variable using this prefix: 'KMS_DOCKER_ENV_',
+# followed by the environment variable you want to setup.
+# For example if you want to setup KMS_MIN_PORT to 50000, it would be KMS_DOCKER_ENV_KMS_MIN_PORT=50000
+
+# Docker hub kurento media server: https://hub.docker.com/r/kurento/kurento-media-server
+# Uncomment the next line and define this variable with KMS image that you want use
+# By default, KMS_IMAGE is defined in media nodes and it does not need to be specified unless
+# you want to use a specific version of KMS
+# KMS_IMAGE=kurento/kurento-media-server:6.15.0
+
+# Uncomment the next line and define this variable to change
+# the verbosity level of the logs of KMS
+# Documentation: https://doc-kurento.readthedocs.io/en/stable/features/logging.html
+# KMS_DOCKER_ENV_GST_DEBUG=
# Cloudformation configuration
# --------------------------
@@ -250,3 +342,5 @@ KIBANA_PASSWORD=
#AWS_SECURITY_GROUP=
#AWS_STACK_ID=
#AWS_STACK_NAME=
+#AWS_CLI_DOCKER_TAG=
+#AWS_VOLUME_SIZE=
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/filebeat.yml b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/filebeat.yml
index c84977e8..369daa45 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/filebeat.yml
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/filebeat.yml
@@ -13,20 +13,36 @@ processors:
fields: ["message"]
target: "json"
overwrite_keys: true
+ - drop_event:
+ when.or:
+ - contains:
+ container.image.name: openvidu/openvidu-coturn
+ - contains:
+ container.image.name: docker.elastic.co/elasticsearch/elasticsearch
+ - contains:
+ container.image.name: docker.elastic.co/kibana/kibana
+ - contains:
+ container.image.name: docker.elastic.co/beats/filebeat-oss
+ - contains:
+ container.image.name: docker.elastic.co/beats/metricbeat-oss
+ - add_fields:
+ fields:
+ cluster-id: ${OPENVIDU_PRO_CLUSTER_ID:undefined}
output:
elasticsearch:
- hosts: ["elasticsearch:9200"]
+ hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
indices:
- index: "filebeat-redis-%{+yyyy.MM.dd}"
when.or:
- contains:
container.image.name: openvidu/openvidu-redis
- - index: "filebeat-coturn-%{+yyyy.MM.dd}"
+ - index: "filebeat-nginx-%{+yyyy.MM.dd}"
when.or:
- contains:
- container.image.name: openvidu/openvidu-coturn
+ container.image.name: openvidu/openvidu-proxy
logging.json: true
-logging.metrics.enabled: false
\ No newline at end of file
+logging.metrics.enabled: false
+setup.ilm.enabled: false
\ No newline at end of file
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml
new file mode 100644
index 00000000..09a68d9a
--- /dev/null
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml
@@ -0,0 +1,10 @@
+metricbeat.modules:
+- module: nginx
+ metricsets: ["stubstatus"]
+ enabled: true
+ period: 10s
+ hosts: ["http://127.0.0.1"]
+ server_status_path: "nginx_status"
+output:
+ elasticsearch:
+ hosts: ["${OPENVIDU_PRO_ELASTICSEARCH_HOST}"]
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh
index 0eb86058..8689f3b7 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_autodiscover.sh
@@ -7,7 +7,7 @@ DEBUG=${DEBUG:-false}
OUTPUT=$(mktemp -t openvidu-autodiscover-XXX --suffix .json)
-docker run --rm amazon/aws-cli:2.0.7 ec2 describe-instances \
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 describe-instances \
--output text \
--filters "Name=instance-state-name,Values=running" \
"Name=tag:ov-cluster-member,Values=kms" \
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_drop.sh b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_drop.sh
index 27cc8d76..8d585480 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_drop.sh
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_drop.sh
@@ -8,4 +8,4 @@ DEBUG=${DEBUG:-false}
ID=$1
[ -z "${ID}" ] && { echo "Must provide instance ID"; exit 1; }
-docker run --rm amazon/aws-cli:2.0.7 ec2 terminate-instances --instance-ids ${ID} --output json
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 terminate-instances --instance-ids ${ID} --output json
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh
index 186ce5a0..48b92bf7 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/cluster/aws/openvidu_launch_kms.sh
@@ -23,7 +23,7 @@ exit_on_error () {
"UnauthorizedOperation")
MSG_COD=$(cat ${ERROUTPUT} | awk -F: '{ print $3 }')
- MSG_DEC=$(docker run --rm amazon/aws-cli:2.0.7 sts decode-authorization-message --encoded-message ${MSG_COD})
+ MSG_DEC=$(docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} sts decode-authorization-message --encoded-message ${MSG_COD})
echo -e "Unauthorized " $(cat ${MSG_DEC}) >&2
exit 1
@@ -35,16 +35,17 @@ exit_on_error () {
esac
}
-docker run --rm amazon/aws-cli:2.0.7 ec2 run-instances \
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 run-instances \
--image-id ${AWS_IMAGE_ID} --count 1 \
--instance-type ${AWS_INSTANCE_TYPE} \
--key-name ${AWS_KEY_NAME} \
--subnet-id ${AWS_SUBNET_ID} \
--tag-specifications "ResourceType=instance,Tags=[{Key='Name',Value='Kurento Media Server'},{Key='ov-cluster-member',Value='kms'},{Key='ov-stack-name',Value='${AWS_STACK_NAME}'},{Key='ov-stack-region',Value='${AWS_DEFAULT_REGION}'}]" \
--iam-instance-profile Name="OpenViduInstanceProfile-${AWS_STACK_NAME}-${AWS_DEFAULT_REGION}" \
+ --block-device-mappings "DeviceName=/dev/sda1,Ebs={DeleteOnTermination=True,VolumeType='gp2',VolumeSize='${AWS_VOLUME_SIZE}'}" \
--security-group-ids ${AWS_SECURITY_GROUP} > ${OUTPUT} 2> ${ERROUTPUT}
-docker run --rm amazon/aws-cli:2.0.7 ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
+docker run --rm amazon/aws-cli:${AWS_CLI_DOCKER_TAG} ec2 wait instance-running --instance-ids $(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .InstanceId')
# Generating the output
KMS_IP=$(cat ${OUTPUT} | jq --raw-output ' .Instances[] | .NetworkInterfaces[0] | .PrivateIpAddress')
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.override.yml b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.override.yml
index c52129f9..d4796d20 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.override.yml
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.override.yml
@@ -9,11 +9,11 @@ services:
#
# Default Application
#
- # Openvidu-Call Version: 2.15.0
+ # Openvidu-Call Version: 2.16.0
#
# --------------------------------------------------------------
app:
- image: openvidu/openvidu-call:2.15.0
+ image: openvidu/openvidu-call:2.16.0
restart: on-failure
network_mode: host
environment:
@@ -21,3 +21,6 @@ services:
- OPENVIDU_URL=http://localhost:5443
- OPENVIDU_SECRET=${OPENVIDU_SECRET}
- CALL_OPENVIDU_CERTTYPE=${CERTIFICATE_TYPE}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml
index 5cd67732..3eb8d3cd 100644
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/docker-compose.yml
@@ -7,11 +7,11 @@
# Application based on OpenVidu should be specified in
# docker-compose.override.yml file
#
-# This docker-compose file coordinates all services of OpenVidu CE Platform.
+# This docker-compose file coordinates all services of OpenVidu Pro Platform
#
# This file will be overridden when update OpenVidu Platform
#
-# Openvidu Version: 2.15.1
+# Openvidu Version: 2.16.0
#
# Installation Mode: On Premises
#
@@ -22,10 +22,10 @@ version: '3.1'
services:
openvidu-server:
- image: openvidu/openvidu-server-pro:2.15.1
+ image: openvidu/openvidu-server-pro:2.17.0-dev5
restart: on-failure
network_mode: host
- entrypoint: ['/bin/bash', '-c', 'export COTURN_IP=`/usr/local/bin/discover_my_public_ip.sh`; /usr/local/bin/entrypoint.sh']
+ entrypoint: ['/usr/local/bin/entrypoint.sh']
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${OPENVIDU_RECORDING_PATH}:${OPENVIDU_RECORDING_PATH}
@@ -41,21 +41,28 @@ services:
- KMS_URIS=[]
- COTURN_REDIS_IP=127.0.0.1
- COTURN_REDIS_PASSWORD=${OPENVIDU_SECRET}
+ - COTURN_IP=${COTURN_IP:-auto-ipv4}
- OPENVIDU_PRO_CLUSTER=true
- - OPENVIDU_PRO_KIBANA_HOST=http://127.0.0.1/kibana
- - OPENVIDU_PRO_ELASTICSEARCH_HOST=http://127.0.0.1:9200
- - WAIT_KIBANA_URL=http://127.0.0.1:5601/api/status
+ - OPENVIDU_PRO_KIBANA_HOST=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
+ - WAIT_KIBANA_URL=${OPENVIDU_PRO_KIBANA_HOST:-http://127.0.0.1/kibana}
- DOTENV_PATH=${PWD}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
redis:
- image: openvidu/openvidu-redis:1.0.0
+ image: openvidu/openvidu-redis:2.0.0-dev2
restart: always
network_mode: host
environment:
- REDIS_PASSWORD=${OPENVIDU_SECRET}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
coturn:
- image: openvidu/openvidu-coturn:1.0.0
+ image: openvidu/openvidu-coturn:3.0.0-dev2
restart: on-failure
network_mode: host
environment:
@@ -63,17 +70,22 @@ services:
- TURN_LISTEN_PORT=3478
- DB_NAME=0
- DB_PASSWORD=${OPENVIDU_SECRET}
- - MIN_PORT=57001
+ - MIN_PORT=40000
- MAX_PORT=65535
+ - TURN_PUBLIC_IP=${TURN_PUBLIC_IP:-auto-ipv4}
+ - ENABLE_COTURN_LOGS=true
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
nginx:
- image: openvidu/openvidu-proxy:3.0.0
+ image: openvidu/openvidu-proxy:5.0.0-dev2
restart: on-failure
network_mode: host
- entrypoint: ['/bin/sh', '-c', 'htpasswd -bc /etc/nginx/kibana.htpasswd "${KIBANA_USER}" "${KIBANA_PASSWORD}" && /usr/local/bin/entrypoint.sh']
volumes:
- ./certificates:/etc/letsencrypt
- ./owncert:/owncert
+ - ./custom-nginx-vhosts:/etc/nginx/vhost.d/
- ${OPENVIDU_RECORDING_CUSTOM_LAYOUT}:/opt/openvidu/custom-layout
environment:
- DOMAIN_OR_PUBLIC_IP=${DOMAIN_OR_PUBLIC_IP}
@@ -85,31 +97,84 @@ services:
- ALLOWED_ACCESS_TO_RESTAPI=${ALLOWED_ACCESS_TO_RESTAPI:-}
- PROXY_MODE=PRO
- WITH_APP=true
+ - SUPPORT_DEPRECATED_API=${SUPPORT_DEPRECATED_API:-true}
+ - REDIRECT_WWW=${REDIRECT_WWW:-false}
+ - WORKER_CONNECTIONS=${WORKER_CONNECTIONS:-10240}
+ - PUBLIC_IP=${PROXY_PUBLIC_IP:-auto-ipv4}
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.8.0
restart: always
environment:
- discovery.type=single-node
+ - xpack.security.enabled=true
+ - "ES_JAVA_OPTS=${ES_JAVA_OPTS:--Xms2g -Xmx2g}"
ports:
- 9200:9200
volumes:
- ./elasticsearch:/usr/share/elasticsearch/data
+ command: >
+ /bin/bash -c "elasticsearch-users useradd ${ELASTICSEARCH_USERNAME}
+ -p ${ELASTICSEARCH_PASSWORD} -r superuser;
+ docker-entrypoint.sh"
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
kibana:
image: docker.elastic.co/kibana/kibana:7.8.0
restart: always
environment:
- SERVER_BASEPATH="/kibana"
+ - xpack.security.enabled=true
+ - ELASTICSEARCH_USERNAME=${ELASTICSEARCH_USERNAME}
+ - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_PASSWORD}
ports:
- 5601:5601
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
- filebeat:
- image: docker.elastic.co/beats/filebeat:7.8.0
+ metricbeat:
+ image: docker.elastic.co/beats/metricbeat-oss:7.8.0
+ network_mode: host
restart: always
user: root
+ env_file:
+ - .env
+ environment:
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
+ volumes:
+ - ./beats/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
+ command: >
+ /bin/bash -c "metricbeat -e -strict.perms=false
+ `if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
+ `if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
+
+ filebeat:
+ image: docker.elastic.co/beats/filebeat-oss:7.8.0
+ network_mode: host
+ restart: always
+ user: root
+ env_file:
+ - .env
+ environment:
+ - OPENVIDU_PRO_ELASTICSEARCH_HOST=${OPENVIDU_PRO_ELASTICSEARCH_HOST:-http://127.0.0.1:9200}
volumes:
- ./beats/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker:/var/lib/docker:ro
- /var/run/docker.sock:/var/run/docker.sock
- command: filebeat -e -strict.perms=false
+ command: >
+ /bin/bash -c "filebeat -e -strict.perms=false
+ `if [ ! -z $ELASTICSEARCH_USERNAME ]; then echo '-E output.elasticsearch.username=$ELASTICSEARCH_USERNAME'; fi`
+ `if [ ! -z $ELASTICSEARCH_PASSWORD ]; then echo '-E output.elasticsearch.password=$ELASTICSEARCH_PASSWORD'; fi`"
+ logging:
+ options:
+ max-size: "${DOCKER_LOGS_MAX_SIZE:-100M}"
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/install_openvidu_pro.sh b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/install_openvidu_pro.sh
index 5dd1ca1f..2dc933a8 100755
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/install_openvidu_pro.sh
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/install_openvidu_pro.sh
@@ -3,6 +3,7 @@
# Global variables
OPENVIDU_FOLDER=openvidu
OPENVIDU_VERSION=master
+OPENVIDU_UPGRADABLE_VERSION="2.16"
AWS_SCRIPTS_FOLDER=${OPENVIDU_FOLDER}/cluster/aws
ELASTICSEARCH_FOLDER=${OPENVIDU_FOLDER}/elasticsearch
BEATS_FOLDER=${OPENVIDU_FOLDER}/beats
@@ -60,6 +61,10 @@ new_ov_installation() {
--output "${BEATS_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml \
+ --output "${BEATS_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env \
--output "${OPENVIDU_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
@@ -95,6 +100,10 @@ new_ov_installation() {
printf "\n => Creating folder 'owncert'..."
mkdir "${OPENVIDU_FOLDER}/owncert" || fatal_error "Error while creating the folder 'owncert'"
+ # Create vhost nginx folder
+ printf "\n => Creating folder 'custom-nginx-vhosts'..."
+ mkdir "${OPENVIDU_FOLDER}/custom-nginx-vhosts" || fatal_error "Error while creating the folder 'custom-nginx-vhosts'"
+
# Ready to use
printf '\n'
printf '\n'
@@ -105,13 +114,15 @@ new_ov_installation() {
printf '\n 1. Go to openvidu folder:'
printf '\n $ cd openvidu'
printf '\n'
- printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, OPENVIDU_SECRET, and KIBANA_PASSWORD in .env file:'
+ printf '\n 2. Configure OPENVIDU_DOMAIN_OR_PUBLIC_IP, OPENVIDU_PRO_LICENSE, '
+ printf '\n OPENVIDU_SECRET, and ELASTICSEARCH_PASSWORD in .env file:'
printf '\n $ nano .env'
printf '\n'
printf '\n 3. Start OpenVidu'
printf '\n $ ./openvidu start'
printf '\n'
- printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. This folder is necessary for store elasticsearch data."
+ printf "\n CAUTION: The folder 'openvidu/elasticsearch' use user and group 1000 permissions. "
+ printf "\n This folder is necessary for store elasticsearch data."
printf "\n For more information, check:"
printf "\n https://docs.openvidu.io/en/${OPENVIDU_VERSION//v}/openvidu-pro/deployment/on-premises/#deployment-instructions"
printf '\n'
@@ -147,10 +158,13 @@ upgrade_ov() {
# Uppgrade Openvidu
OPENVIDU_PREVIOUS_VERSION=$(grep 'Openvidu Version:' "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml" | awk '{ print $4 }')
- [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && OPENVIDU_PREVIOUS_VERSION=2.13.0
+ [ -z "${OPENVIDU_PREVIOUS_VERSION}" ] && fatal_error "Can't find previous OpenVidu version"
# In this point using the variable 'OPENVIDU_PREVIOUS_VERSION' we can verify if the upgrade is
# posible or not. If it is not posible launch a warning and stop the upgrade.
+ if [[ "${OPENVIDU_PREVIOUS_VERSION}" != "${OPENVIDU_UPGRADABLE_VERSION}."* ]]; then
+ fatal_error "You can't update from version ${OPENVIDU_PREVIOUS_VERSION} to ${OPENVIDU_VERSION}.\nNever upgrade across multiple major versions."
+ fi
printf '\n'
printf '\n ======================================='
@@ -188,6 +202,10 @@ upgrade_ov() {
--output "${TMP_FOLDER}/filebeat.yml" || fatal_error "Error when downloading the file 'filebeat.yml'"
printf '\n - filebeat.yml'
+ curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/beats/metricbeat.yml \
+ --output "${TMP_FOLDER}/metricbeat.yml" || fatal_error "Error when downloading the file 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
curl --silent ${DOWNLOAD_URL}/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env \
--output "${TMP_FOLDER}/.env" || fatal_error "Error when downloading the file '.env'"
printf '\n - .env'
@@ -243,15 +261,20 @@ upgrade_ov() {
mv "${OPENVIDU_PREVIOUS_FOLDER}/openvidu" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'openvidu'"
printf '\n - openvidu'
- mv "${OPENVIDU_PREVIOUS_FOLDER}/readme.md" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'readme.md'"
- printf '\n - readme.md'
-
mv "${OPENVIDU_PREVIOUS_FOLDER}/cluster/aws" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'cluster/aws'"
printf '\n - cluster/aws'
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/beats" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous 'beats'"
+ printf '\n - beats'
+
cp "${OPENVIDU_PREVIOUS_FOLDER}/.env" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous '.env'"
printf '\n - .env'
+ if [ -d "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" ]; then
+ mv "${OPENVIDU_PREVIOUS_FOLDER}/custom-nginx-vhosts" "${ROLL_BACK_FOLDER}" || fatal_error "Error while moving previous directory 'custom-nginx-vhosts'"
+ printf '\n - custom-nginx-vhosts'
+ fi
+
# Move tmp files to Openvidu
printf '\n => Updating files:'
@@ -288,6 +311,9 @@ upgrade_ov() {
mv "${TMP_FOLDER}/filebeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/filebeat.yml" || fatal_error "Error while updating 'filebeat.yml'"
printf '\n - filebeat.yml'
+ mv "${TMP_FOLDER}/metricbeat.yml" "${OPENVIDU_PREVIOUS_FOLDER}/beats/metricbeat.yml" || fatal_error "Error while updating 'metricbeat.yml'"
+ printf '\n - metricbeat.yml'
+
printf "\n => Deleting 'tmp' folder"
rm -rf "${TMP_FOLDER}" || fatal_error "Error deleting 'tmp' folder"
@@ -311,16 +337,15 @@ upgrade_ov() {
[ ! -z "${OLD_MODE}" ] && sed -i -r "s/Installation Mode:.+/Installation Mode: ${OLD_MODE}/" "${OPENVIDU_PREVIOUS_FOLDER}/docker-compose.yml"
# In Aws, update AMI ID
- CHECK_AWS=$(curl -s -o /dev/null -w "%{http_code}" http://169.254.169.254)
- if [[ ${CHECK_AWS} == "200" ]]; then
- AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
- [[ -z ${AWS_REGION} ]] && fatal_error "Error while getting AWS_REGION"
+ AWS_REGION=$(grep -E "AWS_DEFAULT_REGION=.*$" "${OPENVIDU_PREVIOUS_FOLDER}/.env" | cut -d'=' -f2)
+ if [[ ! -z ${AWS_REGION} ]]; then
NEW_AMI_ID=$(curl https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/CF-OpenVidu-Pro-${OPENVIDU_VERSION//v}.yaml --silent |
sed -n -e '/KMSAMIMAP:/,/Metadata:/ p' |
grep -A 1 ${AWS_REGION} | grep AMI | tr -d " " | cut -d":" -f2)
[[ -z ${NEW_AMI_ID} ]] && fatal_error "Error while getting new AWS_IMAGE_ID for Media Nodes"
sed -i "s/.*AWS_IMAGE_ID=.*/AWS_IMAGE_ID=${NEW_AMI_ID}/" "${OPENVIDU_PREVIOUS_FOLDER}/.env" || fatal_error "Error while updating new AWS_IMAGE_ID for Media Nodes"
fi
+
# Ready to use
printf '\n'
diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/openvidu b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/openvidu
index 9f948e61..b4229d5a 100755
--- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/openvidu
+++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/openvidu
@@ -120,7 +120,7 @@ generate_report() {
printf '\n'
printf '\n'
- cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/KIBANA_PASSWORD=.+/KIBANA_PASSWORD=****/"
+ cat < "${OV_FOLDER}/.env" | sed -r -e "s/OPENVIDU_SECRET=.+/OPENVIDU_SECRET=****/" -e "s/OPENVIDU_PRO_LICENSE=.+/OPENVIDU_PRO_LICENSE=****/" -e "s/ELASTICSEARCH_PASSWORD=.+/ELASTICSEARCH_PASSWORD=****/"
printf '\n'
printf '\n ========= docker-compose.yml =========='
@@ -183,6 +183,42 @@ generate_report() {
printf "\n"
}
+is_external_url() {
+ local URL=$1
+ if [[ -z "$URL" ]]; then
+ return 1
+ fi
+ if [[ "${URL}" == *"localhost"* ]] || [[ "${URL}" == *"127.0.0.1"* ]] || [[ "${URL}" == *"::1"* ]]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+start_openvidu() {
+ local RUN_LOCAL_ES
+ local RUN_LOCAL_KIBANA
+ local CONFIGURED_ELASTICSEARCH_HOST
+ local CONFIGURED_KIBANA_HOST
+ CONFIGURED_ELASTICSEARCH_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_ELASTICSEARCH_HOST | cut -d '=' -f2)
+ CONFIGURED_KIBANA_HOST=$(grep -v '^#' .env | grep OPENVIDU_PRO_KIBANA_HOST | cut -d '=' -f2)
+ RUN_LOCAL_ES=true
+ RUN_LOCAL_KIBANA=true
+ if is_external_url "${CONFIGURED_ELASTICSEARCH_HOST}"; then
+ printf "Configured external elasticsearch: %s" "${CONFIGURED_ELASTICSEARCH_HOST}"
+ printf "\n"
+ RUN_LOCAL_ES=false
+ fi
+ if is_external_url "${CONFIGURED_KIBANA_HOST}"; then
+ printf "Configured external kibana: %s" "${CONFIGURED_KIBANA_HOST}"
+ printf "\n"
+ RUN_LOCAL_KIBANA=false
+ fi
+ docker-compose up -d \
+ $(if [ "${RUN_LOCAL_ES}" == "false" ]; then echo '--scale elasticsearch=0'; fi) \
+ $(if [ "${RUN_LOCAL_KIBANA}" == "false" ]; then echo '--scale kibana=0'; fi)
+}
+
usage() {
printf "Usage: \n\t openvidu [command]"
printf "\n\nAvailable Commands:"
@@ -198,11 +234,15 @@ usage() {
printf "\n"
}
+[[ -z "${FOLLOW_OPENVIDU_LOGS}" ]] && FOLLOW_OPENVIDU_LOGS=true
+
case $1 in
start)
- docker-compose up -d
- docker-compose logs -f openvidu-server
+ start_openvidu
+ if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
+ docker-compose logs -f openvidu-server
+ fi
;;
stop)
@@ -211,8 +251,10 @@ case $1 in
restart)
docker-compose down
- docker-compose up -d
- docker-compose logs -f openvidu-server
+ start_openvidu
+ if [[ "${FOLLOW_OPENVIDU_LOGS}" == "true" ]]; then
+ docker-compose logs -f openvidu-server
+ fi
;;
logs)
diff --git a/openvidu-server/deployments/pro/taskcat/taskcat.yml b/openvidu-server/deployments/pro/taskcat/taskcat.yml
index 288b8305..fed9a306 100644
--- a/openvidu-server/deployments/pro/taskcat/taskcat.yml
+++ b/openvidu-server/deployments/pro/taskcat/taskcat.yml
@@ -1,6 +1,6 @@
---
global:
- owner: openvidu@gmail.com
+ owner: info@openvidu.io
qsname: openvidu-pro-clustering
regions:
- us-east-1
diff --git a/openvidu-server/docker/openvidu-coturn/Dockerfile b/openvidu-server/docker/openvidu-coturn/Dockerfile
index eac2ee94..bcaf1acd 100644
--- a/openvidu-server/docker/openvidu-coturn/Dockerfile
+++ b/openvidu-server/docker/openvidu-coturn/Dockerfile
@@ -1,7 +1,7 @@
FROM ubuntu:16.04
RUN apt-get update \
- && apt-get install -y coturn curl
+ && apt-get install -y coturn curl dnsutils
COPY ./configuration-files.sh /tmp/
COPY ./entrypoint.sh /usr/local/bin
@@ -11,4 +11,4 @@ RUN chmod +x /tmp/configuration-files.sh \
&& chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/discover_my_public_ip.sh
-CMD /usr/local/bin/entrypoint.sh
\ No newline at end of file
+ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-coturn/README.md b/openvidu-server/docker/openvidu-coturn/README.md
index df542138..4b1ea95e 100644
--- a/openvidu-server/docker/openvidu-coturn/README.md
+++ b/openvidu-server/docker/openvidu-coturn/README.md
@@ -28,9 +28,15 @@ docker run --rm --name some-redis -d -p 6379:6379 redis
Get the ip of the container and after that, run coturn, you can use url as ip too, in this example I am running coturn with nip.io:
```
-docker run -it -e REDIS_IP=172.17.0.2 -e DB_NAME=0 -e DB_PASSWORD=turn -e MIN_PORT=40000 -e MAX_PORT=65535 -e TURN_LISTEN_PORT=3478 --network=host coturn-openvidu
+docker run -it -e REDIS_IP=172.17.0.2 -e DB_NAME=0 -e DB_PASSWORD=turn -e MIN_PORT=40000 -e MAX_PORT=65535 -e TURN_PUBLIC_IP=auto -e TURN_LISTEN_PORT=3478 --network=host openvidu/openvidu-coturn
```
+## Execute turn locally with fixed username and password
+```
+docker run -it -e TURN_PUBLIC_IP=auto -e TURN_USERNAME_PASSWORD=: -e MIN_PORT=40000 -e MAX_PORT=65535 -e TURN_LISTEN_PORT=3478 --network=host openvidu/openvidu-coturn
+```
+
+
# Kubernetes
TODO
diff --git a/openvidu-server/docker/openvidu-coturn/configuration-files.sh b/openvidu-server/docker/openvidu-coturn/configuration-files.sh
index 3d94420c..4a88e4fb 100644
--- a/openvidu-server/docker/openvidu-coturn/configuration-files.sh
+++ b/openvidu-server/docker/openvidu-coturn/configuration-files.sh
@@ -7,15 +7,25 @@ EOF
# Turn server configuration
cat>/etc/turnserver.conf<> /etc/turnserver.conf
+fi
+
+if [[ ! -z "${REDIS_IP}" ]] && [[ ! -z "${DB_NAME}" ]] && [[ ! -z "${DB_PASSWORD}" ]]; then
+ echo "redis-userdb=\"ip=${REDIS_IP} dbname=${DB_NAME} password=${DB_PASSWORD} connect_timeout=30\"" >> /etc/turnserver.conf
+fi
+
+if [[ ! -z "${TURN_USERNAME_PASSWORD}" ]]; then
+ echo "user=${TURN_USERNAME_PASSWORD}" >> /etc/turnserver.conf
+fi
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-coturn/entrypoint.sh b/openvidu-server/docker/openvidu-coturn/entrypoint.sh
index 8a87a719..25863902 100644
--- a/openvidu-server/docker/openvidu-coturn/entrypoint.sh
+++ b/openvidu-server/docker/openvidu-coturn/entrypoint.sh
@@ -5,19 +5,15 @@ DEBUG=${DEBUG:-false}
[ "$DEBUG" == "true" ] && set -x
#Check parameters
-[[ ! -z "${TURN_PUBLIC_IP}" ]] || export TURN_PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh)
+[[ "${TURN_PUBLIC_IP}" == "auto-ipv4" ]] && export TURN_PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh)
+[[ "${TURN_PUBLIC_IP}" == "auto-ipv6" ]] && export TURN_PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh --ipv6)
+[[ -z "${ENABLE_COTURN_LOGS}" ]] && export ENABLE_COTURN_LOGS=true
-echo "TURN public IP: ${TURN_PUBLIC_IP}"
+echo "TURN public IP: ${TURN_PUBLIC_IP:-"empty"}"
[[ ! -z "${TURN_LISTEN_PORT}" ]] && echo "TURN listening port: ${TURN_LISTEN_PORT}" ||
{ echo "TURN_LISTEN_PORT environment variable is not defined"; exit 1; }
-[[ ! -z "${REDIS_IP}" ]] && echo "REDIS IP: ${REDIS_IP}" || { echo "REDIS_IP environment variable is not defined"; exit 1; }
-
-[[ ! -z "${DB_NAME}" ]] || { echo "DB_NAME environment variable is not defined"; exit 1; }
-
-[[ ! -z "${DB_PASSWORD}" ]] || { echo "DB_PASSWORD environment variable is not defined"; exit 1; }
-
[[ ! -z "${MIN_PORT}" ]] && echo "Defined min port coturn: ${MIN_PORT}" || echo "Min port coturn: 40000"
[[ ! -z "${MAX_PORT}" ]] && echo "Defined max port coturn: ${MAX_PORT}" || echo "Max port coturn: 65535"
@@ -28,19 +24,8 @@ source /tmp/configuration-files.sh
# Remove temp file with configuration parameters
rm /tmp/configuration-files.sh
-# Save coturn External IP for other services
-
-
-# Execute turn daemon
-/usr/bin/turnserver -c /etc/turnserver.conf -v &
-
-
-MAX_SECONDS=30
-# K8s only show turn server log in this way
-while [ -z $(ls /var/log/ | grep turn_) ] && [ $SECONDS -lt $MAX_SECONDS ]
-do
- echo "Waiting turnserver to be running"
- sleep 2
-done
-
-tail -f /var/log/turn_*.log
+if [[ "${ENABLE_COTURN_LOGS}" == "true" ]]; then
+ /usr/bin/turnserver -c /etc/turnserver.conf -v --log-file /dev/null
+else
+ /usr/bin/turnserver -c /etc/turnserver.conf -v --log-file /dev/null --no-stdout-log
+fi
diff --git a/openvidu-server/docker/openvidu-proxy/Dockerfile b/openvidu-server/docker/openvidu-proxy/Dockerfile
index b29fd066..9272c08d 100644
--- a/openvidu-server/docker/openvidu-proxy/Dockerfile
+++ b/openvidu-server/docker/openvidu-proxy/Dockerfile
@@ -5,7 +5,9 @@ RUN apk update && \
apk add bash \
certbot \
openssl \
- apache2-utils && \
+ apache2-utils \
+ bind-tools \
+ perl pcre grep && \
rm -rf /var/cache/apk/*
# Default nginx conf
@@ -14,9 +16,16 @@ COPY ./default_nginx_conf /default_nginx_conf
# Entrypoint and discover public ip scripts
COPY ./discover_my_public_ip.sh /usr/local/bin
+
+# Copy nginx.conf
+COPY ./nginx.conf /etc/nginx/nginx.conf
+
+# Entrypoint
COPY ./entrypoint.sh /usr/local/bin
RUN mkdir -p /var/www/certbot && \
+ mkdir -p /etc/nginx/vhost.d/ && \
+ mkdir -p /custom-nginx && \
chmod +x /usr/local/bin/entrypoint.sh && \
chmod +x /usr/local/bin/discover_my_public_ip.sh
diff --git a/openvidu-server/docker/openvidu-proxy/default.conf b/openvidu-server/docker/openvidu-proxy/default.conf
index 8a95ee30..7378f5a4 100644
--- a/openvidu-server/docker/openvidu-proxy/default.conf
+++ b/openvidu-server/docker/openvidu-proxy/default.conf
@@ -1,5 +1,6 @@
server {
listen {http_port};
+ listen [::]:{http_port};
location /.well-known/acme-challenge/ {
root /var/www/certbot;
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app-without-demos.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app-without-demos.conf
deleted file mode 100644
index cdff24fe..00000000
--- a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app-without-demos.conf
+++ /dev/null
@@ -1,103 +0,0 @@
-# Your app
-#upstream yourapp {
-# server localhost:5442;
-#}
-
-upstream openviduserver {
- server localhost:5443;
-}
-
-server {
- listen {https_port} ssl;
- server_name {domain_name};
-
- ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
- ssl_trusted_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
-
- ssl_session_cache shared:SSL:50m;
- ssl_session_timeout 5m;
- ssl_stapling on;
- ssl_stapling_verify on;
-
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
-
- ssl_prefer_server_ciphers on;
-
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Proto https;
- proxy_headers_hash_bucket_size 512;
- proxy_redirect off;
-
- # Websockets
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
-
- # Welcome
- root /var/www/html;
-
- # Your app
- #location / {
- # proxy_pass http://yourapp; # Openvidu call by default
- #}
-
- # Openvidu Admin Panel
- location /dashboard {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # Openvidu Server
- location /layouts/custom {
- rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
- root /opt/openvidu;
- }
-
- location /recordings {
- proxy_pass http://openviduserver;
- }
-
- location /api {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location ~ /openvidu$ {
- proxy_pass http://openviduserver;
- }
-
- location /info {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /config {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /accept-certificate {
- proxy_pass http://openviduserver;
- }
-
- location /cdr {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # letsencrypt
- location /.well-known/acme-challenge {
- root /var/www/certbot;
- try_files $uri $uri/ =404;
- }
-}
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app.conf
deleted file mode 100644
index 0fcd2142..00000000
--- a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default-app.conf
+++ /dev/null
@@ -1,103 +0,0 @@
-# Openvidu call
-upstream yourapp {
- server localhost:5442;
-}
-
-upstream openviduserver {
- server localhost:5443;
-}
-
-server {
- listen {https_port} ssl;
- server_name {domain_name};
-
- ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
- ssl_trusted_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
-
- ssl_session_cache shared:SSL:50m;
- ssl_session_timeout 5m;
- ssl_stapling on;
- ssl_stapling_verify on;
-
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
-
- ssl_prefer_server_ciphers on;
-
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Proto https;
- proxy_headers_hash_bucket_size 512;
- proxy_redirect off;
-
- # Websockets
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
-
- # Welcome
- #root /var/www/html;
-
- # Your app
- location / {
- proxy_pass http://yourapp; # Openvidu call by default
- }
-
- # Openvidu Admin Panel
- location /dashboard {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # Openvidu Server
- location /layouts/custom {
- rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
- root /opt/openvidu;
- }
-
- location /recordings {
- proxy_pass http://openviduserver;
- }
-
- location /api {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location ~ /openvidu$ {
- proxy_pass http://openviduserver;
- }
-
- location /info {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /config {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /accept-certificate {
- proxy_pass http://openviduserver;
- }
-
- location /cdr {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # letsencrypt
- location /.well-known/acme-challenge {
- root /var/www/certbot;
- try_files $uri $uri/ =404;
- }
-}
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default.conf
index 6afc2bbd..60769f42 100644
--- a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default.conf
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/ce/default.conf
@@ -1,5 +1,12 @@
+{app_upstream}
+
+upstream openviduserver {
+ server localhost:5443;
+}
+
server {
listen {http_port};
+ listen [::]:{http_port};
server_name {domain_name};
# Redirect to https
@@ -11,4 +18,39 @@ server {
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
+
+ {nginx_status}
+}
+
+{redirect_www}
+
+{redirect_www_ssl}
+
+server {
+ listen {https_port} ssl;
+ listen [::]:{https_port} ssl;
+ server_name {domain_name};
+
+ {ssl_config}
+
+ {proxy_config}
+
+ {app_config}
+
+ ########################
+ # OpenVidu Locations #
+ ########################
+ {common_api_ce}
+
+ {deprecated_api_ce}
+
+ {new_api_ce}
+
+ #################################
+ # LetsEncrypt #
+ #################################
+ location /.well-known/acme-challenge {
+ root /var/www/certbot;
+ try_files $uri $uri/ =404;
+ }
}
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config.conf
new file mode 100644
index 00000000..2dd7bcee
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config.conf
@@ -0,0 +1,4 @@
+ # Your App
+ location / {
+ proxy_pass http://yourapp; # Openvidu call by default
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config_default.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config_default.conf
new file mode 100644
index 00000000..82f76720
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_config_default.conf
@@ -0,0 +1,2 @@
+ # Welcome
+ root /var/www/html;
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_upstream.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_upstream.conf
new file mode 100644
index 00000000..f4048ec8
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/app_upstream.conf
@@ -0,0 +1,4 @@
+# Your App
+upstream yourapp {
+ server localhost:5442;
+}
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/common_api_ce.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/common_api_ce.conf
new file mode 100644
index 00000000..1ac0df12
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/common_api_ce.conf
@@ -0,0 +1,14 @@
+ #################################
+ # Common rules #
+ #################################
+ # Dashboard rule
+ location /dashboard {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ # Websocket rule
+ location ~ /openvidu$ {
+ proxy_pass http://openviduserver;
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/deprecated_api_ce.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/deprecated_api_ce.conf
new file mode 100644
index 00000000..a572356d
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/deprecated_api_ce.conf
@@ -0,0 +1,40 @@
+ #################################
+ # Deprecated API #
+ #################################
+ # Openvidu Server
+ location /layouts/custom {
+ rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
+ root /opt/openvidu;
+ }
+
+ location /recordings {
+ proxy_pass http://openviduserver;
+ }
+
+ location /api {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /info {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /config {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /accept-certificate {
+ proxy_pass http://openviduserver;
+ }
+
+ location /cdr {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/new_api_ce.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/new_api_ce.conf
new file mode 100644
index 00000000..4b9d940b
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/new_api_ce.conf
@@ -0,0 +1,33 @@
+ #################################
+ # New API #
+ #################################
+ location /openvidu/layouts {
+ rewrite ^/openvidu/layouts/(.*)$ /custom-layout/$1 break;
+ root /opt/openvidu;
+ }
+
+ location /openvidu/recordings {
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/api {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/info {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/accept-certificate {
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/cdr {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/redirect_www.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/redirect_www.conf
new file mode 100644
index 00000000..bef132f8
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ce/redirect_www.conf
@@ -0,0 +1,15 @@
+server {
+ listen {http_port};
+ listen [::]:{http_port};
+ server_name www.{domain_name};
+
+ # Redirect to https
+ location / {
+ rewrite ^(.*) https://{domain_name}:{https_port}$1 permanent;
+ }
+
+ # letsencrypt
+ location /.well-known/acme-challenge/ {
+ root /var/www/certbot;
+ }
+}
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/nginx_status.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/nginx_status.conf
new file mode 100644
index 00000000..be33d323
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/nginx_status.conf
@@ -0,0 +1,5 @@
+ location /nginx_status {
+ stub_status;
+ allow 127.0.0.1; #only allow requests from localhost
+ deny all; #deny all other hosts
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/common_api_pro.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/common_api_pro.conf
new file mode 100644
index 00000000..6df8b233
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/common_api_pro.conf
@@ -0,0 +1,27 @@
+ #################################
+ # Common rules #
+ #################################
+ location /dashboard {
+ {rules_access_dashboard}
+ deny all;
+ rewrite ^/dashboard/(.*)$ /$1 break;
+ proxy_pass http://openviduserver/;
+ }
+
+ location /inspector {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location ~ /openvidu$ {
+ proxy_pass http://openviduserver;
+ }
+
+ location /kibana {
+ {rules_access_dashboard}
+ deny all;
+
+ rewrite ^/kibana/(.*)$ /$1 break;
+ proxy_pass http://kibana/;
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/deprecated_api_pro.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/deprecated_api_pro.conf
new file mode 100644
index 00000000..1c8a45eb
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/deprecated_api_pro.conf
@@ -0,0 +1,59 @@
+ #################################
+ # Deprecated API #
+ #################################
+ # Openvidu Server
+ location /layouts/custom {
+ rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
+ root /opt/openvidu;
+ }
+
+ location /recordings {
+ proxy_pass http://openviduserver;
+ }
+
+ location /api {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /info {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /config {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /accept-certificate {
+ proxy_pass http://openviduserver;
+ }
+
+ location /cdr {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ # Openvidu Server Pro
+ location /pro {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /api-login {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /elasticsearch {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/new_api_pro.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/new_api_pro.conf
new file mode 100644
index 00000000..ea426b5c
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/new_api_pro.conf
@@ -0,0 +1,46 @@
+ #################################
+ # New API #
+ #################################
+ # OpenVidu Server
+ location /openvidu/layouts {
+ rewrite ^/openvidu/layouts/(.*)$ /custom-layout/$1 break;
+ root /opt/openvidu;
+ }
+
+ location /openvidu/recordings {
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/api {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/info {
+ {rules_access_dashboard}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/accept-certificate {
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/cdr {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+ # OpenVidu Server PRO
+ location /openvidu/elk {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
+
+ location /openvidu/inspector-api {
+ {rules_acess_api}
+ deny all;
+ proxy_pass http://openviduserver;
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/redirect_www.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/redirect_www.conf
new file mode 100644
index 00000000..bd17a798
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/pro/redirect_www.conf
@@ -0,0 +1,3 @@
+ if ($host = www.{domain_name}) {
+ rewrite ^(.*) https://{domain_name}:{https_port}$1 permanent;
+ }
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/proxy_config.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/proxy_config.conf
new file mode 100644
index 00000000..9c3add1c
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/proxy_config.conf
@@ -0,0 +1,13 @@
+ # Proxy
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Proto https;
+ proxy_headers_hash_bucket_size 512;
+ proxy_redirect off;
+
+ # Websockets
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/redirect_www_ssl.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/redirect_www_ssl.conf
new file mode 100644
index 00000000..fd1c7803
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/redirect_www_ssl.conf
@@ -0,0 +1,18 @@
+server {
+ listen 443 ssl;
+ server_name www.{domain_name};
+
+ {ssl_config}
+
+ {proxy_config}
+
+ # Redirect to non-www
+ location / {
+ rewrite ^(.*) https://{domain_name}:{https_port}$1 permanent;
+ }
+
+ # letsencrypt
+ location /.well-known/acme-challenge/ {
+ root /var/www/certbot;
+ }
+}
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ssl_config.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ssl_config.conf
new file mode 100644
index 00000000..77ba1dc0
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/global/ssl_config.conf
@@ -0,0 +1,16 @@
+ # SSL Config
+ ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
+ ssl_trusted_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
+
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_timeout 5m;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ ssl_protocols {ssl_protocols};
+ ssl_ciphers "{ssl_ciphers}";
+
+ ssl_prefer_server_ciphers on;
+
+ add_header Strict-Transport-Security "{add_header_hsts}" always;
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default-app-without-demos.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default-app-without-demos.conf
deleted file mode 100644
index 1473ac20..00000000
--- a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default-app-without-demos.conf
+++ /dev/null
@@ -1,165 +0,0 @@
-add_header X-Frame-Options SAMEORIGIN;
-add_header X-Content-Type-Options nosniff;
-add_header X-XSS-Protection "1; mode=block";
-
-upstream kibana {
- server localhost:5601;
-}
-
-upstream openviduserver {
- server localhost:5443;
-}
-
-server {
- # Redirect to https
- if ($host = {domain_name}) {
- rewrite ^(.*) https://{domain_name}:{https_port}$1 permanent;
- } # managed by Certbot
-
- listen {http_port} default_server;
- server_name {domain_name};
-
- # letsencrypt
- location /.well-known/acme-challenge {
- root /var/www/certbot;
- try_files $uri $uri/ =404;
- }
-
- # Kibana panel
- location /kibana {
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
-
- rewrite ^/kibana/(.*)$ /$1 break;
- proxy_pass http://kibana/;
- }
-}
-
-server {
- listen {https_port} ssl default deferred;
- server_name {domain_name};
-
- ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
- ssl_trusted_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
-
- ssl_session_cache shared:SSL:50m;
- ssl_session_timeout 5m;
- ssl_stapling on;
- ssl_stapling_verify on;
-
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
-
- ssl_prefer_server_ciphers on;
-
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Proto https;
- proxy_headers_hash_bucket_size 512;
- proxy_redirect off;
-
- # Websockets
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
-
- # Welcome
- root /var/www/html;
-
- # Openvidu Admin Panel
- location /dashboard {
- {rules_access_dashboard}
- deny all;
- rewrite ^/dashboard/(.*)$ /$1 break;
- proxy_pass http://openviduserver/;
- }
-
- location /inspector {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /kibana {
- {rules_access_dashboard}
- deny all;
- auth_basic "Openvidu Monitoring";
- auth_basic_user_file /etc/nginx/kibana.htpasswd;
-
- rewrite ^/kibana/(.*)$ /$1 break;
- proxy_pass http://kibana/;
- }
-
- # Openvidu Server
- location /layouts/custom {
- rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
- root /opt/openvidu;
- }
-
- location /recordings {
- proxy_pass http://openviduserver;
- }
-
- location /api {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location ~ /openvidu$ {
- proxy_pass http://openviduserver;
- }
-
- location /info {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /config {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /accept-certificate {
- proxy_pass http://openviduserver;
- }
-
- location /cdr {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # Openvidu Server Pro
- location /pro {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /api-login {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /elasticsearch {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # letsencrypt
- location /.well-known/acme-challenge {
- root /var/www/certbot;
- try_files $uri $uri/ =404;
- }
-}
diff --git a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default.conf b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default.conf
index d5ada4eb..f88768cd 100644
--- a/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default.conf
+++ b/openvidu-server/docker/openvidu-proxy/default_nginx_conf/pro/default.conf
@@ -2,10 +2,7 @@ add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
-# Openvidu call
-upstream yourapp {
- server localhost:5442;
-}
+{app_upstream}
upstream kibana {
server localhost:5601;
@@ -15,13 +12,18 @@ upstream openviduserver {
server localhost:5443;
}
+{redirect_www_ssl}
+
server {
# Redirect to https
if ($host = {domain_name}) {
rewrite ^(.*) https://{domain_name}:{https_port}$1 permanent;
} # managed by Certbot
+
+ {redirect_www}
listen {http_port} default_server;
+ listen [::]:{http_port} default_server;
server_name {domain_name};
# letsencrypt
@@ -41,133 +43,34 @@ server {
rewrite ^/kibana/(.*)$ /$1 break;
proxy_pass http://kibana/;
}
+
+ {nginx_status}
}
server {
listen {https_port} ssl default deferred;
+ listen [::]:{https_port} ssl default deferred;
server_name {domain_name};
- ssl_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/{domain_name}/privkey.pem;
- ssl_trusted_certificate /etc/letsencrypt/live/{domain_name}/fullchain.pem;
+ {ssl_config}
- ssl_session_cache shared:SSL:50m;
- ssl_session_timeout 5m;
- ssl_stapling on;
- ssl_stapling_verify on;
+ {proxy_config}
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+ {app_config}
- ssl_prefer_server_ciphers on;
+ ########################
+ # OpenVidu Locations #
+ ########################
+ {common_api_pro}
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Proto https;
- proxy_headers_hash_bucket_size 512;
- proxy_redirect off;
+ {deprecated_api_pro}
- # Websockets
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
+ {new_api_pro}
- # Welcome
- #root /var/www/html;
+ #################################
+ # LetsEncrypt #
+ #################################
- # Your app
- location / {
- proxy_pass http://yourapp; # Openvidu call by default
- }
-
- # Openvidu Admin Panel
- location /dashboard {
- {rules_access_dashboard}
- deny all;
- rewrite ^/dashboard/(.*)$ /$1 break;
- proxy_pass http://openviduserver/;
- }
-
- location /inspector {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /kibana {
- {rules_access_dashboard}
- deny all;
- auth_basic "Openvidu Monitoring";
- auth_basic_user_file /etc/nginx/kibana.htpasswd;
-
- rewrite ^/kibana/(.*)$ /$1 break;
- proxy_pass http://kibana/;
- }
-
- # Openvidu Server
- location /layouts/custom {
- rewrite ^/layouts/custom/(.*)$ /custom-layout/$1 break;
- root /opt/openvidu;
- }
-
- location /recordings {
- proxy_pass http://openviduserver;
- }
-
- location /api {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location ~ /openvidu$ {
- proxy_pass http://openviduserver;
- }
-
- location /info {
- {rules_access_dashboard}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /config {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /accept-certificate {
- proxy_pass http://openviduserver;
- }
-
- location /cdr {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # Openvidu Server Pro
- location /pro {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /api-login {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- location /elasticsearch {
- {rules_acess_api}
- deny all;
- proxy_pass http://openviduserver;
- }
-
- # letsencrypt
location /.well-known/acme-challenge {
root /var/www/certbot;
try_files $uri $uri/ =404;
diff --git a/openvidu-server/docker/openvidu-proxy/entrypoint.sh b/openvidu-server/docker/openvidu-proxy/entrypoint.sh
index 98c8f2f0..659bc90e 100755
--- a/openvidu-server/docker/openvidu-proxy/entrypoint.sh
+++ b/openvidu-server/docker/openvidu-proxy/entrypoint.sh
@@ -28,22 +28,25 @@ if [[ "${CERTIFICATE_TYPE}" == "letsencrypt" && \
fi
# Global variables
-CERTIFICATES_FOLDER=/etc/letsencrypt/live
-CERTIFICATES_CONF="${CERTIFICATES_FOLDER}/certificates.conf"
+CERTIFICATES_FOLDER=/etc/letsencrypt
+CERTIFICATES_LIVE_FOLDER="${CERTIFICATES_FOLDER}/live"
+CERTIFICATES_CONF="${CERTIFICATES_LIVE_FOLDER}/certificates.conf"
-[ ! -d "${CERTIFICATES_FOLDER}" ] && mkdir -p "${CERTIFICATES_FOLDER}"
+[ ! -d "${CERTIFICATES_LIVE_FOLDER}" ] && mkdir -p "${CERTIFICATES_LIVE_FOLDER}"
[ ! -f "${CERTIFICATES_CONF}" ] && touch "${CERTIFICATES_CONF}"
[ -z "${PROXY_HTTP_PORT}" ] && export PROXY_HTTP_PORT=80
[ -z "${PROXY_HTTPS_PORT}" ] && export PROXY_HTTPS_PORT=443
+[ -z "${PROXY_HTTPS_PROTOCOLS}" ] && export PROXY_HTTPS_PROTOCOLS='TLSv1 TLSv1.1 TLSv1.2 TLSv1.3'
+[ -z "${PROXY_HTTPS_CIPHERS}" ] && export PROXY_HTTPS_CIPHERS='ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'
[ -z "${WITH_APP}" ] && export WITH_APP=true
+[ -z "${SUPPORT_DEPRECATED_API}" ] && export SUPPORT_DEPRECATED_API=true
+[ -z "${REDIRECT_WWW}" ] && export REDIRECT_WWW=false
[ -z "${PROXY_MODE}" ] && export PROXY_MODE=CE
+[ -z "${WORKER_CONNECTIONS}" ] && export WORKER_CONNECTIONS=10240
+[ -z "${PUBLIC_IP}" ] && export PUBLIC_IP=auto-ipv4
[ -z "${ALLOWED_ACCESS_TO_DASHBOARD}" ] && export ALLOWED_ACCESS_TO_DASHBOARD=all
[ -z "${ALLOWED_ACCESS_TO_RESTAPI}" ] && export ALLOWED_ACCESS_TO_RESTAPI=all
-# Start with default certbot conf
-sed -i "s/{http_port}/${PROXY_HTTP_PORT}/" /etc/nginx/conf.d/default.conf
-nginx -g "daemon on;"
-
# Show input enviroment variables
printf "\n ======================================="
printf "\n = INPUT VARIABLES ="
@@ -53,8 +56,11 @@ printf "\n"
printf "\n Config NGINX:"
printf "\n - Http Port: %s" "${PROXY_HTTP_PORT}"
printf "\n - Https Port: %s" "${PROXY_HTTPS_PORT}"
+printf "\n - Worker Connections: %s" "${WORKER_CONNECTIONS}"
printf "\n - Allowed Access in Openvidu Dashboard: %s" "${ALLOWED_ACCESS_TO_DASHBOARD}"
printf "\n - Allowed Access in Openvidu API: %s" "${ALLOWED_ACCESS_TO_RESTAPI}"
+printf "\n - Support deprecated API: %s" "${SUPPORT_DEPRECATED_API}"
+printf "\n - Redirect www to non-www: %s" "${REDIRECT_WWW}"
printf "\n"
printf "\n Config Openvidu Application:"
printf "\n - Domain name: %s" "${DOMAIN_OR_PUBLIC_IP}"
@@ -69,42 +75,54 @@ printf "\n = CONFIGURATION NGINX ="
printf "\n ======================================="
printf "\n"
-printf "\n Configure %s domain..." "${DOMAIN_OR_PUBLIC_IP}"
-CERTIFICATED_OLD_CONFIG=$(grep "${DOMAIN_OR_PUBLIC_IP}" "${CERTIFICATES_CONF}" | cut -f2 -d$'\t')
+# Override worker connections
+sed -i "s/{worker_connections}/${WORKER_CONNECTIONS}/g" /etc/nginx/nginx.conf
-printf "\n - New configuration: %s" "${CERTIFICATE_TYPE}"
+printf "\n Configure %s domain..." "${DOMAIN_OR_PUBLIC_IP}"
+OLD_DOMAIN_OR_PUBLIC_IP=$(head -n 1 "${CERTIFICATES_CONF}" | cut -f1 -d$'\t')
+CERTIFICATED_OLD_CONFIG=$(head -n 1 "${CERTIFICATES_CONF}" | cut -f2 -d$'\t')
+
+printf "\n - New configuration: %s %s" "${CERTIFICATE_TYPE}" "${DOMAIN_OR_PUBLIC_IP}"
if [ -z "${CERTIFICATED_OLD_CONFIG}" ]; then
- printf "\n - Old configuration: none"
+ printf "\n - Old configuration: none\n"
else
- printf "\n - Old configuration: %s" "${CERTIFICATED_OLD_CONFIG}"
+ printf "\n - Old configuration: %s %s\n" "${CERTIFICATED_OLD_CONFIG}" "${OLD_DOMAIN_OR_PUBLIC_IP}"
+
+ if [ "${CERTIFICATED_OLD_CONFIG}" != "${CERTIFICATE_TYPE}" ] || \
+ [ "${OLD_DOMAIN_OR_PUBLIC_IP}" != "${DOMAIN_OR_PUBLIC_IP}" ]; then
- if [ "${CERTIFICATED_OLD_CONFIG}" != "${CERTIFICATE_TYPE}" ]; then
printf "\n - Restarting configuration... Removing old certificated..."
-
- rm -rf "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/"*
+ # Remove certificate folder safely
+ find "${CERTIFICATES_FOLDER:?}" -mindepth 1 -delete
+ # Recreate certificates folder
+ mkdir -p "${CERTIFICATES_LIVE_FOLDER}"
+ touch "${CERTIFICATES_CONF}"
fi
fi
# Save actual conf
-sed -i "/${DOMAIN_OR_PUBLIC_IP}/d" "${CERTIFICATES_CONF}"
-echo -e "${DOMAIN_OR_PUBLIC_IP}\t${CERTIFICATE_TYPE}" >> "${CERTIFICATES_CONF}"
+echo -e "${DOMAIN_OR_PUBLIC_IP}\t${CERTIFICATE_TYPE}" > "${CERTIFICATES_CONF}"
+
+# Start with default certbot conf
+sed -i "s/{http_port}/${PROXY_HTTP_PORT}/" /etc/nginx/conf.d/default.conf
+nginx -g "daemon on;"
case ${CERTIFICATE_TYPE} in
"selfsigned")
- if [[ ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
- ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
+ if [[ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
+ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
printf "\n - Generating selfsigned certificate...\n"
# Delete and create certificate folder
- rm -rf "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
- mkdir -p "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}"
+ rm -rf "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
+ mkdir -p "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}"
openssl req -new -nodes -x509 \
-subj "/CN=${DOMAIN_OR_PUBLIC_IP}" -days 365 \
- -keyout "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" \
- -out "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" \
+ -keyout "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" \
+ -out "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" \
-extensions v3_ca
else
printf "\n - Selfsigned certificate already exists, using them..."
@@ -112,18 +130,18 @@ case ${CERTIFICATE_TYPE} in
;;
"owncert")
- if [[ ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
- ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
- printf "\n - Copying owmcert certificate..."
+ if [[ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
+ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
+ printf "\n - Copying owncert certificate..."
# Delete and create certificate folder
- rm -rf "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
- mkdir -p "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}"
+ rm -rf "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
+ mkdir -p "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}"
- cp /owncert/certificate.key "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem"
- cp /owncert/certificate.cert "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem"
+ cp /owncert/certificate.key "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem"
+ cp /owncert/certificate.cert "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem"
else
- printf "\n - Owmcert certificate already exists, using them..."
+ printf "\n - Owncert certificate already exists, using them..."
fi
;;
@@ -132,16 +150,17 @@ case ${CERTIFICATE_TYPE} in
/usr/sbin/crond -f &
echo '0 */12 * * * certbot renew --post-hook "nginx -s reload" >> /var/log/cron-letsencrypt.log' | crontab - # Auto renew cert
- if [[ ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
- ! -f "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
+ if [[ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/privkey.pem" && \
+ ! -f "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}/fullchain.pem" ]]; then
printf "\n - Requesting LetsEncrypt certificate..."
# Delete certificate folder
- rm -rf "${CERTIFICATES_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
+ rm -rf "${CERTIFICATES_LIVE_FOLDER:?}/${DOMAIN_OR_PUBLIC_IP}" | true
certbot certonly -n --webroot -w /var/www/certbot \
-m "${LETSENCRYPT_EMAIL}" \
- --agree-tos -d "${DOMAIN_OR_PUBLIC_IP}"
+ --agree-tos -d "${DOMAIN_OR_PUBLIC_IP}" \
+ `if [[ "${REDIRECT_WWW}" == "true" ]]; then echo "-d www.${DOMAIN_OR_PUBLIC_IP}" ; fi`
else
printf "\n - LetsEncrypt certificate already exists, using them..."
fi
@@ -153,27 +172,14 @@ chmod -R 777 /etc/letsencrypt
# Use certificates in folder '/default_nginx_conf'
if [ "${PROXY_MODE}" == "CE" ]; then
- if [ "${WITH_APP}" == "true" ]; then
- mv /default_nginx_conf/ce/default-app.conf /default_nginx_conf/default-app.conf
- mv /default_nginx_conf/ce/default.conf /default_nginx_conf/default.conf
- else
- mv /default_nginx_conf/ce/default-app-without-demos.conf /default_nginx_conf/default-app.conf
- mv /default_nginx_conf/ce/default.conf /default_nginx_conf/default.conf
- fi
-
- rm -rf /default_nginx_conf/ce
- rm -rf /default_nginx_conf/pro
+ # Remove previous configuration
+ [[ -f /default_nginx_conf/default.conf ]] && rm /default_nginx_conf/default.conf
+ cp /default_nginx_conf/ce/default.conf /default_nginx_conf/default.conf
fi
if [ "${PROXY_MODE}" == "PRO" ]; then
- if [ "${WITH_APP}" == "true" ]; then
- mv /default_nginx_conf/pro/default.conf /default_nginx_conf/default.conf
- else
- mv /default_nginx_conf/pro/default-app-without-demos.conf /default_nginx_conf/default.conf
- fi
-
- rm -rf /default_nginx_conf/ce
- rm -rf /default_nginx_conf/pro
+[[ -f /default_nginx_conf/default.conf ]] && rm /default_nginx_conf/default.conf
+ cp /default_nginx_conf/pro/default.conf /default_nginx_conf/default.conf
fi
# Create index.html
@@ -184,10 +190,85 @@ EOF
# Load nginx conf files
rm /etc/nginx/conf.d/*
-cp /default_nginx_conf/* /etc/nginx/conf.d
+
+# If custom config, don't generate configuration files
+if [[ -f /custom-nginx/custom-nginx.conf ]]; then
+ cp /custom-nginx/custom-nginx.conf /etc/nginx/conf.d/custom-nginx.conf
+ printf "\n"
+ printf "\n ======================================="
+ printf "\n = START OPENVIDU PROXY ="
+ printf "\n = WITH CUSTOM CONFIG ="
+ printf "\n ======================================="
+ printf "\n\n"
+ nginx -s reload
+
+ # nginx logs
+ tail -f /var/log/nginx/*.log
+ exit 0
+fi
+
+# Replace config files
+cp /default_nginx_conf/default* /etc/nginx/conf.d
+
+sed -e '/{ssl_config}/{r default_nginx_conf/global/ssl_config.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{proxy_config}/{r default_nginx_conf/global/proxy_config.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{nginx_status}/{r default_nginx_conf/global/nginx_status.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{common_api_ce}/{r default_nginx_conf/global/ce/common_api_ce.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{new_api_ce}/{r default_nginx_conf/global/ce/new_api_ce.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{common_api_pro}/{r default_nginx_conf/global/pro/common_api_pro.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{new_api_pro}/{r default_nginx_conf/global/pro/new_api_pro.conf' -e 'd}' -i /etc/nginx/conf.d/*
+
+if [[ "${WITH_APP}" == "true" ]]; then
+ sed -e '/{app_upstream}/{r default_nginx_conf/global/app_upstream.conf' -e 'd}' -i /etc/nginx/conf.d/*
+ sed -e '/{app_config}/{r default_nginx_conf/global/app_config.conf' -e 'd}' -i /etc/nginx/conf.d/*
+elif [[ "${WITH_APP}" == "false" ]]; then
+ sed -i '/{app_upstream}/d' /etc/nginx/conf.d/*
+ sed -e '/{app_config}/{r default_nginx_conf/global/app_config_default.conf' -e 'd}' -i /etc/nginx/conf.d/*
+fi
+
+if [[ "${SUPPORT_DEPRECATED_API}" == "true" ]]; then
+ sed -e '/{deprecated_api_ce}/{r default_nginx_conf/global/ce/deprecated_api_ce.conf' -e 'd}' -i /etc/nginx/conf.d/*
+ sed -e '/{deprecated_api_pro}/{r default_nginx_conf/global/pro/deprecated_api_pro.conf' -e 'd}' -i /etc/nginx/conf.d/*
+elif [[ "${SUPPORT_DEPRECATED_API}" == "false" ]]; then
+ sed -i '/{deprecated_api_ce}/d' /etc/nginx/conf.d/*
+ sed -i '/{deprecated_api_pro}/d' /etc/nginx/conf.d/*
+fi
+
+if [[ "${REDIRECT_WWW}" == "true" ]]; then
+ sed -e '/{redirect_www_ssl}/{r default_nginx_conf/global/redirect_www_ssl.conf' -e 'd}' -i /etc/nginx/conf.d/*
+ if [[ "${PROXY_MODE}" == "CE" ]]; then
+ sed -e '/{redirect_www}/{r default_nginx_conf/global/ce/redirect_www.conf' -e 'd}' -i /etc/nginx/conf.d/*
+ fi
+
+ if [ "${PROXY_MODE}" == "PRO" ]; then
+ sed -e '/{redirect_www}/{r default_nginx_conf/global/pro/redirect_www.conf' -e 'd}' -i /etc/nginx/conf.d/*
+ fi
+elif [[ "${REDIRECT_WWW}" == "false" ]]; then
+ sed -i '/{redirect_www}/d' /etc/nginx/conf.d/*
+ sed -i '/{redirect_www_ssl}/d' /etc/nginx/conf.d/*
+fi
+
+# Process main configs
+sed -e '/{ssl_config}/{r default_nginx_conf/global/ssl_config.conf' -e 'd}' -i /etc/nginx/conf.d/*
+sed -e '/{proxy_config}/{r default_nginx_conf/global/proxy_config.conf' -e 'd}' -i /etc/nginx/conf.d/*
sed -i "s/{domain_name}/${DOMAIN_OR_PUBLIC_IP}/g" /etc/nginx/conf.d/*
+
+# IPv6 listening (RFC 6540)
+if [ ! -f /proc/net/if_inet6 ]; then
+ sed -i '/\[::\]:{http_port}/d' /etc/nginx/conf.d/*
+ sed -i '/\[::\]:{https_port}/d' /etc/nginx/conf.d/*
+fi
+
sed -i "s/{http_port}/${PROXY_HTTP_PORT}/g" /etc/nginx/conf.d/*
sed -i "s/{https_port}/${PROXY_HTTPS_PORT}/g" /etc/nginx/conf.d/*
+sed -i "s/{ssl_protocols}/${PROXY_HTTPS_PROTOCOLS}/g" /etc/nginx/conf.d/*
+sed -i "s/{ssl_ciphers}/${PROXY_HTTPS_CIPHERS}/g" /etc/nginx/conf.d/*
+
+if [ -n "${PROXY_HTTPS_HSTS}" ]; then
+ sed -i "s/{add_header_hsts}/${PROXY_HTTPS_HSTS}/g" /etc/nginx/conf.d/*
+else
+ sed -i '/{add_header_hsts}/d' /etc/nginx/conf.d/*
+fi
# NGINX access
printf "\n"
@@ -197,8 +278,6 @@ printf "\n ======================================="
printf "\n"
printf "\n Adding rules..."
-LOCAL_NETWORKS=$(ip route list | grep -Eo '([0-9]*\.){3}[0-9]*/[0-9]*')
-PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh)
valid_ip_v4()
{
@@ -222,6 +301,23 @@ valid_ip_v6()
fi
}
+LOCAL_NETWORKS=$(ip route list | grep -Eo '([0-9]*\.){3}[0-9]*/[0-9]*')
+if [[ "${PUBLIC_IP}" == "auto-ipv4" ]]; then
+ PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh)
+ printf "\n - Public IPv4 for rules: %s" "$PUBLIC_IP"
+elif [[ "${PUBLIC_IP}" == "auto-ipv6" ]]; then
+ PUBLIC_IP=$(/usr/local/bin/discover_my_public_ip.sh --ipv6)
+ printf "\n - Public IPv6 for rules: %s" "$PUBLIC_IP"
+else
+ if valid_ip_v4 "$PUBLIC_IP"; then
+ printf "\n - Valid defined public IPv4: %s" "$PUBLIC_IP"
+ elif valid_ip_v6 "$PUBLIC_IP"; then
+ printf "\n - Valid defined public IPv6: %s" "$PUBLIC_IP"
+ else
+ printf "\n - Not valid defined IP Address: %s" "$PUBLIC_IP"
+ fi
+fi
+
if [ "${ALLOWED_ACCESS_TO_DASHBOARD}" != "all" ]; then
IFS=','
for IP in $(echo "${ALLOWED_ACCESS_TO_DASHBOARD}" | tr -d '[:space:]')
@@ -287,7 +383,7 @@ else
fi
if [ "${RULES_DASHBOARD}" != "allow all;" ]; then
- if ! echo "${RULES_DASHBOARD}" | grep -q "$PUBLIC_IP" && valid_ip_v4 "$PUBLIC_IP" || valid_ip_v6 "$IP"; then
+ if ! echo "${RULES_DASHBOARD}" | grep -q "$PUBLIC_IP" && valid_ip_v4 "$PUBLIC_IP" || valid_ip_v6 "$PUBLIC_IP"; then
RULES_DASHBOARD="${RULES_DASHBOARD}{new_line}allow $PUBLIC_IP;"
fi
@@ -305,7 +401,7 @@ if [ "${RULES_DASHBOARD}" != "allow all;" ]; then
fi
if [ "${RULES_RESTAPI}" != "allow all;" ]; then
- if ! echo "${RULES_RESTAPI}" | grep -q "$PUBLIC_IP" && valid_ip_v4 "$PUBLIC_IP" || valid_ip_v6 "$IP"; then
+ if ! echo "${RULES_RESTAPI}" | grep -q "$PUBLIC_IP" && valid_ip_v4 "$PUBLIC_IP" || valid_ip_v6 "$PUBLIC_IP"; then
RULES_RESTAPI="${RULES_RESTAPI}{new_line}allow $PUBLIC_IP;"
fi
diff --git a/openvidu-server/docker/openvidu-proxy/nginx.conf b/openvidu-server/docker/openvidu-proxy/nginx.conf
new file mode 100644
index 00000000..bd56d495
--- /dev/null
+++ b/openvidu-server/docker/openvidu-proxy/nginx.conf
@@ -0,0 +1,34 @@
+user nginx;
+worker_processes auto;
+
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+
+events {
+ worker_connections {worker_connections};
+}
+
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ server_tokens off;
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/vhost.d/*.conf;
+}
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-recording/create_image.sh b/openvidu-server/docker/openvidu-recording/create_image.sh
index b85144a8..46f1c62a 100755
--- a/openvidu-server/docker/openvidu-recording/create_image.sh
+++ b/openvidu-server/docker/openvidu-recording/create_image.sh
@@ -1,4 +1,6 @@
#!/bin/bash -x
+
+# eg: $ ./create_image.sh ubuntu-20-04 86.0.4240.193-1 2.16.0
OPENVIDU_RECORDING_UBUNTU_VERSION=$1
OPENVIDU_RECORDING_CHROME_VERSION=$2 # https://www.ubuntuupdates.org/package_logs?noppa=&page=1&type=ppas&vals=8#
OPENVIDU_RECORDING_DOCKER_TAG=$3
diff --git a/openvidu-server/docker/openvidu-recording/firefox/Dockerfile b/openvidu-server/docker/openvidu-recording/firefox/Dockerfile
index 98a6fcb1..46b9c210 100644
--- a/openvidu-server/docker/openvidu-recording/firefox/Dockerfile
+++ b/openvidu-server/docker/openvidu-recording/firefox/Dockerfile
@@ -1,5 +1,5 @@
FROM ubuntu:20.04
-MAINTAINER openvidu@gmail.com
+MAINTAINER info@openvidu.io
# Install packages
RUN apt-get update && apt-get -y upgrade && apt-get install -y \
diff --git a/openvidu-server/docker/openvidu-recording/scripts/composed.sh b/openvidu-server/docker/openvidu-recording/scripts/composed.sh
index fe70c076..226a9584 100644
--- a/openvidu-server/docker/openvidu-recording/scripts/composed.sh
+++ b/openvidu-server/docker/openvidu-recording/scripts/composed.sh
@@ -41,13 +41,16 @@ fi
chmod 777 /recordings/$VIDEO_ID
echo $RECORDING_JSON > /recordings/$VIDEO_ID/.recording.$VIDEO_ID
- pulseaudio -D
+ # Cleanup to be "stateless" on startup, otherwise pulseaudio daemon can't start
+ rm -rf /var/run/pulse /var/lib/pulse /root/.config/pulse
+ # Run pulseaudio
+ pulseaudio -D --system --disallow-exit --disallow-module-loading
### Start Chrome in headless mode with xvfb, using the display num previously obtained ###
touch xvfb.log
chmod 777 xvfb.log
- xvfb-run --auto-servernum --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required $DEBUG_CHROME_FLAGS $URL &> xvfb.log &
+ xvfb-run --auto-servernum --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --disable-dev-shm-usage --autoplay-policy=no-user-gesture-required $DEBUG_CHROME_FLAGS $URL &> xvfb.log &
touch stop
chmod 777 /recordings
diff --git a/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh b/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh
index 68fd2aab..0e58e72c 100644
--- a/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh
+++ b/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh
@@ -24,13 +24,16 @@ if [[ -z "${COMPOSED_QUICK_START_ACTION}" ]]; then
export HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)"
export RECORDING_MODE=${RECORDING_MODE}
- pulseaudio -D
+ # Cleanup to be "stateless" on startup, otherwise pulseaudio daemon can't start
+ rm -rf /var/run/pulse /var/lib/pulse /root/.config/pulse
+ # Run pulseaudio
+ pulseaudio -D --system --disallow-exit --disallow-module-loading
### Start Chrome in headless mode with xvfb, using the display num previously obtained ###
touch xvfb.log
chmod 777 xvfb.log
- xvfb-run --auto-servernum --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required --enable-logging --v=1 $DEBUG_CHROME_FLAGS $URL &> xvfb.log &
+ xvfb-run --auto-servernum --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --disable-dev-shm-usage --autoplay-policy=no-user-gesture-required --enable-logging --v=1 $DEBUG_CHROME_FLAGS $URL &> xvfb.log &
chmod 777 /recordings
until pids=$(pidof Xvfb)
@@ -96,6 +99,9 @@ elif [[ "${COMPOSED_QUICK_START_ACTION}" == "--start-recording" ]]; then
<./stop ffmpeg -y -f alsa -i pulse -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:a aac -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT"
fi
+ # Warn the stop thread about ffmpeg process being completed
+ echo "ffmpeg-completed" > /tmp/$VIDEO_ID-completed.txt
+
} 2>&1 | tee -a /tmp/container-start-recording.log
elif [[ "${COMPOSED_QUICK_START_ACTION}" == "--stop-recording" ]]; then
@@ -109,10 +115,17 @@ elif [[ "${COMPOSED_QUICK_START_ACTION}" == "--stop-recording" ]]; then
exit 0
fi
- # Stop and wait ffmpeg process to be stopped
+ # Stop ffmpeg process
FFMPEG_PID=$(pgrep ffmpeg)
echo 'q' > stop && tail --pid=$FFMPEG_PID -f /dev/null
+ ## Wait for the ffmpeg process to be finished
+ until [ -f /tmp/$VIDEO_ID-completed.txt ]
+ do
+ # Check 20 times per second
+ sleep 0.05
+ done
+ rm -f /tmp/$VIDEO_ID-completed.txt
### Generate video report file ###
ffprobe -v quiet -print_format json -show_format -show_streams /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT > /recordings/$VIDEO_ID/$VIDEO_ID.info
diff --git a/openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile b/openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile
index bee21093..efd7487a 100644
--- a/openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile
+++ b/openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile
@@ -1,5 +1,5 @@
FROM ubuntu:16.04
-MAINTAINER openvidu@gmail.com
+MAINTAINER info@openvidu.io
ARG CHROME_VERSION
@@ -19,6 +19,9 @@ RUN apt-get install -y ffmpeg pulseaudio xvfb
# Install jq for managing JSON
RUN apt-get install -y jq
+# Add root user to pulseaudio group
+RUN adduser root pulse-access
+
# Clean
RUN apt-get autoclean
diff --git a/openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile b/openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile
index 72f88a42..7f7c7eaf 100644
--- a/openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile
+++ b/openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile
@@ -1,5 +1,5 @@
FROM ubuntu:20.04
-MAINTAINER openvidu@gmail.com
+MAINTAINER info@openvidu.io
ARG CHROME_VERSION
@@ -23,6 +23,9 @@ RUN wget http://dl.google.com/linux/deb/pool/main/g/google-chrome-stable/google-
&& rm google-chrome-stable_${CHROME_VERSION}_amd64.deb \
&& google-chrome --version
+# Add root user to pulseaudio group
+RUN adduser root pulse-access
+
# Clean
RUN apt-get clean && apt-get autoclean && apt-get autoremove
diff --git a/openvidu-server/docker/openvidu-redis/entrypoint.sh b/openvidu-server/docker/openvidu-redis/entrypoint.sh
index 3a319247..2cb1dc5e 100644
--- a/openvidu-server/docker/openvidu-redis/entrypoint.sh
+++ b/openvidu-server/docker/openvidu-redis/entrypoint.sh
@@ -1,6 +1,10 @@
#!/bin/sh
-[ -z "${REDIS_BINDING}" ] && REDIS_BINDING="127.0.0.1"
+if [ -f /proc/net/if_inet6 ]; then
+ [ -z "${REDIS_BINDING}" ] && REDIS_BINDING="127.0.0.1 ::1"
+else
+ [ -z "${REDIS_BINDING}" ] && REDIS_BINDING="127.0.0.1"
+fi
printf "\n"
printf "\n ======================================="
diff --git a/openvidu-server/docker/openvidu-server-kms/Dockerfile b/openvidu-server/docker/openvidu-server-kms/Dockerfile
index a4d2b0cc..58950dad 100644
--- a/openvidu-server/docker/openvidu-server-kms/Dockerfile
+++ b/openvidu-server/docker/openvidu-server-kms/Dockerfile
@@ -1,18 +1,23 @@
-FROM ubuntu:16.04
-MAINTAINER openvidu@gmail.com
+FROM ubuntu:18.04
+MAINTAINER info@openvidu.io
+
+# Update and install dependencies
+RUN apt-get update && apt-get -y upgrade && \
+ apt-get install -y gnupg2 && \
+ rm -rf /var/lib/apt/lists/*
# Install Kurento Media Server (KMS)
-RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.14.0 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
- && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5AFA7A83 \
- && apt-get update \
- && apt-get -y install kurento-media-server \
- && rm -rf /var/lib/apt/lists/*
+RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.15.0 bionic kms6" | tee /etc/apt/sources.list.d/kurento.list && \
+ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5AFA7A83 && \
+ apt-get update && \
+ apt-get -y install kurento-media-server && \
+ rm -rf /var/lib/apt/lists/*
# Install Java, supervisor and netstat
RUN apt-get update && apt-get install -y \
- openjdk-8-jre \
- supervisor \
-&& rm -rf /var/lib/apt/lists/*
+ openjdk-11-jre \
+ supervisor && \
+ rm -rf /var/lib/apt/lists/*
# Configure supervisor
RUN mkdir -p /var/log/supervisor
@@ -24,6 +29,10 @@ COPY openvidu-server.jar openvidu-server.jar
# Copy KMS entrypoint
COPY kms.sh /kms.sh
+# Cleanup
+RUN rm -rf /var/lib/apt/lists/* && \
+ apt-get autoremove --purge -y && apt-get autoclean
+
EXPOSE 8888
EXPOSE 9091
EXPOSE 4443
diff --git a/openvidu-server/docker/openvidu-server-kms/create_image.sh b/openvidu-server/docker/openvidu-server-kms/create_image.sh
index 2f5c47f5..75610611 100755
--- a/openvidu-server/docker/openvidu-server-kms/create_image.sh
+++ b/openvidu-server/docker/openvidu-server-kms/create_image.sh
@@ -1,5 +1,8 @@
-cp ../../target/openvidu-server-"$1".jar ./openvidu-server.jar
-
-docker build -t openvidu/openvidu-server-kms .
-
-rm ./openvidu-server.jar
+VERSION=$1
+if [[ ! -z $VERSION ]]; then
+ cp ../../target/openvidu-server-*.jar ./openvidu-server.jar
+ docker build -t openvidu/openvidu-server-kms:$VERSION .
+ rm ./openvidu-server.jar
+else
+ echo "Error: You need to specify a version as first argument"
+fi
\ No newline at end of file
diff --git a/openvidu-server/docker/openvidu-server-pro/Dockerfile b/openvidu-server/docker/openvidu-server-pro/Dockerfile
index 4271b2ea..f85f777f 100644
--- a/openvidu-server/docker/openvidu-server-pro/Dockerfile
+++ b/openvidu-server/docker/openvidu-server-pro/Dockerfile
@@ -1,15 +1,17 @@
-FROM ubuntu:16.04
-MAINTAINER openvidu@gmail.com
+FROM ubuntu:18.04
+MAINTAINER info@openvidu.io
# Install main components
RUN apt-get update && apt-get install -y \
curl \
wget \
- openjdk-8-jre \
+ openjdk-11-jre \
coturn \
redis-tools \
jq \
docker.io \
+ ethtool \
+ dnsutils \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/openvidu /usr/local/bin/
diff --git a/openvidu-server/docker/openvidu-server-pro/create_image.sh b/openvidu-server/docker/openvidu-server-pro/create_image.sh
index 1a5e6430..82e6097d 100755
--- a/openvidu-server/docker/openvidu-server-pro/create_image.sh
+++ b/openvidu-server/docker/openvidu-server-pro/create_image.sh
@@ -1,9 +1,7 @@
VERSION=$1
if [[ ! -z $VERSION ]]; then
cp ../utils/discover_my_public_ip.sh ./discover_my_public_ip.sh
-
docker build -t openvidu/openvidu-server-pro:$VERSION .
-
rm ./discover_my_public_ip.sh
else
echo "Error: You need to specify a version as first argument"
diff --git a/openvidu-server/docker/openvidu-server-pro/entrypoint.sh b/openvidu-server/docker/openvidu-server-pro/entrypoint.sh
index b7b8ed9e..253a91c0 100755
--- a/openvidu-server/docker/openvidu-server-pro/entrypoint.sh
+++ b/openvidu-server/docker/openvidu-server-pro/entrypoint.sh
@@ -8,19 +8,12 @@ if [ ! -z "${WAIT_KIBANA_URL}" ]; then
printf "\n ======================================="
printf "\n"
- while true
- do
- HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${WAIT_KIBANA_URL}")
-
+ until $(curl --insecure --output /dev/null --silent --head --fail --max-time 10 --connect-timeout 10 ${WAIT_KIBANA_URL})
+ do
printf "\n Waiting for kibana in '%s' URL..." "${WAIT_KIBANA_URL}"
-
- if [ "$HTTP_STATUS" == "200" ]; then
- printf "\n ==== Kibana is Ready ===="
- break
- fi
-
sleep 1
done
+ printf "\n ==== Kibana is Ready ===="
fi
# Launch Openvidu Pro
@@ -30,6 +23,18 @@ printf "\n = LAUNCH OPENVIDU-SERVER ="
printf "\n ======================================="
printf "\n"
+# Get coturn public ip
+[[ -z "${COTURN_IP}" ]] && export COTURN_IP=auto-ipv4
+if [[ "${COTURN_IP}" == "auto-ipv4" ]]; then
+ COTURN_IP=$(/usr/local/bin/discover_my_public_ip.sh)
+elif [[ "${COTURN_IP}" == "auto-ipv6" ]]; then
+ COTURN_IP=$(/usr/local/bin/discover_my_public_ip.sh --ipv6)
+fi
+
+if [[ "${OV_CE_DEBUG_LEVEL}" == "DEBUG" ]]; then
+ export LOGGING_LEVEL_IO_OPENVIDU_SERVER=DEBUG
+fi
+
if [ ! -z "${JAVA_OPTIONS}" ]; then
printf "\n Using java options: %s" "${JAVA_OPTIONS}"
fi
diff --git a/openvidu-server/docker/openvidu-server/Dockerfile b/openvidu-server/docker/openvidu-server/Dockerfile
index 506a88f7..b6bed77d 100644
--- a/openvidu-server/docker/openvidu-server/Dockerfile
+++ b/openvidu-server/docker/openvidu-server/Dockerfile
@@ -1,13 +1,14 @@
-FROM ubuntu:16.04
-MAINTAINER openvidu@gmail.com
+FROM ubuntu:18.04
+MAINTAINER info@openvidu.io
# Install Java, supervisor and netstat
RUN apt-get update && apt-get install -y \
curl \
wget \
- openjdk-8-jre \
+ openjdk-11-jre \
coturn \
redis-tools \
+ dnsutils \
&& rm -rf /var/lib/apt/lists/*
# Copy OpenVidu Server
diff --git a/openvidu-server/docker/openvidu-server/entrypoint.sh b/openvidu-server/docker/openvidu-server/entrypoint.sh
index be6b71d0..7a509808 100644
--- a/openvidu-server/docker/openvidu-server/entrypoint.sh
+++ b/openvidu-server/docker/openvidu-server/entrypoint.sh
@@ -6,6 +6,18 @@ printf "\n = LAUNCH OPENVIDU-SERVER ="
printf "\n ======================================="
printf "\n"
+# Get coturn public ip
+[[ -z "${COTURN_IP}" ]] && export COTURN_IP=auto-ipv4
+if [[ "${COTURN_IP}" == "auto-ipv4" ]]; then
+ COTURN_IP=$(/usr/local/bin/discover_my_public_ip.sh)
+elif [[ "${COTURN_IP}" == "auto-ipv6" ]]; then
+ COTURN_IP=$(/usr/local/bin/discover_my_public_ip.sh --ipv6)
+fi
+
+if [[ "${OV_CE_DEBUG_LEVEL}" == "DEBUG" ]]; then
+ export LOGGING_LEVEL_IO_OPENVIDU_SERVER=DEBUG
+fi
+
if [ ! -z "${JAVA_OPTIONS}" ]; then
printf "\n Using java options: %s" "${JAVA_OPTIONS}"
fi
diff --git a/openvidu-server/docker/utils/discover_my_public_ip.sh b/openvidu-server/docker/utils/discover_my_public_ip.sh
index b42b3a97..e8227993 100755
--- a/openvidu-server/docker/utils/discover_my_public_ip.sh
+++ b/openvidu-server/docker/utils/discover_my_public_ip.sh
@@ -1,47 +1,116 @@
-#!/bin/bash
+#!/usr/bin/env bash
-# Check if a txt is a valid ip
-function valid_ip()
-{
- local ip=$1
- local stat=1
+#/ Use DNS to find out about the external IP of the running system.
+#/
+#/ This script is useful when running from a machine that sits behind a NAT.
+#/ Due to how NAT works, machines behind it belong to an internal or private
+#/ subnet, with a different address space than the external or public side.
+#/
+#/ Typically it is possible to make an HTTP request to a number of providers
+#/ that offer the external IP in their response body (eg: ifconfig.me). However,
+#/ why do a slow and heavy HTTP request, when DNS exists and is much faster?
+#/ Well established providers such as OpenDNS or Google offer special hostnames
+#/ that, when resolved, will actually return the IP address of the caller.
+#/
+#/ https://unix.stackexchange.com/questions/22615/how-can-i-get-my-external-ip-address-in-a-shell-script/81699#81699
+#/
+#/
+#/ Arguments
+#/ ---------
+#/
+#/ --ipv4
+#/
+#/ Find the external IPv4 address.
+#/ Optional. Default: Enabled.
+#/
+#/ --ipv6
+#/
+#/ Find the external IPv6 address.
+#/ Optional. Default: Disabled.
- if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
- OIFS=$IFS
- IFS='.'
- ip=($ip)
- IFS=$OIFS
- [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
- && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
- stat=$?
- fi
- return $stat
+
+
+# Shell setup
+# ===========
+
+# Bash options for strict error checking.
+set -o errexit -o errtrace -o pipefail -o nounset
+
+# Trace all commands (to stderr).
+#set -o xtrace
+
+# Trap function for unhandled errors.
+function on_error() {
+ echo "[getmyip] ERROR ($?)" >&2
+ exit 1
}
+trap on_error ERR
-# Services to get public ip
-SERVICES=(
- "curl --silent -sw :%{http_code} ipv4.icanhazip.com"
- "curl --silent -sw :%{http_code} ifconfig.me"
- "curl --silent -sw :%{http_code} -4 ifconfig.co"
- "curl --silent -sw :%{http_code} ipecho.net/plain"
- "curl --silent -sw :%{http_code} ipinfo.io/ip"
- "curl --silent -sw :%{http_code} checkip.amazonaws.com"
- "curl --silent -sw :%{http_code} v4.ident.me"
-)
-# Get public ip
-for service in "${SERVICES[@]}"; do
- RUN_COMMAND=$($service | tr -d '[:space:]')
- IP=$(echo "$RUN_COMMAND" | cut -d':' -f1)
- HTTP_CODE=$(echo "$RUN_COMMAND" | cut -d':' -f2)
- if [ "$HTTP_CODE" == "200" ]; then
- if valid_ip "$IP"; then
- printf "%s" "$IP"
- exit 0
- fi
+# Parse call arguments
+# ====================
+
+CFG_IPV4="true"
+CFG_IPV6="false"
+
+while [[ $# -gt 0 ]]; do
+ case "${1-}" in
+ --ipv4)
+ CFG_IPV4="true"
+ CFG_IPV6="false"
+ ;;
+ --ipv6)
+ CFG_IPV4="false"
+ CFG_IPV6="true"
+ ;;
+ *)
+ echo "Invalid argument: '${1-}'" >&2
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+
+
+# Obtain the external IP address
+# ==============================
+
+if [[ "$CFG_IPV4" == "true" ]]; then
+ COMMANDS=(
+ 'dig @resolver1.opendns.com myip.opendns.com A -4 +short'
+ 'dig @ns1.google.com o-o.myaddr.l.google.com TXT -4 +short | tr -d \"'
+ 'dig @1.1.1.1 whoami.cloudflare TXT CH -4 +short | tr -d \"'
+ 'dig @ns1-1.akamaitech.net whoami.akamai.net A -4 +short'
+ )
+
+ function is_valid_ip() {
+ # Check if the input looks like an IPv4 address.
+ # Doesn't check if the actual values are valid; assumes they are.
+ echo "$1" | grep --perl-regexp --quiet '^(\d{1,3}\.){3}\d{1,3}$'
+ }
+elif [[ "$CFG_IPV6" == "true" ]]; then
+ COMMANDS=(
+ 'dig @resolver1.opendns.com myip.opendns.com AAAA -6 +short'
+ 'dig @ns1.google.com o-o.myaddr.l.google.com TXT -6 +short | tr -d \"'
+ 'dig @2606:4700:4700::1111 whoami.cloudflare TXT CH -6 +short | tr -d \"'
+ )
+
+ function is_valid_ip() {
+ # Check if the input looks like an IPv6 address.
+ # It's almost impossible to check the IPv6 representation because it
+ # varies wildly, so just check that there are at least 2 colons.
+ [[ "$(echo "$1" | awk -F':' '{print NF-1}')" -ge 2 ]]
+ }
+fi
+
+for COMMAND in "${COMMANDS[@]}"; do
+ if IP="$(eval "$COMMAND")" && is_valid_ip "$IP"; then
+ echo "$IP"
+ exit 0
fi
done
-printf "error"
-exit 0
\ No newline at end of file
+echo "[getmyip] All providers failed" >&2
+exit 1
\ No newline at end of file
diff --git a/openvidu-server/pom.xml b/openvidu-server/pom.xml
index a2282893..bd2fbf0d 100644
--- a/openvidu-server/pom.xml
+++ b/openvidu-server/pom.xml
@@ -12,7 +12,7 @@
jar
OpenVidu Server
- 2.15.0
+ 2.16.0
OpenVidu Server
https://openvidu.io
@@ -187,6 +187,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework
+ spring-websocket
+
org.slf4j
slf4j-api
@@ -258,6 +262,11 @@
spring-boot-starter-logging
${version.spring-boot}
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+ ${version.spring-boot}
+
com.github.docker-java
docker-java
diff --git a/openvidu-server/src/dashboard/angular.json b/openvidu-server/src/dashboard/angular.json
index 9403c727..c94c23b1 100644
--- a/openvidu-server/src/dashboard/angular.json
+++ b/openvidu-server/src/dashboard/angular.json
@@ -38,7 +38,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
- "extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@@ -127,7 +126,7 @@
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
- "styleext": "css"
+ "style": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
diff --git a/openvidu-server/src/dashboard/package-lock.json b/openvidu-server/src/dashboard/package-lock.json
index 346a0f16..9af76751 100644
--- a/openvidu-server/src/dashboard/package-lock.json
+++ b/openvidu-server/src/dashboard/package-lock.json
@@ -5,211 +5,354 @@
"requires": true,
"dependencies": {
"@angular-devkit/architect": {
- "version": "0.901.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.901.1.tgz",
- "integrity": "sha512-foWDAurMfBDYLAJxHpTFkJBub1c2A8+eWHbBjgqIHmT8xadnE7t8nSA9XDl+k/kIoWw/qFU+6IttPirudYc/vw==",
+ "version": "0.1100.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.1.tgz",
+ "integrity": "sha512-DIAvTRRY+k7T2xHf4RVV06P16D0V7wSf1MSpGSDWVpfWcA3HNOSGfsk1F+COMlbFehXuKztwieXarv5rXbBCng==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
- "rxjs": "6.5.4"
+ "@angular-devkit/core": "11.0.1",
+ "rxjs": "6.6.3"
},
"dependencies": {
"rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
}
}
},
"@angular-devkit/build-angular": {
- "version": "0.901.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.901.1.tgz",
- "integrity": "sha512-6uEvo5htsJoxQHBVwHOGmM6YWq5q6m9UWMv/ughlek0RtSLFfOt9TZQ/yQHgtGQsCQvscD/jBzVoD0zD5Ax/SQ==",
+ "version": "0.1100.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.1.tgz",
+ "integrity": "sha512-w8NcoXuruOHio0D/JbX47iDl9FVH8X9k/OlZ/rSNVQ3uEpV6uxIaTm3fZ1ZSrKffi+97rKEwpHOf2N0DXl4XGQ==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.901.1",
- "@angular-devkit/build-optimizer": "0.901.1",
- "@angular-devkit/build-webpack": "0.901.1",
- "@angular-devkit/core": "9.1.1",
- "@babel/core": "7.9.0",
- "@babel/generator": "7.9.3",
- "@babel/preset-env": "7.9.0",
- "@babel/template": "7.8.6",
- "@jsdevtools/coverage-istanbul-loader": "3.0.3",
- "@ngtools/webpack": "9.1.1",
- "ajv": "6.12.0",
- "autoprefixer": "9.7.4",
- "babel-loader": "8.0.6",
+ "@angular-devkit/architect": "0.1100.1",
+ "@angular-devkit/build-optimizer": "0.1100.1",
+ "@angular-devkit/build-webpack": "0.1100.1",
+ "@angular-devkit/core": "11.0.1",
+ "@babel/core": "7.12.3",
+ "@babel/generator": "7.12.1",
+ "@babel/plugin-transform-runtime": "7.12.1",
+ "@babel/preset-env": "7.12.1",
+ "@babel/runtime": "7.12.1",
+ "@babel/template": "7.10.4",
+ "@jsdevtools/coverage-istanbul-loader": "3.0.5",
+ "@ngtools/webpack": "11.0.1",
+ "ansi-colors": "4.1.1",
+ "autoprefixer": "9.8.6",
+ "babel-loader": "8.1.0",
"browserslist": "^4.9.1",
- "cacache": "15.0.0",
+ "cacache": "15.0.5",
"caniuse-lite": "^1.0.30001032",
"circular-dependency-plugin": "5.2.0",
- "copy-webpack-plugin": "5.1.1",
- "core-js": "3.6.4",
+ "copy-webpack-plugin": "6.2.1",
+ "core-js": "3.6.5",
+ "css-loader": "5.0.0",
"cssnano": "4.1.10",
- "file-loader": "6.0.0",
+ "file-loader": "6.1.1",
"find-cache-dir": "3.3.1",
"glob": "7.1.6",
- "jest-worker": "25.1.0",
+ "inquirer": "7.3.3",
+ "jest-worker": "26.5.0",
"karma-source-map-support": "1.4.0",
- "less": "3.11.1",
- "less-loader": "5.0.0",
- "license-webpack-plugin": "2.1.4",
+ "less": "3.12.2",
+ "less-loader": "7.0.2",
+ "license-webpack-plugin": "2.3.1",
"loader-utils": "2.0.0",
- "mini-css-extract-plugin": "0.9.0",
+ "mini-css-extract-plugin": "1.2.1",
"minimatch": "3.0.4",
- "open": "7.0.3",
- "parse5": "4.0.0",
- "postcss": "7.0.27",
+ "open": "7.3.0",
+ "ora": "5.1.0",
+ "parse5-html-rewriting-stream": "6.0.1",
+ "pnp-webpack-plugin": "1.6.4",
+ "postcss": "7.0.32",
"postcss-import": "12.0.1",
- "postcss-loader": "3.0.0",
- "raw-loader": "4.0.0",
- "regenerator-runtime": "0.13.5",
+ "postcss-loader": "4.0.4",
+ "raw-loader": "4.0.2",
+ "regenerator-runtime": "0.13.7",
+ "resolve-url-loader": "3.1.2",
"rimraf": "3.0.2",
- "rollup": "2.1.0",
- "rxjs": "6.5.4",
- "sass": "1.26.3",
- "sass-loader": "8.0.2",
- "semver": "7.1.3",
+ "rollup": "2.32.1",
+ "rxjs": "6.6.3",
+ "sass": "1.27.0",
+ "sass-loader": "10.0.5",
+ "semver": "7.3.2",
"source-map": "0.7.3",
- "source-map-loader": "0.2.4",
- "source-map-support": "0.5.16",
- "speed-measure-webpack-plugin": "1.3.1",
- "style-loader": "1.1.3",
- "stylus": "0.54.7",
- "stylus-loader": "3.0.2",
- "terser": "4.6.10",
- "terser-webpack-plugin": "2.3.5",
+ "source-map-loader": "1.1.2",
+ "source-map-support": "0.5.19",
+ "speed-measure-webpack-plugin": "1.3.3",
+ "style-loader": "2.0.0",
+ "stylus": "0.54.8",
+ "stylus-loader": "4.1.1",
+ "terser": "5.3.7",
+ "terser-webpack-plugin": "4.2.3",
+ "text-table": "0.2.0",
"tree-kill": "1.2.2",
- "webpack": "4.42.0",
+ "webpack": "4.44.2",
"webpack-dev-middleware": "3.7.2",
- "webpack-dev-server": "3.10.3",
- "webpack-merge": "4.2.2",
- "webpack-sources": "1.4.3",
- "webpack-subresource-integrity": "1.4.0",
- "worker-plugin": "4.0.2"
+ "webpack-dev-server": "3.11.0",
+ "webpack-merge": "5.2.0",
+ "webpack-sources": "2.0.1",
+ "webpack-subresource-integrity": "1.5.1",
+ "worker-plugin": "5.0.0"
},
"dependencies": {
- "core-js": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
- "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz",
+ "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
- "parse5": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
- "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
"rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
}
}
}
},
"@angular-devkit/build-optimizer": {
- "version": "0.901.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.901.1.tgz",
- "integrity": "sha512-o0A9CcyDQSUnC5CQIKf92VH8amIYRYrMgLf2kdhSMcy0QV+rEJyN81dSvwX/Yxgnr9NbWEAQg7jnyKk06vfhOw==",
+ "version": "0.1100.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.1.tgz",
+ "integrity": "sha512-PpqBmDd+/cmaMj9MURe5pSSudo+Qz6BrGdzvYB16ekSp8bSDYLUriv5NvE/bm+ODKwo3jHgFrwWLiwK65vQcxQ==",
"dev": true,
"requires": {
"loader-utils": "2.0.0",
"source-map": "0.7.3",
- "tslib": "1.11.1",
- "typescript": "3.6.5",
- "webpack-sources": "1.4.3"
+ "tslib": "2.0.3",
+ "typescript": "4.0.5",
+ "webpack-sources": "2.0.1"
},
"dependencies": {
- "typescript": {
- "version": "3.6.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.5.tgz",
- "integrity": "sha512-BEjlc0Z06ORZKbtcxGrIvvwYs5hAnuo6TKdNFL55frVDlB+na3z5bsLhFaIxmT+dPWgBIjMo6aNnTOgHHmHgiQ==",
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==",
"dev": true
}
}
},
"@angular-devkit/build-webpack": {
- "version": "0.901.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.901.1.tgz",
- "integrity": "sha512-9oNI+wPSk8yECy+f0EebfMx4PH3uDJRrifYZAxcr84IpzEbpfpRuYhE3ecwqd7k0zu2Kdjw1uUrGxBuH4/sbGg==",
+ "version": "0.1100.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.1.tgz",
+ "integrity": "sha512-nfgsUfP6WyZ35rxgjqDYydB552Si/JdYLMtwy/kAFybW/6yTpw0sBOgCQoppyQ4mvVwyX9X0ZTQsMNhPOzy3sA==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.901.1",
- "@angular-devkit/core": "9.1.1",
- "rxjs": "6.5.4"
+ "@angular-devkit/architect": "0.1100.1",
+ "@angular-devkit/core": "11.0.1",
+ "rxjs": "6.6.3"
},
"dependencies": {
"rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
}
}
},
"@angular-devkit/core": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.1.1.tgz",
- "integrity": "sha512-57MNew2u1QwVb69jxZyhXgdW9kqcGyWyRy2ui/hWCkWLg7RumWtyypmdTs89FNExB4HqtXlQ2eO3JZxfs7QR3w==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.1.tgz",
+ "integrity": "sha512-ui3g7w/0SpU9oq8uwN9upR8Y1eOXZ+P2p3NyDydBrR7ZEfEkRLS1mhozN/ib8farrwK5N3kIIJxMb5t3187Hng==",
"dev": true,
"requires": {
- "ajv": "6.12.0",
+ "ajv": "6.12.6",
"fast-json-stable-stringify": "2.1.0",
"magic-string": "0.25.7",
- "rxjs": "6.5.4",
+ "rxjs": "6.6.3",
"source-map": "0.7.3"
},
"dependencies": {
"rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
}
}
},
"@angular-devkit/schematics": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-9.1.1.tgz",
- "integrity": "sha512-6wx2HcvafHvEjEa1tjDzW2hXrOiSE8ALqJUArb3+NoO1BDM42aGcqyPo0ODzKtDk12CgSsFXdNKRpQ5AmpSPtw==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.1.tgz",
+ "integrity": "sha512-rAOnAndcybEH398xf5wzmcUPCoCi0dKiOo/+1dkKU5aTxynw1OUnANt5K6A+ZZTGnJmfjtP0ovkZGYun9IUDxQ==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
- "ora": "4.0.3",
- "rxjs": "6.5.4"
+ "@angular-devkit/core": "11.0.1",
+ "ora": "5.1.0",
+ "rxjs": "6.6.3"
},
"dependencies": {
"rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
}
}
},
"@angular/animations": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.1.2.tgz",
- "integrity": "sha512-5UJ8SzCtFj4vZChVsni4K9oa4qE9tQ67bwnP6DKxkLEJKQWWyasYp+2siAi/7zD2ro2XA0qRMYhgQz5Vj6eBoQ=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.0.tgz",
+ "integrity": "sha512-RGaAnZOI73bPnNWrJq/p8sc+hpUBhScq139M6r4qQjQPsPahazL6v6hHAgRhZNemqw164d1oE4K/22O/i0E3Tw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/cdk": {
"version": "9.2.1",
@@ -220,63 +363,98 @@
}
},
"@angular/cli": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-9.1.1.tgz",
- "integrity": "sha512-sjRAV4UF8M5v+2gw+EwCYSgciBZDc05AbNxQt+uUdxdfR1QU9hifWq8WDxfOR6jdDP5YqMtQsNaFNwrUyjJJoQ==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.1.tgz",
+ "integrity": "sha512-zB20jTLQxLpkJjhbYelhRyMcgGsjwbD8pSYYAO6QX656Tx1tCtJ2UskEtf4ePQvgzu0Ds2dnAEfco+tekIMRTA==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.901.1",
- "@angular-devkit/core": "9.1.1",
- "@angular-devkit/schematics": "9.1.1",
- "@schematics/angular": "9.1.1",
- "@schematics/update": "0.901.1",
+ "@angular-devkit/architect": "0.1100.1",
+ "@angular-devkit/core": "11.0.1",
+ "@angular-devkit/schematics": "11.0.1",
+ "@schematics/angular": "11.0.1",
+ "@schematics/update": "0.1100.1",
"@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.1",
- "debug": "4.1.1",
+ "debug": "4.2.0",
"ini": "1.3.5",
- "inquirer": "7.1.0",
- "npm-package-arg": "8.0.1",
- "npm-pick-manifest": "6.0.0",
- "open": "7.0.3",
+ "inquirer": "7.3.3",
+ "npm-package-arg": "8.1.0",
+ "npm-pick-manifest": "6.1.0",
+ "open": "7.3.0",
"pacote": "9.5.12",
- "read-package-tree": "5.3.1",
+ "resolve": "1.18.1",
"rimraf": "3.0.2",
- "semver": "7.1.3",
- "symbol-observable": "1.2.0",
- "universal-analytics": "0.4.20",
- "uuid": "7.0.2"
+ "semver": "7.3.2",
+ "symbol-observable": "2.0.3",
+ "universal-analytics": "0.4.23",
+ "uuid": "8.3.1"
},
"dependencies": {
- "ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "resolve": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
+ "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.0.0",
+ "path-parse": "^1.0.6"
+ }
},
"uuid": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
- "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==",
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
+ "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==",
"dev": true
}
}
},
"@angular/common": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.1.2.tgz",
- "integrity": "sha512-MAQW0DGq2NhvJSETLTwuImdzwI+wboG+5Uzgc0L8C/hX7SrEE65Hmz4nIjmGi2CPGLpodfWEcCPlV0R0dHun4A=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-11.0.0.tgz",
+ "integrity": "sha512-chlbtxR7jpPs3Rc1ymdp3UfUzqEr57OFIxVMG6hROODclPQQk/7oOHdQB4hpUObaF9y4ZTLeKHKWiR/twi21Pg==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/compiler": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.2.tgz",
- "integrity": "sha512-82DitvlZA5DoE++qiiJdgx6HE71h96sfLHCjjvYJHApoOcnX+zBZFBrflpbXK9W4YdvydU4Lc1Ql1eleQPeT/g=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.0.0.tgz",
+ "integrity": "sha512-I7wVhdqvhtBTQTtW61z0lwPb1LiQQ0NOwjsbfN5sAc7/uwxw7em+Kyb/XJgBwgaTKtAL8bZEzdoQGLdsSKQF2g==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/compiler-cli": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.1.2.tgz",
- "integrity": "sha512-zC/oIuT68vtuiYrgXqWsDNi/0DQ1Ti6J6gMTrXVvZXlEDikEExTAJKrrBV5jo6B0bpUofe/dDcJaKR3Ys3cM3Q==",
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-11.0.0.tgz",
+ "integrity": "sha512-zrd/cU9syZ8XuQ3ItfIGaKDn1ZBCWyiqdLVRH9VDmyNqQFiCc/VWQ9Th9z8qpLptgdpzE9+lKFgeZJTDtbcveQ==",
"dev": true,
"requires": {
+ "@babel/core": "^7.8.6",
+ "@babel/types": "^7.8.6",
"canonical-path": "1.0.0",
"chokidar": "^3.0.0",
"convert-source-map": "^1.5.1",
@@ -288,25 +466,25 @@
"semver": "^6.3.0",
"source-map": "^0.6.1",
"sourcemap-codec": "^1.4.8",
+ "tslib": "^2.0.0",
"yargs": "15.3.0"
},
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -343,18 +521,6 @@
"path-exists": "^4.0.0"
}
},
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -364,15 +530,6 @@
"p-locate": "^4.1.0"
}
},
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
@@ -382,24 +539,12 @@
"p-limit": "^2.2.0"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -412,25 +557,11 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==",
+ "dev": true
},
"wrap-ansi": {
"version": "6.2.0",
@@ -463,9 +594,9 @@
}
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
@@ -475,9 +606,19 @@
}
},
"@angular/core": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.1.2.tgz",
- "integrity": "sha512-nAqRl+5drAnZlBT7AgSo2JJubmPNrGCvhZ83dosPEUqJoLr69/lYipnF/iqKzYn5xo4MM1O27WIhSNDtDjBmzQ=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.0.tgz",
+ "integrity": "sha512-FNewyMwYy+kGdw1xWfrtaPD2cSQs3kDVFbl8mNMSzp933W5yMsHDvjXb0+nPFqEb8ywEIdm3MsBMK0y3iBWZQw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/flex-layout": {
"version": "9.0.0-beta.29",
@@ -485,14 +626,24 @@
"integrity": "sha512-93sxR+kYfYMOdnlWL0Q77FZ428gg8XnBu0YZm6GsCdkw/vLggIT/G1ZAqHlCPIODt6pxmCJ5KXh4ShvniIYDsA=="
},
"@angular/forms": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.1.2.tgz",
- "integrity": "sha512-/f2WhMiKDo1/RoisTI71Dy4Z4+sAsAuzgIxJEXvgDGSYvzLl9G8erFx4N6be8Cy/xXwErmp3JOwXIAXjzxF8tA=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.0.tgz",
+ "integrity": "sha512-hP6GF1ZkxKQp7Y+EVbEe9PPDQPrUQNdfVxphCWQYwu3tm8+tn1r91KVXkp2MA3M4Fh6Xo2HQEU2d+VXv4w0iNQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/language-service": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-9.1.2.tgz",
- "integrity": "sha512-0BnDIFbIAtFKS/2HDI0efcnW3DSkAAFhUWluBnssvwGzuMHikOKGeamuWM27ki3IzPQKGJ7fGS2fz8A7sMeIYQ==",
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-11.0.0.tgz",
+ "integrity": "sha512-lwUVlaiIASNbKQ/EtCK5KOVIlpiyVvysN6idAD0rJHr6BRtrlqwiayNYbV5as5IJyPYLf2E8au3an9j0E/PFDw==",
"dev": true
},
"@angular/material": {
@@ -501,19 +652,49 @@
"integrity": "sha512-nqn/0Eg04DxwnkRGSM1xnmGgtfHYOBcEPbFeTu8c1qAbjFEozd6tpw4y6dQrCCL/JLNIRQPsxsUsVnKeWDF/4Q=="
},
"@angular/platform-browser": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.2.tgz",
- "integrity": "sha512-/3L4DdvnebvaJUurusaq8RJBFfr/SHWG6DMmV1VVpADxe8kjREyN0LdNDSkZgVf/QcUSwNEA6153iwcF92Otew=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.0.tgz",
+ "integrity": "sha512-p8sF6JfaBI+YyLpp5OSg6UcCqjtLKRR+Otq1P/tro5SuxrsrBNRVU8j0tl/crkScsMwAvgmJ1joRyUKdI2mUGQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/platform-browser-dynamic": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.2.tgz",
- "integrity": "sha512-BWHxy8S71z+NmUQmtR+/Dkohlj3LQIXltOqeVdCSpjV9cultBNN3bE1w0Rjp3BmCRGCIDH7qFlr4U5woHa7ldw=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.0.tgz",
+ "integrity": "sha512-NAmKGhHK+tl7dr/Hcqxvr/813Opec3Mv0IRwIgmKdlpZd7qAwT/mw4RnO4YPSEoDOM6hqGt7GdlWrSDX802duQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@angular/router": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.2.tgz",
- "integrity": "sha512-csxE4HkuhVR1X932Q3kSDqBoF7Awuq5dsjv0nFk78raiHgG3CNnfMLHt8xD9XtOmR7ZT+D4yh/YmIK6W7J5hbQ=="
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-11.0.0.tgz",
+ "integrity": "sha512-10ZeobfK3HqVeWS6zjdKU16ccxFtdCHkxT11bnFg3Jwq9vKt+LI5KitAkCI5rYTY3DRfVzasRkqBzZfZMkbftw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
+ }
+ }
},
"@babel/code-frame": {
"version": "7.8.3",
@@ -525,48 +706,152 @@
}
},
"@babel/compat-data": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz",
- "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==",
- "dev": true,
- "requires": {
- "browserslist": "^4.9.1",
- "invariant": "^2.2.4",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz",
+ "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==",
+ "dev": true
},
"@babel/core": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
- "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
+ "version": "7.12.3",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
+ "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.0",
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helpers": "^7.9.0",
- "@babel/parser": "^7.9.0",
- "@babel/template": "^7.8.6",
- "@babel/traverse": "^7.9.0",
- "@babel/types": "^7.9.0",
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.1",
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helpers": "^7.12.1",
+ "@babel/parser": "^7.12.3",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.12.1",
+ "@babel/types": "^7.12.1",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.2",
- "lodash": "^4.17.13",
+ "lodash": "^4.17.19",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
},
"dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
+ "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz",
+ "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.5",
+ "@babel/types": "^7.12.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -581,55 +866,84 @@
}
}
},
- "@babel/generator": {
- "version": "7.9.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.3.tgz",
- "integrity": "sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==",
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
+ "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
"dev": true,
"requires": {
- "@babel/types": "^7.9.0",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
+ "@babel/types": "^7.10.4"
},
"dependencies": {
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
}
},
- "@babel/helper-annotate-as-pure": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz",
- "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
"@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz",
- "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz",
+ "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==",
"dev": true,
"requires": {
- "@babel/helper-explode-assignable-expression": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/helper-explode-assignable-expression": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-compilation-targets": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz",
- "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==",
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz",
+ "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==",
"dev": true,
"requires": {
- "@babel/compat-data": "^7.8.6",
- "browserslist": "^4.9.1",
- "invariant": "^2.2.4",
- "levenary": "^1.1.1",
+ "@babel/compat-data": "^7.12.5",
+ "@babel/helper-validator-option": "^7.12.1",
+ "browserslist": "^4.14.5",
"semver": "^5.5.0"
},
"dependencies": {
@@ -641,166 +955,788 @@
}
}
},
- "@babel/helper-create-regexp-features-plugin": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz",
- "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==",
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz",
+ "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==",
"dev": true,
"requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-regex": "^7.8.3",
- "regexpu-core": "^4.7.0"
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-member-expression-to-functions": "^7.12.1",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-create-regexp-features-plugin": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz",
+ "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-regex": "^7.10.4",
+ "regexpu-core": "^4.7.1"
}
},
"@babel/helper-define-map": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz",
- "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==",
+ "version": "7.10.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz",
+ "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==",
"dev": true,
"requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/types": "^7.8.3",
- "lodash": "^4.17.13"
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/types": "^7.10.5",
+ "lodash": "^4.17.19"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-explode-assignable-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz",
- "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz",
+ "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==",
"dev": true,
"requires": {
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-function-name": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
- "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
- "dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.9.5"
- }
- },
- "@babel/helper-get-function-arity": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
- "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-hoist-variables": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz",
- "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz",
+ "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-member-expression-to-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
- "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz",
+ "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz",
+ "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.12.5"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-module-transforms": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
- "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz",
+ "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==",
"dev": true,
"requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-simple-access": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/template": "^7.8.6",
- "@babel/types": "^7.9.0",
- "lodash": "^4.17.13"
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-simple-access": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.12.1",
+ "@babel/types": "^7.12.1",
+ "lodash": "^4.17.19"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
+ "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz",
+ "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.5",
+ "@babel/types": "^7.12.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"@babel/helper-optimise-call-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
- "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz",
+ "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+ "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
"dev": true
},
"@babel/helper-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz",
- "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==",
+ "version": "7.10.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz",
+ "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==",
"dev": true,
"requires": {
- "lodash": "^4.17.13"
+ "lodash": "^4.17.19"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-remap-async-to-generator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz",
- "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz",
+ "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==",
"dev": true,
"requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-wrap-function": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-wrap-function": "^7.10.4",
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-replace-supers": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
- "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz",
+ "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==",
"dev": true,
"requires": {
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.6",
- "@babel/types": "^7.8.6"
+ "@babel/helper-member-expression-to-functions": "^7.12.1",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/traverse": "^7.12.5",
+ "@babel/types": "^7.12.5"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
+ "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz",
+ "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.5",
+ "@babel/types": "^7.12.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"@babel/helper-simple-access": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz",
- "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz",
+ "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==",
"dev": true,
"requires": {
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
- "@babel/helper-split-export-declaration": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
- "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz",
+ "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/helper-validator-identifier": {
@@ -809,27 +1745,283 @@
"integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
"dev": true
},
+ "@babel/helper-validator-option": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz",
+ "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==",
+ "dev": true
+ },
"@babel/helper-wrap-function": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz",
- "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==",
+ "version": "7.12.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz",
+ "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==",
"dev": true,
"requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
+ "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz",
+ "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.5",
+ "@babel/types": "^7.12.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"@babel/helpers": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz",
- "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==",
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz",
+ "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==",
"dev": true,
"requires": {
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.9.0",
- "@babel/types": "^7.9.0"
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.12.5",
+ "@babel/types": "^7.12.5"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
+ "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz",
+ "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.5",
+ "@babel/types": "^7.12.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
}
},
"@babel/highlight": {
@@ -843,102 +2035,137 @@
"js-tokens": "^4.0.0"
}
},
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
"@babel/plugin-proposal-async-generator-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz",
- "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz",
+ "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-remap-async-to-generator": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-remap-async-to-generator": "^7.12.1",
"@babel/plugin-syntax-async-generators": "^7.8.0"
}
},
- "@babel/plugin-proposal-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==",
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz",
+ "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-create-class-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz",
+ "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-dynamic-import": "^7.8.0"
}
},
- "@babel/plugin-proposal-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz",
- "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==",
+ "@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz",
+ "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz",
+ "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-json-strings": "^7.8.0"
}
},
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
+ "@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz",
+ "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz",
+ "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
}
},
"@babel/plugin-proposal-numeric-separator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz",
- "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==",
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz",
+ "integrity": "sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
}
},
"@babel/plugin-proposal-object-rest-spread": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz",
- "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz",
+ "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-object-rest-spread": "^7.8.0",
- "@babel/plugin-transform-parameters": "^7.9.5"
+ "@babel/plugin-transform-parameters": "^7.12.1"
}
},
"@babel/plugin-proposal-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz",
+ "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-optional-catch-binding": "^7.8.0"
}
},
"@babel/plugin-proposal-optional-chaining": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz",
- "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz",
+ "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1",
"@babel/plugin-syntax-optional-chaining": "^7.8.0"
}
},
- "@babel/plugin-proposal-unicode-property-regex": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz",
- "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==",
+ "@babel/plugin-proposal-private-methods": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz",
+ "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==",
"dev": true,
"requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.8",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-create-class-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz",
+ "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-async-generators": {
@@ -950,6 +2177,15 @@
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz",
+ "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
"@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -959,6 +2195,15 @@
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-syntax-json-strings": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
@@ -968,6 +2213,15 @@
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
"@babel/plugin-syntax-nullish-coalescing-operator": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
@@ -978,12 +2232,12 @@
}
},
"@babel/plugin-syntax-numeric-separator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz",
- "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==",
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-object-rest-spread": {
@@ -1014,386 +2268,453 @@
}
},
"@babel/plugin-syntax-top-level-await": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz",
- "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz",
+ "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-arrow-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
- "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz",
+ "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-async-to-generator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz",
- "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz",
+ "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==",
"dev": true,
"requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-remap-async-to-generator": "^7.8.3"
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-remap-async-to-generator": "^7.12.1"
}
},
"@babel/plugin-transform-block-scoped-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz",
- "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz",
+ "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-block-scoping": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz",
- "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz",
+ "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "lodash": "^4.17.13"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-classes": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz",
- "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz",
+ "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==",
"dev": true,
"requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-define-map": "^7.8.3",
- "@babel/helper-function-name": "^7.9.5",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-define-map": "^7.10.4",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.10.4",
"globals": "^11.1.0"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/plugin-transform-computed-properties": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz",
- "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz",
+ "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-destructuring": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz",
- "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz",
+ "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-dotall-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz",
- "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz",
+ "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==",
"dev": true,
"requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-duplicate-keys": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz",
- "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz",
+ "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-exponentiation-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz",
- "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz",
+ "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==",
"dev": true,
"requires": {
- "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-for-of": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz",
- "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz",
+ "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-function-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz",
- "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz",
+ "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==",
"dev": true,
"requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
+ "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ }
}
},
"@babel/plugin-transform-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz",
- "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz",
+ "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-member-expression-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz",
- "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz",
+ "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-modules-amd": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz",
- "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz",
+ "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==",
"dev": true,
"requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
}
},
"@babel/plugin-transform-modules-commonjs": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz",
- "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz",
+ "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==",
"dev": true,
"requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-simple-access": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-simple-access": "^7.12.1",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
}
},
"@babel/plugin-transform-modules-systemjs": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz",
- "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz",
+ "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==",
"dev": true,
"requires": {
- "@babel/helper-hoist-variables": "^7.8.3",
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
+ "@babel/helper-hoist-variables": "^7.10.4",
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ }
}
},
"@babel/plugin-transform-modules-umd": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz",
- "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz",
+ "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==",
"dev": true,
"requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz",
- "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz",
+ "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==",
"dev": true,
"requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3"
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1"
}
},
"@babel/plugin-transform-new-target": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz",
- "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz",
+ "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-object-super": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz",
- "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz",
+ "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1"
}
},
"@babel/plugin-transform-parameters": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz",
- "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz",
+ "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==",
"dev": true,
"requires": {
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-property-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz",
- "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz",
+ "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-transform-regenerator": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz",
- "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz",
+ "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==",
"dev": true,
"requires": {
"regenerator-transform": "^0.14.2"
}
},
"@babel/plugin-transform-reserved-words": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz",
- "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz",
+ "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
+ "@babel/helper-plugin-utils": "^7.10.4"
}
},
- "@babel/plugin-transform-shorthand-properties": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz",
- "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==",
+ "@babel/plugin-transform-runtime": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz",
+ "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==",
"dev": true,
"requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz",
- "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-sticky-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz",
- "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-regex": "^7.8.3"
- }
- },
- "@babel/plugin-transform-template-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz",
- "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==",
- "dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-typeof-symbol": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz",
- "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-unicode-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
- "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==",
- "dev": true,
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/preset-env": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz",
- "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.9.0",
- "@babel/helper-compilation-targets": "^7.8.7",
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-proposal-async-generator-functions": "^7.8.3",
- "@babel/plugin-proposal-dynamic-import": "^7.8.3",
- "@babel/plugin-proposal-json-strings": "^7.8.3",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-proposal-numeric-separator": "^7.8.3",
- "@babel/plugin-proposal-object-rest-spread": "^7.9.0",
- "@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
- "@babel/plugin-proposal-optional-chaining": "^7.9.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
- "@babel/plugin-syntax-async-generators": "^7.8.0",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0",
- "@babel/plugin-syntax-json-strings": "^7.8.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
- "@babel/plugin-syntax-numeric-separator": "^7.8.0",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0",
- "@babel/plugin-syntax-top-level-await": "^7.8.3",
- "@babel/plugin-transform-arrow-functions": "^7.8.3",
- "@babel/plugin-transform-async-to-generator": "^7.8.3",
- "@babel/plugin-transform-block-scoped-functions": "^7.8.3",
- "@babel/plugin-transform-block-scoping": "^7.8.3",
- "@babel/plugin-transform-classes": "^7.9.0",
- "@babel/plugin-transform-computed-properties": "^7.8.3",
- "@babel/plugin-transform-destructuring": "^7.8.3",
- "@babel/plugin-transform-dotall-regex": "^7.8.3",
- "@babel/plugin-transform-duplicate-keys": "^7.8.3",
- "@babel/plugin-transform-exponentiation-operator": "^7.8.3",
- "@babel/plugin-transform-for-of": "^7.9.0",
- "@babel/plugin-transform-function-name": "^7.8.3",
- "@babel/plugin-transform-literals": "^7.8.3",
- "@babel/plugin-transform-member-expression-literals": "^7.8.3",
- "@babel/plugin-transform-modules-amd": "^7.9.0",
- "@babel/plugin-transform-modules-commonjs": "^7.9.0",
- "@babel/plugin-transform-modules-systemjs": "^7.9.0",
- "@babel/plugin-transform-modules-umd": "^7.9.0",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3",
- "@babel/plugin-transform-new-target": "^7.8.3",
- "@babel/plugin-transform-object-super": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.8.7",
- "@babel/plugin-transform-property-literals": "^7.8.3",
- "@babel/plugin-transform-regenerator": "^7.8.7",
- "@babel/plugin-transform-reserved-words": "^7.8.3",
- "@babel/plugin-transform-shorthand-properties": "^7.8.3",
- "@babel/plugin-transform-spread": "^7.8.3",
- "@babel/plugin-transform-sticky-regex": "^7.8.3",
- "@babel/plugin-transform-template-literals": "^7.8.3",
- "@babel/plugin-transform-typeof-symbol": "^7.8.4",
- "@babel/plugin-transform-unicode-regex": "^7.8.3",
- "@babel/preset-modules": "^0.1.3",
- "@babel/types": "^7.9.0",
- "browserslist": "^4.9.1",
- "core-js-compat": "^3.6.2",
- "invariant": "^2.2.2",
- "levenary": "^1.1.1",
- "semver": "^5.5.0"
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "resolve": "^1.8.1",
+ "semver": "^5.5.1"
},
"dependencies": {
"semver": {
@@ -1404,10 +2725,181 @@
}
}
},
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz",
+ "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz",
+ "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz",
+ "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-regex": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz",
+ "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz",
+ "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-unicode-escapes": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz",
+ "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz",
+ "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz",
+ "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.12.1",
+ "@babel/helper-compilation-targets": "^7.12.1",
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-validator-option": "^7.12.1",
+ "@babel/plugin-proposal-async-generator-functions": "^7.12.1",
+ "@babel/plugin-proposal-class-properties": "^7.12.1",
+ "@babel/plugin-proposal-dynamic-import": "^7.12.1",
+ "@babel/plugin-proposal-export-namespace-from": "^7.12.1",
+ "@babel/plugin-proposal-json-strings": "^7.12.1",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
+ "@babel/plugin-proposal-numeric-separator": "^7.12.1",
+ "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.12.1",
+ "@babel/plugin-proposal-optional-chaining": "^7.12.1",
+ "@babel/plugin-proposal-private-methods": "^7.12.1",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.12.1",
+ "@babel/plugin-syntax-async-generators": "^7.8.0",
+ "@babel/plugin-syntax-class-properties": "^7.12.1",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.0",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.0",
+ "@babel/plugin-syntax-top-level-await": "^7.12.1",
+ "@babel/plugin-transform-arrow-functions": "^7.12.1",
+ "@babel/plugin-transform-async-to-generator": "^7.12.1",
+ "@babel/plugin-transform-block-scoped-functions": "^7.12.1",
+ "@babel/plugin-transform-block-scoping": "^7.12.1",
+ "@babel/plugin-transform-classes": "^7.12.1",
+ "@babel/plugin-transform-computed-properties": "^7.12.1",
+ "@babel/plugin-transform-destructuring": "^7.12.1",
+ "@babel/plugin-transform-dotall-regex": "^7.12.1",
+ "@babel/plugin-transform-duplicate-keys": "^7.12.1",
+ "@babel/plugin-transform-exponentiation-operator": "^7.12.1",
+ "@babel/plugin-transform-for-of": "^7.12.1",
+ "@babel/plugin-transform-function-name": "^7.12.1",
+ "@babel/plugin-transform-literals": "^7.12.1",
+ "@babel/plugin-transform-member-expression-literals": "^7.12.1",
+ "@babel/plugin-transform-modules-amd": "^7.12.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.12.1",
+ "@babel/plugin-transform-modules-systemjs": "^7.12.1",
+ "@babel/plugin-transform-modules-umd": "^7.12.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1",
+ "@babel/plugin-transform-new-target": "^7.12.1",
+ "@babel/plugin-transform-object-super": "^7.12.1",
+ "@babel/plugin-transform-parameters": "^7.12.1",
+ "@babel/plugin-transform-property-literals": "^7.12.1",
+ "@babel/plugin-transform-regenerator": "^7.12.1",
+ "@babel/plugin-transform-reserved-words": "^7.12.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.12.1",
+ "@babel/plugin-transform-spread": "^7.12.1",
+ "@babel/plugin-transform-sticky-regex": "^7.12.1",
+ "@babel/plugin-transform-template-literals": "^7.12.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.12.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.12.1",
+ "@babel/plugin-transform-unicode-regex": "^7.12.1",
+ "@babel/preset-modules": "^0.1.3",
+ "@babel/types": "^7.12.1",
+ "core-js-compat": "^3.6.2",
+ "semver": "^5.5.0"
+ },
+ "dependencies": {
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/types": {
+ "version": "7.12.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
+ "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
"@babel/preset-modules": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz",
- "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz",
+ "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -1418,62 +2910,14 @@
}
},
"@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz",
+ "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
- "@babel/template": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
- "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.6",
- "@babel/types": "^7.8.6"
- }
- },
- "@babel/traverse": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz",
- "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.5",
- "@babel/helper-function-name": "^7.9.5",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.9.0",
- "@babel/types": "^7.9.5",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- },
- "dependencies": {
- "@babel/generator": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz",
- "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.9.5",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
- }
- }
- },
"@babel/types": {
"version": "7.9.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
@@ -1492,128 +2936,119 @@
"dev": true
},
"@jsdevtools/coverage-istanbul-loader": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.3.tgz",
- "integrity": "sha512-TAdNkeGB5Fe4Og+ZkAr1Kvn9by2sfL44IAHFtxlh1BA1XJ5cLpO9iSNki5opWESv3l3vSHsZ9BNKuqFKbEbFaA==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz",
+ "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==",
"dev": true,
"requires": {
"convert-source-map": "^1.7.0",
- "istanbul-lib-instrument": "^4.0.1",
- "loader-utils": "^1.4.0",
+ "istanbul-lib-instrument": "^4.0.3",
+ "loader-utils": "^2.0.0",
"merge-source-map": "^1.1.0",
- "schema-utils": "^2.6.4"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- }
+ "schema-utils": "^2.7.0"
}
},
"@ngtools/webpack": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.1.1.tgz",
- "integrity": "sha512-4RPlk6aIlYhk9isTvXbMaA2G0LhxOzcZ+2iG7zV9Yj/Vm8+lrRexpQ/kC/Dh0GI/oCtKIkVpUzx5LTozYeTLdQ==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.1.tgz",
+ "integrity": "sha512-z62qQ4J5LhDxW68HjYYCRo+sDK/5yHwX4fCCY2iXngyTtA5cQbGI5WXr3+9B4foX64ft5WvV0WJkx8mjE/VR6w==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
- "enhanced-resolve": "4.1.1",
- "rxjs": "6.5.4",
- "webpack-sources": "1.4.3"
+ "@angular-devkit/core": "11.0.1",
+ "enhanced-resolve": "5.3.1",
+ "webpack-sources": "2.0.1"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+ "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.3",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+ "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+ "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.3",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@npmcli/move-file": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz",
+ "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4"
},
"dependencies": {
- "rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
}
}
},
"@schematics/angular": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-9.1.1.tgz",
- "integrity": "sha512-V0DcDNgHQ2YR+PGZI6+pf/mUNNxt5SusShkZ1PbwIMk/HUQpzEGkLjm3v1Jw9eIZKiuDx615GNU1xDzQ/KyNRQ==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.1.tgz",
+ "integrity": "sha512-cYq3NhFn4DLSXXtbYYU2w0sginkMfN1w7pXjZLT/+etXXbtANQAXSPrPrDQql004ZNMbuDKuC0aoXjv8hgXOfw==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
- "@angular-devkit/schematics": "9.1.1"
+ "@angular-devkit/core": "11.0.1",
+ "@angular-devkit/schematics": "11.0.1",
+ "jsonc-parser": "2.3.1"
}
},
"@schematics/update": {
- "version": "0.901.1",
- "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.901.1.tgz",
- "integrity": "sha512-g5B8hjXKCbUNLKCkWQxc5r2D9lOQXRTLZJNIqva+l/ck0AML5MpelxkqQId9ZGLfQqfFk/XjdSHRWgcmUs1WnA==",
+ "version": "0.1100.1",
+ "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.1.tgz",
+ "integrity": "sha512-EVcqdM/d5rC5L1UYnwhFMk/TjHlNgL5LGfroE13C38A+WpKKJquAjgOQLj4nPvJ5csdEZqn3Sui9yeEWc3hklQ==",
"dev": true,
"requires": {
- "@angular-devkit/core": "9.1.1",
- "@angular-devkit/schematics": "9.1.1",
+ "@angular-devkit/core": "11.0.1",
+ "@angular-devkit/schematics": "11.0.1",
"@yarnpkg/lockfile": "1.1.0",
"ini": "1.3.5",
"npm-package-arg": "^8.0.0",
"pacote": "9.5.12",
- "rxjs": "6.5.4",
- "semver": "7.1.3",
+ "semver": "7.3.2",
"semver-intersect": "1.4.0"
- },
- "dependencies": {
- "rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
- }
}
},
- "@types/color-name": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
- "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
- "dev": true
- },
- "@types/events": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
- "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
- "dev": true
- },
"@types/glob": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
- "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
"dev": true,
"requires": {
- "@types/events": "*",
"@types/minimatch": "*",
"@types/node": "*"
}
},
"@types/jasmine": {
- "version": "3.5.10",
- "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.10.tgz",
- "integrity": "sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.1.tgz",
+ "integrity": "sha512-eeSCVhBsgwHNS1FmaMu4zrLxfykCTWJMLFZv7lmyrZQjw7foUUXoPu4GukSN9v7JvUw7X+/aDH3kCaymirBSTg==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
+ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/minimatch": {
@@ -1628,10 +3063,21 @@
"integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==",
"dev": true
},
+ "@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+ "dev": true
+ },
+ "@types/platform": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/platform/-/platform-1.3.3.tgz",
+ "integrity": "sha512-1fuOulBHWIxAPLBtLms+UtbeRDt6rL7gP5R+Yugfzdg+poCLxXqXTE8i+FpYeiytGRLUEtnFkjsY/j+usbQBqw=="
+ },
"@types/q": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
- "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
+ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
"dev": true
},
"@types/selenium-webdriver": {
@@ -1647,9 +3093,9 @@
"dev": true
},
"@types/webpack-sources": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz",
- "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==",
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz",
+ "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==",
"dev": true,
"requires": {
"@types/node": "*",
@@ -1666,178 +3112,177 @@
}
},
"@webassemblyjs/ast": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
- "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+ "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
"dev": true,
"requires": {
- "@webassemblyjs/helper-module-context": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/wast-parser": "1.8.5"
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0"
}
},
"@webassemblyjs/floating-point-hex-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
- "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+ "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
"dev": true
},
"@webassemblyjs/helper-api-error": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
- "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+ "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
"dev": true
},
"@webassemblyjs/helper-buffer": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
- "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+ "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
"dev": true
},
"@webassemblyjs/helper-code-frame": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
- "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+ "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
"dev": true,
"requires": {
- "@webassemblyjs/wast-printer": "1.8.5"
+ "@webassemblyjs/wast-printer": "1.9.0"
}
},
"@webassemblyjs/helper-fsm": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
- "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+ "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
"dev": true
},
"@webassemblyjs/helper-module-context": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
- "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+ "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "mamacro": "^0.0.3"
+ "@webassemblyjs/ast": "1.9.0"
}
},
"@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
- "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+ "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
"dev": true
},
"@webassemblyjs/helper-wasm-section": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
- "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+ "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5"
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0"
}
},
"@webassemblyjs/ieee754": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
- "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+ "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
"dev": true,
"requires": {
"@xtuc/ieee754": "^1.2.0"
}
},
"@webassemblyjs/leb128": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
- "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+ "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
"dev": true,
"requires": {
"@xtuc/long": "4.2.2"
}
},
"@webassemblyjs/utf8": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
- "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+ "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
"dev": true
},
"@webassemblyjs/wasm-edit": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
- "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+ "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/helper-wasm-section": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5",
- "@webassemblyjs/wasm-opt": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5",
- "@webassemblyjs/wast-printer": "1.8.5"
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/helper-wasm-section": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-opt": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "@webassemblyjs/wast-printer": "1.9.0"
}
},
"@webassemblyjs/wasm-gen": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
- "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+ "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/ieee754": "1.8.5",
- "@webassemblyjs/leb128": "1.8.5",
- "@webassemblyjs/utf8": "1.8.5"
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
}
},
"@webassemblyjs/wasm-opt": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
- "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+ "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5"
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0"
}
},
"@webassemblyjs/wasm-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
- "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+ "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-api-error": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/ieee754": "1.8.5",
- "@webassemblyjs/leb128": "1.8.5",
- "@webassemblyjs/utf8": "1.8.5"
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
}
},
"@webassemblyjs/wast-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
- "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+ "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/floating-point-hex-parser": "1.8.5",
- "@webassemblyjs/helper-api-error": "1.8.5",
- "@webassemblyjs/helper-code-frame": "1.8.5",
- "@webassemblyjs/helper-fsm": "1.8.5",
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-code-frame": "1.9.0",
+ "@webassemblyjs/helper-fsm": "1.9.0",
"@xtuc/long": "4.2.2"
}
},
"@webassemblyjs/wast-printer": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
- "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+ "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/wast-parser": "1.8.5",
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0",
"@xtuc/long": "4.2.2"
}
},
@@ -1869,6 +3314,12 @@
"through": ">=2.2.7 <3"
}
},
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -1880,15 +3331,25 @@
}
},
"acorn": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
- "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
"dev": true
},
+ "adjust-sourcemap-loader": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz",
+ "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "regex-parser": "^2.2.11"
+ }
+ },
"adm-zip": {
- "version": "0.4.14",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
- "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==",
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
"dev": true
},
"after": {
@@ -1916,9 +3377,9 @@
}
},
"aggregate-error": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
- "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"requires": {
"clean-stack": "^2.0.0",
@@ -1926,9 +3387,9 @@
}
},
"ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -1944,9 +3405,9 @@
"dev": true
},
"ajv-keywords": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
- "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
},
"alphanum-sort": {
@@ -1956,9 +3417,9 @@
"dev": true
},
"ansi-colors": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
- "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
"dev": true
},
"ansi-escapes": {
@@ -1977,9 +3438,9 @@
"dev": true
},
"ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
@@ -2002,20 +3463,11 @@
}
},
"app-root-path": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz",
- "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
+ "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==",
"dev": true
},
- "append-transform": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
- "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
- "dev": true,
- "requires": {
- "default-require-extensions": "^2.0.0"
- }
- },
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -2047,6 +3499,12 @@
"commander": "^2.11.0"
}
},
+ "arity-n": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
+ "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=",
+ "dev": true
+ },
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -2072,13 +3530,10 @@
"dev": true
},
"array-union": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
- "dev": true,
- "requires": {
- "array-uniq": "^1.0.1"
- }
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
},
"array-uniq": {
"version": "1.0.3",
@@ -2104,12 +3559,6 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
- "asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
- "dev": true
- },
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -2120,14 +3569,23 @@
}
},
"asn1.js": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
- "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"dev": true,
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"assert": {
@@ -2209,18 +3667,18 @@
"dev": true
},
"autoprefixer": {
- "version": "9.7.4",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz",
- "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==",
+ "version": "9.8.6",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
+ "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==",
"dev": true,
"requires": {
- "browserslist": "^4.8.3",
- "caniuse-lite": "^1.0.30001020",
- "chalk": "^2.4.2",
+ "browserslist": "^4.12.0",
+ "caniuse-lite": "^1.0.30001109",
+ "colorette": "^1.2.1",
"normalize-range": "^0.1.2",
"num2fraction": "^1.2.2",
- "postcss": "^7.0.26",
- "postcss-value-parser": "^4.0.2"
+ "postcss": "^7.0.32",
+ "postcss-value-parser": "^4.1.0"
}
},
"aws-sign2": {
@@ -2230,9 +3688,9 @@
"dev": true
},
"aws4": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true
},
"axobject-query": {
@@ -2245,15 +3703,16 @@
}
},
"babel-loader": {
- "version": "8.0.6",
- "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
- "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz",
+ "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==",
"dev": true,
"requires": {
- "find-cache-dir": "^2.0.0",
- "loader-utils": "^1.0.2",
- "mkdirp": "^0.5.1",
- "pify": "^4.0.1"
+ "find-cache-dir": "^2.1.0",
+ "loader-utils": "^1.4.0",
+ "mkdirp": "^0.5.3",
+ "pify": "^4.0.1",
+ "schema-utils": "^2.6.5"
},
"dependencies": {
"find-cache-dir": {
@@ -2290,9 +3749,9 @@
}
},
"babel-plugin-dynamic-import-node": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz",
- "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
"dev": true,
"requires": {
"object.assign": "^4.1.0"
@@ -2362,25 +3821,31 @@
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
}
}
},
"base64-arraybuffer": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
- "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+ "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
"dev": true
},
"base64-js": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
- "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true
},
"base64id": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
- "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
"dev": true
},
"batch": {
@@ -2414,9 +3879,9 @@
"dev": true
},
"binary-extensions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
- "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
"dev": true
},
"blob": {
@@ -2441,9 +3906,9 @@
"dev": true
},
"bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz",
+ "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==",
"dev": true
},
"body-parser": {
@@ -2484,12 +3949,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
- },
- "qs": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
- "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
- "dev": true
}
}
},
@@ -2576,28 +4035,49 @@
}
},
"browserify-rsa": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
- "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
+ "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
"dev": true,
"requires": {
- "bn.js": "^4.1.0",
+ "bn.js": "^5.0.0",
"randombytes": "^2.0.1"
}
},
"browserify-sign": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+ "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
"dev": true,
"requires": {
- "bn.js": "^4.1.1",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.2",
- "elliptic": "^6.0.0",
- "inherits": "^2.0.1",
- "parse-asn1": "^5.0.0"
+ "bn.js": "^5.1.1",
+ "browserify-rsa": "^4.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.3",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.5",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
}
},
"browserify-zlib": {
@@ -2610,15 +4090,16 @@
}
},
"browserslist": {
- "version": "4.11.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz",
- "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==",
+ "version": "4.14.7",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz",
+ "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30001038",
- "electron-to-chromium": "^1.3.390",
- "node-releases": "^1.1.53",
- "pkg-up": "^2.0.0"
+ "caniuse-lite": "^1.0.30001157",
+ "colorette": "^1.2.1",
+ "electron-to-chromium": "^1.3.591",
+ "escalade": "^3.1.1",
+ "node-releases": "^1.1.66"
}
},
"browserstack": {
@@ -2684,27 +4165,27 @@
"dev": true
},
"cacache": {
- "version": "15.0.0",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.0.tgz",
- "integrity": "sha512-L0JpXHhplbJSiDGzyJJnJCTL7er7NzbBgxzVqLswEb4bO91Zbv17OUMuUeu/q0ZwKn3V+1HM4wb9tO4eVE/K8g==",
+ "version": "15.0.5",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz",
+ "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==",
"dev": true,
"requires": {
- "chownr": "^1.1.2",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"glob": "^7.1.4",
"infer-owner": "^1.0.4",
- "lru-cache": "^5.1.1",
+ "lru-cache": "^6.0.0",
"minipass": "^3.1.1",
"minipass-collect": "^1.0.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.2",
"mkdirp": "^1.0.3",
- "move-concurrently": "^1.0.1",
- "p-map": "^3.0.0",
+ "p-map": "^4.0.0",
"promise-inflight": "^1.0.1",
- "rimraf": "^2.7.1",
+ "rimraf": "^3.0.2",
"ssri": "^8.0.0",
- "tar": "^6.0.1",
+ "tar": "^6.0.2",
"unique-filename": "^1.1.1"
},
"dependencies": {
@@ -2713,15 +4194,6 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
}
}
},
@@ -2740,6 +4212,24 @@
"to-object-path": "^0.3.0",
"union-value": "^1.0.0",
"unset-value": "^1.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ }
+ }
+ },
+ "call-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz",
+ "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.0"
}
},
"caller-callsite": {
@@ -2773,9 +4263,9 @@
"dev": true
},
"camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
+ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
"dev": true
},
"caniuse-api": {
@@ -2791,9 +4281,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001042",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz",
- "integrity": "sha512-igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw==",
+ "version": "1.0.30001158",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001158.tgz",
+ "integrity": "sha512-s5loVYY+yKpuVA3HyW8BarzrtJvwHReuzugQXlv1iR3LKSReoFXRm86mT6hT7PEF5RxW+XQZg+6nYjlywYzQ+g==",
"dev": true
},
"canonical-path": {
@@ -2826,9 +4316,9 @@
"dev": true
},
"chokidar": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
- "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
+ "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
@@ -2838,24 +4328,13 @@
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.3.0"
- },
- "dependencies": {
- "glob-parent": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
- "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- }
+ "readdirp": "~3.5.0"
}
},
"chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"dev": true
},
"chrome-trace-event": {
@@ -2865,6 +4344,14 @@
"dev": true,
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
},
"cipher-base": {
@@ -2903,6 +4390,12 @@
"requires": {
"is-descriptor": "^0.1.0"
}
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
}
}
},
@@ -2922,49 +4415,72 @@
}
},
"cli-spinners": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz",
- "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz",
+ "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==",
"dev": true
},
"cli-width": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
- "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
"dev": true
},
"cliui": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
- "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0",
- "wrap-ansi": "^2.0.0"
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
- "ansi-regex": "^3.0.0"
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
}
}
}
},
"clone": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
- "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
"clone-deep": {
@@ -2989,29 +4505,40 @@
"q": "^1.1.2"
}
},
- "code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
- "dev": true
- },
"codelyzer": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz",
- "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz",
+ "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==",
"dev": true,
"requires": {
- "app-root-path": "^2.2.1",
+ "@angular/compiler": "9.0.0",
+ "@angular/core": "9.0.0",
+ "app-root-path": "^3.0.0",
"aria-query": "^3.0.0",
"axobject-query": "2.0.2",
"css-selector-tokenizer": "^0.7.1",
"cssauron": "^1.4.0",
"damerau-levenshtein": "^1.0.4",
+ "rxjs": "^6.5.3",
"semver-dsl": "^1.0.1",
"source-map": "^0.5.7",
- "sprintf-js": "^1.1.2"
+ "sprintf-js": "^1.1.2",
+ "tslib": "^1.10.0",
+ "zone.js": "~0.10.3"
},
"dependencies": {
+ "@angular/compiler": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz",
+ "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==",
+ "dev": true
+ },
+ "@angular/core": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz",
+ "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==",
+ "dev": true
+ },
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -3023,6 +4550,12 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
}
}
},
@@ -3037,13 +4570,13 @@
}
},
"color": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
- "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
+ "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
"dev": true,
"requires": {
"color-convert": "^1.9.1",
- "color-string": "^1.5.2"
+ "color-string": "^1.5.4"
}
},
"color-convert": {
@@ -3062,15 +4595,21 @@
"dev": true
},
"color-string": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
- "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
+ "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
"dev": true,
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
+ "colorette": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
+ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
+ "dev": true
+ },
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
@@ -3098,12 +4637,6 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
- "compare-versions": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
- "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
- "dev": true
- },
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
@@ -3122,6 +4655,15 @@
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
"dev": true
},
+ "compose-function": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz",
+ "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=",
+ "dev": true,
+ "requires": {
+ "arity-n": "^1.0.4"
+ }
+ },
"compressible": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
@@ -3296,121 +4838,58 @@
"dev": true
},
"copy-webpack-plugin": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz",
- "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz",
+ "integrity": "sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q==",
"dev": true,
"requires": {
- "cacache": "^12.0.3",
- "find-cache-dir": "^2.1.0",
- "glob-parent": "^3.1.0",
- "globby": "^7.1.1",
- "is-glob": "^4.0.1",
- "loader-utils": "^1.2.3",
- "minimatch": "^3.0.4",
+ "cacache": "^15.0.5",
+ "fast-glob": "^3.2.4",
+ "find-cache-dir": "^3.3.1",
+ "glob-parent": "^5.1.1",
+ "globby": "^11.0.1",
+ "loader-utils": "^2.0.0",
"normalize-path": "^3.0.0",
- "p-limit": "^2.2.1",
- "schema-utils": "^1.0.0",
- "serialize-javascript": "^2.1.2",
- "webpack-log": "^2.0.0"
+ "p-limit": "^3.0.2",
+ "schema-utils": "^3.0.0",
+ "serialize-javascript": "^5.0.1",
+ "webpack-sources": "^1.4.3"
},
"dependencies": {
- "cacache": {
- "version": "12.0.4",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
- "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
- "dev": true,
- "requires": {
- "bluebird": "^3.5.5",
- "chownr": "^1.1.1",
- "figgy-pudding": "^3.5.1",
- "glob": "^7.1.4",
- "graceful-fs": "^4.1.15",
- "infer-owner": "^1.0.3",
- "lru-cache": "^5.1.1",
- "mississippi": "^3.0.0",
- "mkdirp": "^0.5.1",
- "move-concurrently": "^1.0.1",
- "promise-inflight": "^1.0.1",
- "rimraf": "^2.6.3",
- "ssri": "^6.0.1",
- "unique-filename": "^1.1.1",
- "y18n": "^4.0.0"
- }
- },
- "find-cache-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
- "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^2.0.0",
- "pkg-dir": "^3.0.0"
- }
- },
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
"p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"dev": true,
"requires": {
- "glob": "^7.1.3"
- }
- },
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- },
- "ssri": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
- "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
- "dev": true,
- "requires": {
- "figgy-pudding": "^3.5.1"
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
}
}
}
@@ -3421,12 +4900,12 @@
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
},
"core-js-compat": {
- "version": "3.6.5",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
- "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz",
+ "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==",
"dev": true,
"requires": {
- "browserslist": "^4.8.5",
+ "browserslist": "^4.14.6",
"semver": "7.0.0"
},
"dependencies": {
@@ -3457,13 +4936,21 @@
}
},
"create-ecdh": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
- "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
"dev": true,
"requires": {
"bn.js": "^4.1.0",
- "elliptic": "^6.0.0"
+ "elliptic": "^6.5.3"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"create-hash": {
@@ -3569,6 +5056,57 @@
"timsort": "^0.3.0"
}
},
+ "css-loader": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.0.0.tgz",
+ "integrity": "sha512-9g35eXRBgjvswyJWoqq/seWp+BOxvUl8IinVNTsUBFFxtwfEYvlmEn6ciyn0liXGbGh5HyJjPGCuobDSfqMIVg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^6.1.0",
+ "cssesc": "^3.0.0",
+ "icss-utils": "^5.0.0",
+ "loader-utils": "^2.0.0",
+ "postcss": "^8.1.1",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.1.0",
+ "schema-utils": "^3.0.0",
+ "semver": "^7.3.2"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "8.1.7",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.7.tgz",
+ "integrity": "sha512-llCQW1Pz4MOPwbZLmOddGM9eIJ8Bh7SZ2Oj5sxZva77uVaotYDsYTch1WBTNu7fUY0fpWp0fdt7uW40D4sRiiQ==",
+ "dev": true,
+ "requires": {
+ "colorette": "^1.2.1",
+ "line-column": "^1.0.2",
+ "nanoid": "^3.1.16",
+ "source-map": "^0.6.1"
+ }
+ },
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"css-parse": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
@@ -3597,14 +5135,13 @@
"dev": true
},
"css-selector-tokenizer": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz",
- "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==",
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+ "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
- "fastparse": "^1.1.2",
- "regexpu-core": "^4.6.0"
+ "fastparse": "^1.1.2"
}
},
"css-tree": {
@@ -3626,9 +5163,9 @@
}
},
"css-what": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz",
- "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
+ "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
"dev": true
},
"cssauron": {
@@ -3724,28 +5261,28 @@
"dev": true
},
"csso": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz",
- "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-4.1.1.tgz",
+ "integrity": "sha512-Rvq+e1e0TFB8E8X+8MQjHSY6vtol45s5gxtLI/018UsAn2IBMmwNEZRM/h+HVnAJRHjasLIKKUO3uvoMM28LvA==",
"dev": true,
"requires": {
- "css-tree": "1.0.0-alpha.39"
+ "css-tree": "^1.0.0"
},
"dependencies": {
"css-tree": {
- "version": "1.0.0-alpha.39",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz",
- "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.1.tgz",
+ "integrity": "sha512-WroX+2MvsYcRGP8QA0p+rxzOniT/zpAoQ/DTKDSJzh5T3IQKUkFHeIIfgIapm2uaP178GWY3Mime1qbk8GO/tA==",
"dev": true,
"requires": {
- "mdn-data": "2.0.6",
+ "mdn-data": "2.0.12",
"source-map": "^0.6.1"
}
},
"mdn-data": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz",
- "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==",
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.12.tgz",
+ "integrity": "sha512-ULbAlgzVb8IqZ0Hsxm6hHSlQl3Jckst2YEQS7fODu9ilNWy2LvcoSY7TRFIktABP2mdppBioc66va90T+NUs8Q==",
"dev": true
},
"source-map": {
@@ -3768,6 +5305,16 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
"damerau-levenshtein": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
@@ -3784,9 +5331,9 @@
}
},
"date-format": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
- "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
+ "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==",
"dev": true
},
"debug": {
@@ -3798,12 +5345,6 @@
"ms": "^2.1.1"
}
},
- "debuglog": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
- "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
- "dev": true
- },
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@@ -3840,15 +5381,6 @@
"ip-regex": "^2.1.0"
}
},
- "default-require-extensions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
- "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
- "dev": true,
- "requires": {
- "strip-bom": "^3.0.0"
- }
- },
"defaults": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
@@ -3856,14 +5388,6 @@
"dev": true,
"requires": {
"clone": "^1.0.2"
- },
- "dependencies": {
- "clone": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
- "dev": true
- }
}
},
"define-properties": {
@@ -3913,6 +5437,12 @@
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
}
}
},
@@ -3931,6 +5461,15 @@
"rimraf": "^2.6.3"
},
"dependencies": {
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
"globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
@@ -4009,16 +5548,6 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
- "dezalgo": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
- "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
- "dev": true,
- "requires": {
- "asap": "^2.0.0",
- "wrappy": "1"
- }
- },
"di": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
@@ -4040,15 +5569,23 @@
"bn.js": "^4.1.0",
"miller-rabin": "^4.0.0",
"randombytes": "^2.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"dir-glob": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
- "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"requires": {
- "path-type": "^3.0.0"
+ "path-type": "^4.0.0"
}
},
"dns-equal": {
@@ -4099,9 +5636,9 @@
},
"dependencies": {
"domelementtype": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
- "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
+ "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
"dev": true
}
}
@@ -4129,9 +5666,9 @@
}
},
"dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
"dev": true,
"requires": {
"is-obj": "^2.0.0"
@@ -4166,15 +5703,15 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.409",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.409.tgz",
- "integrity": "sha512-CB2HUXiMsaVYY5VvcpELhDShiTRhI2FfN7CuacEZ5mDmMFuSG/ZVm8HoSya0+S61RvUd3TjIjFSKywqHZpRPzQ==",
+ "version": "1.3.596",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.596.tgz",
+ "integrity": "sha512-nLO2Wd2yU42eSoNJVQKNf89CcEGqeFZd++QsnN2XIgje1s/19AgctfjLIbPORlvcCO8sYjLwX4iUgDdusOY8Sg==",
"dev": true
},
"elliptic": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
- "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
+ "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -4184,6 +5721,14 @@
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"emoji-regex": {
@@ -4205,12 +5750,23 @@
"dev": true
},
"encoding": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
- "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"requires": {
- "iconv-lite": "~0.4.13"
+ "iconv-lite": "^0.6.2"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+ "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
}
},
"end-of-stream": {
@@ -4223,17 +5779,17 @@
}
},
"engine.io": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
- "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
+ "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
"dev": true,
"requires": {
"accepts": "~1.3.4",
- "base64id": "1.0.0",
+ "base64id": "2.0.0",
"cookie": "0.3.1",
- "debug": "~3.1.0",
- "engine.io-parser": "~2.1.0",
- "ws": "~3.3.1"
+ "debug": "~4.1.0",
+ "engine.io-parser": "~2.2.0",
+ "ws": "^7.1.2"
},
"dependencies": {
"cookie": {
@@ -4242,59 +5798,33 @@
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
"dev": true
},
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
"ws": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
- "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
- "dev": true,
- "requires": {
- "async-limiter": "~1.0.0",
- "safe-buffer": "~5.1.0",
- "ultron": "~1.1.0"
- }
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
+ "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==",
+ "dev": true
}
}
},
"engine.io-client": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
- "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
+ "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
"dev": true,
"requires": {
- "component-emitter": "1.2.1",
+ "component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
- "engine.io-parser": "~2.1.1",
+ "engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
- "parseqs": "0.0.5",
- "parseuri": "0.0.5",
- "ws": "~3.3.1",
+ "parseqs": "0.0.6",
+ "parseuri": "0.0.6",
+ "ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
- "component-emitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
- "dev": true
- },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@@ -4310,41 +5840,50 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
+ "parseqs": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+ "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==",
+ "dev": true
+ },
+ "parseuri": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+ "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==",
+ "dev": true
+ },
"ws": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
- "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
+ "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"dev": true,
"requires": {
- "async-limiter": "~1.0.0",
- "safe-buffer": "~5.1.0",
- "ultron": "~1.1.0"
+ "async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
- "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
+ "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
"dev": true,
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
- "base64-arraybuffer": "0.1.5",
+ "base64-arraybuffer": "0.1.4",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"enhanced-resolve": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
- "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz",
+ "integrity": "sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==",
"dev": true,
"requires": {
- "graceful-fs": "^4.1.2",
- "memory-fs": "^0.5.0",
- "tapable": "^1.0.0"
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.0.0"
}
},
"ent": {
@@ -4354,9 +5893,9 @@
"dev": true
},
"entities": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
- "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+ "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
"dev": true
},
"err-code": {
@@ -4384,22 +5923,22 @@
}
},
"es-abstract": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
- "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
}
},
"es-to-primitive": {
@@ -4413,6 +5952,28 @@
"is-symbol": "^1.0.2"
}
},
+ "es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
@@ -4428,6 +5989,22 @@
"es6-promise": "^4.0.3"
}
},
+ "es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "dev": true,
+ "requires": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -4457,12 +6034,20 @@
"dev": true
},
"esrecurse": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
- "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"requires": {
- "estraverse": "^4.1.0"
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
}
},
"estraverse": {
@@ -4484,15 +6069,15 @@
"dev": true
},
"eventemitter3": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
- "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
},
"events": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
- "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
+ "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==",
"dev": true
},
"eventsource": {
@@ -4643,11 +6228,22 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
- },
- "qs": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
- "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+ }
+ }
+ },
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "dev": true,
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
+ "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==",
"dev": true
}
}
@@ -4762,11 +6358,25 @@
"dev": true
},
"fast-deep-equal": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
+ "fast-glob": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+ "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.0",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.2",
+ "picomatch": "^2.2.1"
+ }
+ },
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -4779,6 +6389,15 @@
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
"dev": true
},
+ "fastq": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz",
+ "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
"faye-websocket": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@@ -4804,23 +6423,26 @@
}
},
"file-loader": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz",
- "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz",
+ "integrity": "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw==",
"dev": true,
"requires": {
"loader-utils": "^2.0.0",
- "schema-utils": "^2.6.5"
- }
- },
- "fileset": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
- "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
- "dev": true,
- "requires": {
- "glob": "^7.0.3",
- "minimatch": "^3.0.3"
+ "schema-utils": "^3.0.0"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
}
},
"fill-range": {
@@ -4895,23 +6517,14 @@
}
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
@@ -4921,12 +6534,6 @@
"p-limit": "^2.2.0"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4951,12 +6558,12 @@
}
},
"find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
- "locate-path": "^2.0.0"
+ "locate-path": "^3.0.0"
}
},
"flatted": {
@@ -4976,24 +6583,10 @@
}
},
"follow-redirects": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz",
- "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==",
- "dev": true,
- "requires": {
- "debug": "^3.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- }
- }
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
+ "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
+ "dev": true
},
"for-in": {
"version": "1.0.2",
@@ -5033,6 +6626,14 @@
"map-cache": "^0.2.2"
}
},
+ "freeice": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/freeice/-/freeice-2.2.2.tgz",
+ "integrity": "sha512-XNoIxDHufqPIBSLpp4IrFPnoc+hv/0RwdOGhIoggIDC2ZKf5r6OoixbeoFJSmZOAq2aYiEUArhuQ8zVVrM5C4w==",
+ "requires": {
+ "normalice": "^1.0.0"
+ }
+ },
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -5088,9 +6689,9 @@
"dev": true
},
"fsevents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
- "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"dev": true,
"optional": true
},
@@ -5107,17 +6708,28 @@
"dev": true
},
"gensync": {
- "version": "1.0.0-beta.1",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
- "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true
},
"get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
+ "get-intrinsic": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz",
+ "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@@ -5157,24 +6769,12 @@
}
},
"glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"dev": true,
"requires": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- },
- "dependencies": {
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.0"
- }
- }
+ "is-glob": "^4.0.1"
}
},
"globals": {
@@ -5184,31 +6784,23 @@
"dev": true
},
"globby": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
- "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+ "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"dev": true,
"requires": {
- "array-union": "^1.0.1",
- "dir-glob": "^2.0.0",
- "glob": "^7.1.2",
- "ignore": "^3.3.5",
- "pify": "^3.0.0",
- "slash": "^1.0.0"
- },
- "dependencies": {
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- }
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
}
},
"graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"handle-thing": {
@@ -5224,15 +6816,23 @@
"dev": true
},
"har-validator": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
- "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"dev": true,
"requires": {
- "ajv": "^6.5.5",
+ "ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
+ "hark": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/hark/-/hark-1.2.3.tgz",
+ "integrity": "sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==",
+ "requires": {
+ "wildemitter": "^1.2.0"
+ }
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -5249,6 +6849,14 @@
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ }
}
},
"has-binary2": {
@@ -5295,6 +6903,14 @@
"get-value": "^2.0.6",
"has-values": "^1.0.0",
"isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ }
}
},
"has-values": {
@@ -5339,13 +6955,33 @@
}
},
"hash-base": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"dev": true,
"requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
}
},
"hash.js": {
@@ -5376,12 +7012,12 @@
}
},
"hosted-git-info": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz",
- "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==",
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz",
+ "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==",
"dev": true,
"requires": {
- "lru-cache": "^5.1.1"
+ "lru-cache": "^6.0.0"
}
},
"hpack.js": {
@@ -5459,16 +7095,10 @@
}
}
},
- "http-parser-js": {
- "version": "0.4.10",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
- "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
- "dev": true
- },
"http-proxy": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
@@ -5513,6 +7143,117 @@
"is-glob": "^4.0.0",
"lodash": "^4.17.11",
"micromatch": "^3.1.10"
+ },
+ "dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
}
},
"http-signature": {
@@ -5571,10 +7312,16 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "icss-utils": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.0.0.tgz",
+ "integrity": "sha512-aF2Cf/CkEZrI/vsu5WI/I+akFgdbwQHVE9YRZxATrhH4PVIe6a3BIjwjEcW+z+jP/hNh+YvM3lAAn1wJQ6opSg==",
+ "dev": true
+ },
"ieee754": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true
},
"iferr": {
@@ -5584,9 +7331,9 @@
"dev": true
},
"ignore": {
- "version": "3.3.10",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
- "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+ "version": "5.1.8",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
"dev": true
},
"ignore-walk": {
@@ -5611,15 +7358,6 @@
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
"dev": true
},
- "import-cwd": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
- "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
- "dev": true,
- "requires": {
- "import-from": "^2.1.0"
- }
- },
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
@@ -5630,15 +7368,6 @@
"resolve-from": "^3.0.0"
}
},
- "import-from": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
- "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
- "dev": true,
- "requires": {
- "resolve-from": "^3.0.0"
- }
- },
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -5702,46 +7431,39 @@
"dev": true
},
"inquirer": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz",
- "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==",
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
+ "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
"dev": true,
"requires": {
"ansi-escapes": "^4.2.1",
- "chalk": "^3.0.0",
+ "chalk": "^4.1.0",
"cli-cursor": "^3.1.0",
- "cli-width": "^2.0.0",
+ "cli-width": "^3.0.0",
"external-editor": "^3.0.3",
"figures": "^3.0.0",
- "lodash": "^4.17.15",
+ "lodash": "^4.17.19",
"mute-stream": "0.0.8",
"run-async": "^2.4.0",
- "rxjs": "^6.5.3",
+ "rxjs": "^6.6.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0",
"through": "^2.3.6"
},
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -5769,36 +7491,33 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
},
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "rxjs": {
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
},
"supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
@@ -5816,21 +7535,6 @@
"ipaddr.js": "^1.9.0"
}
},
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "invert-kv": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
- "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
- "dev": true
- },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -5903,9 +7607,9 @@
"dev": true
},
"is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
"dev": true
},
"is-color-stop": {
@@ -5922,6 +7626,15 @@
"rgba-regex": "^1.0.0"
}
},
+ "is-core-module": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz",
+ "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -5974,9 +7687,9 @@
"dev": true
},
"is-docker": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz",
- "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
+ "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
"dev": true
},
"is-extendable": {
@@ -5992,9 +7705,9 @@
"dev": true
},
"is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"is-glob": {
@@ -6012,6 +7725,12 @@
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"dev": true
},
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "dev": true
+ },
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -6048,12 +7767,6 @@
"path-is-inside": "^1.0.2"
}
},
- "is-plain-obj": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
- "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
- "dev": true
- },
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -6061,21 +7774,23 @@
"dev": true,
"requires": {
"isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ }
}
},
- "is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
- "dev": true
- },
"is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"dev": true,
"requires": {
- "has": "^1.0.3"
+ "has-symbols": "^1.0.1"
}
},
"is-resolvable": {
@@ -6121,10 +7836,13 @@
"dev": true
},
"is-wsl": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
- "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==",
- "dev": true
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
},
"isarray": {
"version": "1.0.0",
@@ -6145,10 +7863,13 @@
"dev": true
},
"isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
},
"isstream": {
"version": "0.1.2",
@@ -6156,81 +7877,19 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
- "istanbul-api": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.6.tgz",
- "integrity": "sha512-x0Eicp6KsShG1k1rMgBAi/1GgY7kFGEBwQpw3PXGEmu+rBcBNhqU8g2DgY9mlepAsLPzrzrbqSgCGANnki4POA==",
- "dev": true,
- "requires": {
- "async": "^2.6.2",
- "compare-versions": "^3.4.0",
- "fileset": "^2.0.3",
- "istanbul-lib-coverage": "^2.0.5",
- "istanbul-lib-hook": "^2.0.7",
- "istanbul-lib-instrument": "^3.3.0",
- "istanbul-lib-report": "^2.0.8",
- "istanbul-lib-source-maps": "^3.0.6",
- "istanbul-reports": "^2.2.4",
- "js-yaml": "^3.13.1",
- "make-dir": "^2.1.0",
- "minimatch": "^3.0.4",
- "once": "^1.4.0"
- },
- "dependencies": {
- "istanbul-lib-coverage": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
- "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
- "dev": true
- },
- "istanbul-lib-instrument": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
- "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
- "dev": true,
- "requires": {
- "@babel/generator": "^7.4.0",
- "@babel/parser": "^7.4.3",
- "@babel/template": "^7.4.0",
- "@babel/traverse": "^7.4.3",
- "@babel/types": "^7.4.0",
- "istanbul-lib-coverage": "^2.0.5",
- "semver": "^6.0.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
"istanbul-lib-coverage": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
"integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
"dev": true
},
- "istanbul-lib-hook": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
- "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==",
- "dev": true,
- "requires": {
- "append-transform": "^1.0.0"
- }
- },
"istanbul-lib-instrument": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz",
- "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
"dev": true,
"requires": {
"@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/template": "^7.7.4",
- "@babel/traverse": "^7.7.4",
"@istanbuljs/schema": "^0.1.2",
"istanbul-lib-coverage": "^3.0.0",
"semver": "^6.3.0"
@@ -6245,29 +7904,44 @@
}
},
"istanbul-lib-report": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
- "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
"dev": true,
"requires": {
- "istanbul-lib-coverage": "^2.0.5",
- "make-dir": "^2.1.0",
- "supports-color": "^6.1.0"
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
},
"dependencies": {
- "istanbul-lib-coverage": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
- "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
- "has-flag": "^3.0.0"
+ "has-flag": "^4.0.0"
}
}
}
@@ -6309,12 +7983,13 @@
}
},
"istanbul-reports": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz",
- "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
"dev": true,
"requires": {
- "html-escaper": "^2.0.0"
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
}
},
"jasmine": {
@@ -6337,9 +8012,9 @@
}
},
"jasmine-core": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz",
- "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz",
+ "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==",
"dev": true
},
"jasmine-spec-reporter": {
@@ -6358,11 +8033,12 @@
"dev": true
},
"jest-worker": {
- "version": "25.1.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz",
- "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==",
+ "version": "26.5.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz",
+ "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==",
"dev": true,
"requires": {
+ "@types/node": "*",
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
},
@@ -6374,9 +8050,9 @@
"dev": true
},
"supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
@@ -6423,6 +8099,12 @@
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -6456,6 +8138,12 @@
"minimist": "^1.2.5"
}
},
+ "jsonc-parser": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz",
+ "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==",
+ "dev": true
+ },
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@@ -6484,9 +8172,9 @@
}
},
"jszip": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.3.0.tgz",
- "integrity": "sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
+ "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
"dev": true,
"requires": {
"lie": "~3.3.0",
@@ -6496,53 +8184,52 @@
}
},
"karma": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/karma/-/karma-5.0.1.tgz",
- "integrity": "sha512-xrDGtZ0mykEQjx1BUHOP1ITi39MDsCGocmSvLJWHxUQpxuKwxk3ZUrC6HI2VWh1plLC6+7cA3B19m12yzO/FRw==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-5.0.9.tgz",
+ "integrity": "sha512-dUA5z7Lo7G4FRSe1ZAXqOINEEWxmCjDBbfRBmU/wYlSMwxUQJP/tEEP90yJt3Uqo03s9rCgVnxtlfq+uDhxSPg==",
"dev": true,
"requires": {
- "body-parser": "^1.16.1",
+ "body-parser": "^1.19.0",
"braces": "^3.0.2",
"chokidar": "^3.0.0",
- "colors": "^1.1.0",
- "connect": "^3.6.0",
+ "colors": "^1.4.0",
+ "connect": "^3.7.0",
"di": "^0.0.1",
- "dom-serialize": "^2.2.0",
- "flatted": "^2.0.0",
- "glob": "^7.1.1",
- "graceful-fs": "^4.1.2",
- "http-proxy": "^1.13.0",
- "isbinaryfile": "^4.0.2",
- "lodash": "^4.17.14",
- "log4js": "^4.0.0",
- "mime": "^2.3.1",
- "minimatch": "^3.0.2",
- "qjobs": "^1.1.4",
- "range-parser": "^1.2.0",
- "rimraf": "^2.6.0",
- "socket.io": "2.1.1",
+ "dom-serialize": "^2.2.1",
+ "flatted": "^2.0.2",
+ "glob": "^7.1.6",
+ "graceful-fs": "^4.2.4",
+ "http-proxy": "^1.18.1",
+ "isbinaryfile": "^4.0.6",
+ "lodash": "^4.17.15",
+ "log4js": "^6.2.1",
+ "mime": "^2.4.5",
+ "minimatch": "^3.0.4",
+ "qjobs": "^1.2.0",
+ "range-parser": "^1.2.1",
+ "rimraf": "^3.0.2",
+ "socket.io": "^2.3.0",
"source-map": "^0.6.1",
- "tmp": "0.0.33",
+ "tmp": "0.2.1",
"ua-parser-js": "0.7.21",
"yargs": "^15.3.1"
},
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -6579,18 +8266,6 @@
"path-exists": "^4.0.0"
}
},
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -6601,20 +8276,11 @@
}
},
"mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
"dev": true
},
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
@@ -6624,57 +8290,25 @@
"p-limit": "^2.2.0"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "tmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
"dev": true,
"requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
+ "rimraf": "^3.0.0"
}
},
"wrap-ansi": {
@@ -6689,9 +8323,9 @@
}
},
"yargs": {
- "version": "15.3.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
- "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dev": true,
"requires": {
"cliui": "^6.0.0",
@@ -6704,13 +8338,13 @@
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
- "yargs-parser": "^18.1.1"
+ "yargs-parser": "^18.1.2"
}
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
@@ -6729,22 +8363,33 @@
}
},
"karma-coverage-istanbul-reporter": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz",
- "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz",
+ "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==",
"dev": true,
"requires": {
- "istanbul-api": "^2.1.6",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^3.0.6",
+ "istanbul-reports": "^3.0.2",
"minimatch": "^3.0.4"
}
},
"karma-jasmine": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.1.1.tgz",
- "integrity": "sha512-pxBmv5K7IkBRLsFSTOpgiK/HzicQT3mfFF+oHAC7nxMfYKhaYFgxOa5qjnHW4sL5rUnmdkSajoudOnnOdPyW4Q==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz",
+ "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==",
"dev": true,
"requires": {
- "jasmine-core": "^3.5.0"
+ "jasmine-core": "^3.6.0"
+ },
+ "dependencies": {
+ "jasmine-core": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz",
+ "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==",
+ "dev": true
+ }
}
},
"karma-jasmine-html-reporter": {
@@ -6774,29 +8419,24 @@
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
- "lcid": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
- "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
- "dev": true,
- "requires": {
- "invert-kv": "^2.0.0"
- }
+ "klona": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
+ "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
+ "dev": true
},
"less": {
- "version": "3.11.1",
- "resolved": "https://registry.npmjs.org/less/-/less-3.11.1.tgz",
- "integrity": "sha512-tlWX341RECuTOvoDIvtFqXsKj072hm3+9ymRBe76/mD6O5ZZecnlAOVDlWAleF2+aohFrxNidXhv2773f6kY7g==",
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
+ "integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
"dev": true,
"requires": {
- "clone": "^2.1.2",
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
"mime": "^1.4.1",
- "mkdirp": "^0.5.0",
- "promise": "^7.1.1",
- "request": "^2.83.0",
+ "native-request": "^1.0.5",
"source-map": "~0.6.0",
"tslib": "^1.10.0"
},
@@ -6807,65 +8447,65 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
}
}
},
"less-loader": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz",
- "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-7.0.2.tgz",
+ "integrity": "sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w==",
"dev": true,
"requires": {
- "clone": "^2.1.1",
- "loader-utils": "^1.1.0",
- "pify": "^4.0.1"
+ "klona": "^2.0.4",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
}
}
},
- "leven": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
- "dev": true
- },
- "levenary": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz",
- "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==",
- "dev": true,
- "requires": {
- "leven": "^3.1.0"
- }
- },
"license-webpack-plugin": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.4.tgz",
- "integrity": "sha512-1Xq72fmPbTg5KofXs+yI5L4QqPFjQ6mZxoeI6D7gfiEDOtaEIk6PGrdLaej90bpDqKNHNxlQ/MW4tMAL6xMPJQ==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz",
+ "integrity": "sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ==",
"dev": true,
"requires": {
"@types/webpack-sources": "^0.1.5",
"webpack-sources": "^1.2.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ }
}
},
"lie": {
@@ -6877,6 +8517,22 @@
"immediate": "~3.0.5"
}
},
+ "line-column": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz",
+ "integrity": "sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI=",
+ "dev": true,
+ "requires": {
+ "isarray": "^1.0.0",
+ "isobject": "^2.0.0"
+ }
+ },
+ "lines-and-columns": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
+ "dev": true
+ },
"loader-runner": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
@@ -6895,12 +8551,12 @@
}
},
"locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
- "p-locate": "^2.0.0",
+ "p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
@@ -6910,12 +8566,6 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
- "lodash.clonedeep": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
- "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
- "dev": true
- },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -6929,57 +8579,91 @@
"dev": true
},
"log-symbols": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
"dev": true,
"requires": {
- "chalk": "^2.4.2"
+ "chalk": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
}
},
"log4js": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz",
- "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
+ "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
"dev": true,
"requires": {
- "date-format": "^2.0.0",
+ "date-format": "^3.0.0",
"debug": "^4.1.1",
- "flatted": "^2.0.0",
+ "flatted": "^2.0.1",
"rfdc": "^1.1.4",
- "streamroller": "^1.0.6"
+ "streamroller": "^2.2.4"
}
},
"loglevel": {
- "version": "1.6.8",
- "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
- "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz",
+ "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==",
"dev": true
},
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- },
"lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
- "yallist": "^3.0.2"
- },
- "dependencies": {
- "yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- }
+ "yallist": "^4.0.0"
}
},
"magic-string": {
@@ -7057,6 +8741,21 @@
"y18n": "^4.0.0"
}
},
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -7074,24 +8773,15 @@
"requires": {
"figgy-pudding": "^3.5.1"
}
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
}
}
},
- "mamacro": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
- "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
- "dev": true
- },
- "map-age-cleaner": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
- "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
- "dev": true,
- "requires": {
- "p-defer": "^1.0.0"
- }
- },
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -7130,21 +8820,10 @@
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
- "mem": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
- "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
- "dev": true,
- "requires": {
- "map-age-cleaner": "^0.1.1",
- "mimic-fn": "^2.0.0",
- "p-is-promise": "^2.0.0"
- }
- },
"memory-fs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
- "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
"dev": true,
"requires": {
"errno": "^0.1.3",
@@ -7180,6 +8859,12 @@
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@@ -7187,108 +8872,13 @@
"dev": true
},
"micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+ "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"dev": true,
"requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- },
- "dependencies": {
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
- "dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- }
+ "braces": "^3.0.1",
+ "picomatch": "^2.0.5"
}
},
"miller-rabin": {
@@ -7299,6 +8889,14 @@
"requires": {
"bn.js": "^4.0.0",
"brorand": "^1.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"mime": {
@@ -7308,18 +8906,18 @@
"dev": true
},
"mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"dev": true
},
"mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dev": true,
"requires": {
- "mime-db": "1.43.0"
+ "mime-db": "1.44.0"
}
},
"mimic-fn": {
@@ -7329,58 +8927,41 @@
"dev": true
},
"mini-css-extract-plugin": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
- "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz",
+ "integrity": "sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A==",
"dev": true,
"requires": {
- "loader-utils": "^1.1.0",
- "normalize-url": "1.9.1",
- "schema-utils": "^1.0.0",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0",
"webpack-sources": "^1.1.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "normalize-url": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
- "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
- "dev": true,
- "requires": {
- "object-assign": "^4.0.1",
- "prepend-http": "^1.0.0",
- "query-string": "^4.1.0",
- "sort-keys": "^1.0.0"
- }
- },
"schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
}
}
}
@@ -7413,9 +8994,9 @@
"dev": true
},
"minipass": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
- "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
@@ -7440,18 +9021,18 @@
}
},
"minipass-pipeline": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz",
- "integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minizlib": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
- "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dev": true,
"requires": {
"minipass": "^3.0.0",
@@ -7559,6 +9140,12 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
+ "nanoid": {
+ "version": "3.1.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.16.tgz",
+ "integrity": "sha512-+AK8MN0WHji40lj8AEuwLOvLSbWYApQpre/aFJZD71r43wVRLrOYS4FmJOPQYon1TqB462RzrrxlfA74XRES8w==",
+ "dev": true
+ },
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -7578,6 +9165,13 @@
"to-regex": "^3.0.1"
}
},
+ "native-request": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.8.tgz",
+ "integrity": "sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag==",
+ "dev": true,
+ "optional": true
+ },
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -7585,9 +9179,15 @@
"dev": true
},
"neo-async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
- "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
"nice-try": {
@@ -7608,9 +9208,9 @@
}
},
"node-forge": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
- "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
"dev": true
},
"node-libs-browser": {
@@ -7653,11 +9253,16 @@
}
},
"node-releases": {
- "version": "1.1.53",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz",
- "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
+ "version": "1.1.66",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz",
+ "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==",
"dev": true
},
+ "normalice": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/normalice/-/normalice-1.0.1.tgz",
+ "integrity": "sha1-A0NcLuzVYxprygLaOTDsPjRagPc="
+ },
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -7727,12 +9332,12 @@
"dev": true
},
"npm-package-arg": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.0.1.tgz",
- "integrity": "sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.0.tgz",
+ "integrity": "sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig==",
"dev": true,
"requires": {
- "hosted-git-info": "^3.0.2",
+ "hosted-git-info": "^3.0.6",
"semver": "^7.0.0",
"validate-npm-package-name": "^3.0.0"
}
@@ -7749,9 +9354,9 @@
}
},
"npm-pick-manifest": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz",
- "integrity": "sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz",
+ "integrity": "sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw==",
"dev": true,
"requires": {
"npm-install-checks": "^4.0.0",
@@ -7760,9 +9365,9 @@
}
},
"npm-registry-fetch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
- "integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz",
+ "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
"dev": true,
"requires": {
"JSONStream": "^1.3.4",
@@ -7780,6 +9385,15 @@
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
"npm-package-arg": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
@@ -7793,9 +9407,9 @@
}
},
"safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"semver": {
@@ -7803,6 +9417,12 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
}
}
},
@@ -7830,12 +9450,6 @@
"integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
"dev": true
},
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
- "dev": true
- },
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
@@ -7886,19 +9500,41 @@
}
},
"object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
"dev": true
},
"object-is": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
- "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz",
+ "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
+ "es-abstract": "^1.18.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"object-keys": {
@@ -7914,18 +9550,26 @@
"dev": true,
"requires": {
"isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ }
}
},
"object.assign": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
- "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
- "define-properties": "^1.1.2",
- "function-bind": "^1.1.1",
- "has-symbols": "^1.0.0",
- "object-keys": "^1.0.11"
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
}
},
"object.getownpropertydescriptors": {
@@ -7945,6 +9589,14 @@
"dev": true,
"requires": {
"isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ }
}
},
"object.values": {
@@ -7990,18 +9642,18 @@
}
},
"onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
"requires": {
"mimic-fn": "^2.1.0"
}
},
"open": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/open/-/open-7.0.3.tgz",
- "integrity": "sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA==",
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz",
+ "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==",
"dev": true,
"requires": {
"is-docker": "^2.0.0",
@@ -8009,4387 +9661,28 @@
}
},
"openvidu-browser": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/openvidu-browser/-/openvidu-browser-2.15.0.tgz",
- "integrity": "sha512-agnyeYIf1ze5ynGqNw32zFedlov9JZzjoFQHNMwuAoFYc2/24Aajs9cyw3j0m7v8xmMkqWSOYvsu7kGc8z1mZg==",
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/openvidu-browser/-/openvidu-browser-2.16.0.tgz",
+ "integrity": "sha512-o7TWCKhEKPYYuDpYhL1u2OMvZVhzj6Kef8iD64QOTO3q3Wq6jpDGpM6J9ZxfqjE9owyJWQkDL6jdzq4tEiiQvw==",
"requires": {
- "@types/node": "13.13.2",
- "@types/platform": "1.3.2",
+ "@types/node": "14.14.7",
+ "@types/platform": "1.3.3",
"freeice": "2.2.2",
"hark": "1.2.3",
- "platform": "1.3.5",
- "uuid": "7.0.3",
+ "platform": "1.3.6",
+ "uuid": "8.3.1",
"wolfy87-eventemitter": "5.2.9"
},
"dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/helper-validator-identifier": {
- "version": "7.9.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
- "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g=="
- },
- "@babel/highlight": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
- "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
"@types/node": {
- "version": "13.13.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz",
- "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A=="
- },
- "@types/platform": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/@types/platform/-/platform-1.3.2.tgz",
- "integrity": "sha512-Tn6OuJDAG7bJbyi4R7HqcxXp1w2lmIxVXqyNhPt1Bm0FO2EWIi3CI87JVzF7ncqK0ZMPuUycS3wTMIk85EeF1Q=="
- },
- "JSONStream": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
- "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
- "requires": {
- "jsonparse": "^1.2.0",
- "through": ">=2.2.7 <3"
- }
- },
- "abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
- },
- "acorn": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
- "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg=="
- },
- "acorn-node": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
- "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
- "requires": {
- "acorn": "^7.0.0",
- "acorn-walk": "^7.0.0",
- "xtend": "^4.0.2"
- }
- },
- "acorn-walk": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
- "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ=="
- },
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "any-promise": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
- },
- "anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
- "requires": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
- },
- "dependencies": {
- "normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
- "requires": {
- "remove-trailing-separator": "^1.0.1"
- }
- }
- }
- },
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "requires": {
- "sprintf-js": "~1.0.2"
- },
- "dependencies": {
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
- }
- }
- },
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
- },
- "arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
- },
- "arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
- },
- "array-each": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
- "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8="
- },
- "array-find-index": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
- },
- "array-slice": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
- "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w=="
- },
- "array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
- },
- "asn1.js": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
- "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
- "requires": {
- "bn.js": "^4.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
- }
- },
- "assert": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
- "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
- "requires": {
- "object-assign": "^4.1.1",
- "util": "0.10.3"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
- "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
- },
- "util": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
- "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
- "requires": {
- "inherits": "2.0.1"
- }
- }
- }
- },
- "assign-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
- },
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- },
- "async-each": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
- "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
- },
- "atob": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
- },
- "base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "requires": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- }
- }
- }
- },
- "base64-js": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
- "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
- },
- "binary-extensions": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
- "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
- },
- "bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "optional": true,
- "requires": {
- "file-uri-to-path": "1.0.0"
- }
- },
- "bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
- },
- "body": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
- "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=",
- "requires": {
- "continuable-cache": "^0.3.1",
- "error": "^7.0.0",
- "raw-body": "~1.1.0",
- "safe-json-parse": "~1.0.1"
- }
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "brorand": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
- "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
- },
- "browser-pack": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
- "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==",
- "requires": {
- "JSONStream": "^1.0.3",
- "combine-source-map": "~0.8.0",
- "defined": "^1.0.0",
- "safe-buffer": "^5.1.1",
- "through2": "^2.0.0",
- "umd": "^3.0.0"
- }
- },
- "browser-resolve": {
- "version": "1.11.3",
- "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
- "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
- "requires": {
- "resolve": "1.1.7"
- },
- "dependencies": {
- "resolve": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
- "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
- }
- }
- },
- "browserify": {
- "version": "16.5.1",
- "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.1.tgz",
- "integrity": "sha512-EQX0h59Pp+0GtSRb5rL6OTfrttlzv+uyaUVlK6GX3w11SQ0jKPKyjC/54RhPR2ib2KmfcELM06e8FxcI5XNU2A==",
- "requires": {
- "JSONStream": "^1.0.3",
- "assert": "^1.4.0",
- "browser-pack": "^6.0.1",
- "browser-resolve": "^1.11.0",
- "browserify-zlib": "~0.2.0",
- "buffer": "~5.2.1",
- "cached-path-relative": "^1.0.0",
- "concat-stream": "^1.6.0",
- "console-browserify": "^1.1.0",
- "constants-browserify": "~1.0.0",
- "crypto-browserify": "^3.0.0",
- "defined": "^1.0.0",
- "deps-sort": "^2.0.0",
- "domain-browser": "^1.2.0",
- "duplexer2": "~0.1.2",
- "events": "^2.0.0",
- "glob": "^7.1.0",
- "has": "^1.0.0",
- "htmlescape": "^1.1.0",
- "https-browserify": "^1.0.0",
- "inherits": "~2.0.1",
- "insert-module-globals": "^7.0.0",
- "labeled-stream-splicer": "^2.0.0",
- "mkdirp-classic": "^0.5.2",
- "module-deps": "^6.0.0",
- "os-browserify": "~0.3.0",
- "parents": "^1.0.1",
- "path-browserify": "~0.0.0",
- "process": "~0.11.0",
- "punycode": "^1.3.2",
- "querystring-es3": "~0.2.0",
- "read-only-stream": "^2.0.0",
- "readable-stream": "^2.0.2",
- "resolve": "^1.1.4",
- "shasum": "^1.0.0",
- "shell-quote": "^1.6.1",
- "stream-browserify": "^2.0.0",
- "stream-http": "^3.0.0",
- "string_decoder": "^1.1.1",
- "subarg": "^1.0.0",
- "syntax-error": "^1.1.1",
- "through2": "^2.0.0",
- "timers-browserify": "^1.0.1",
- "tty-browserify": "0.0.1",
- "url": "~0.11.0",
- "util": "~0.10.1",
- "vm-browserify": "^1.0.0",
- "xtend": "^4.0.0"
- }
- },
- "browserify-aes": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
- "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
- "requires": {
- "buffer-xor": "^1.0.3",
- "cipher-base": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.3",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "browserify-cipher": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
- "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
- "requires": {
- "browserify-aes": "^1.0.4",
- "browserify-des": "^1.0.0",
- "evp_bytestokey": "^1.0.0"
- }
- },
- "browserify-des": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
- "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
- "requires": {
- "cipher-base": "^1.0.1",
- "des.js": "^1.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
- "browserify-rsa": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
- "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
- "requires": {
- "bn.js": "^4.1.0",
- "randombytes": "^2.0.1"
- }
- },
- "browserify-sign": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
- "requires": {
- "bn.js": "^4.1.1",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.2",
- "elliptic": "^6.0.0",
- "inherits": "^2.0.1",
- "parse-asn1": "^5.0.0"
- }
- },
- "browserify-zlib": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
- "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
- "requires": {
- "pako": "~1.0.5"
- }
- },
- "buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
- "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
- "requires": {
- "base64-js": "^1.0.2",
- "ieee754": "^1.1.4"
- }
- },
- "buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
- },
- "buffer-xor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
- "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
- },
- "builtin-modules": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
- },
- "builtin-status-codes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
- "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
- },
- "bytes": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
- "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g="
- },
- "cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
- "requires": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
- }
- },
- "cached-path-relative": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz",
- "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg=="
- },
- "camelcase": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
- },
- "camelcase-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
- "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
- "requires": {
- "camelcase": "^2.0.0",
- "map-obj": "^1.0.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "fsevents": "^1.2.7",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- },
- "dependencies": {
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "requires": {
- "is-extglob": "^2.1.1"
- }
- }
- }
- },
- "cipher-base": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
- "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
- "requires": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- }
- }
- },
- "coffeescript": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
- "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4="
- },
- "collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
- "requires": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
- },
- "colors": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
- "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM="
- },
- "combine-source-map": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
- "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
- "requires": {
- "convert-source-map": "~1.1.0",
- "inline-source-map": "~0.6.0",
- "lodash.memoize": "~3.0.3",
- "source-map": "~0.5.3"
- }
- },
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
- "component-emitter": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
- },
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "requires": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "console-browserify": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
- "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="
- },
- "constants-browserify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
- "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
- },
- "continuable-cache": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
- "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8="
- },
- "convert-source-map": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
- "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA="
- },
- "copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
- },
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
- },
- "create-ecdh": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
- "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
- "requires": {
- "bn.js": "^4.1.0",
- "elliptic": "^6.0.0"
- }
- },
- "create-hash": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
- "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
- "requires": {
- "cipher-base": "^1.0.1",
- "inherits": "^2.0.1",
- "md5.js": "^1.3.4",
- "ripemd160": "^2.0.1",
- "sha.js": "^2.4.0"
- }
- },
- "create-hmac": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
- "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
- "requires": {
- "cipher-base": "^1.0.3",
- "create-hash": "^1.1.0",
- "inherits": "^2.0.1",
- "ripemd160": "^2.0.0",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
- }
- },
- "cross-spawn": {
- "version": "0.2.9",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.9.tgz",
- "integrity": "sha1-vWf5bAfvtjA7f+lMHpefiEeOCjk=",
- "requires": {
- "lru-cache": "^2.5.0"
- }
- },
- "crypto-browserify": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
- "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
- "requires": {
- "browserify-cipher": "^1.0.0",
- "browserify-sign": "^4.0.0",
- "create-ecdh": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.0",
- "diffie-hellman": "^5.0.0",
- "inherits": "^2.0.1",
- "pbkdf2": "^3.0.3",
- "public-encrypt": "^4.0.0",
- "randombytes": "^2.0.0",
- "randomfill": "^1.0.3"
- }
- },
- "csproj2ts": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/csproj2ts/-/csproj2ts-1.1.0.tgz",
- "integrity": "sha512-sk0RTT51t4lUNQ7UfZrqjQx7q4g0m3iwNA6mvyh7gLsgQYvwKzfdyoAgicC9GqJvkoIkU0UmndV9c7VZ8pJ45Q==",
- "requires": {
- "es6-promise": "^4.1.1",
- "lodash": "^4.17.4",
- "semver": "^5.4.1",
- "xml2js": "^0.4.19"
- },
- "dependencies": {
- "es6-promise": {
- "version": "4.2.8",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
- "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
- }
- }
- },
- "currently-unhandled": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
- "requires": {
- "array-find-index": "^1.0.1"
- }
- },
- "dargs": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
- "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "dash-ast": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz",
- "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA=="
- },
- "dateformat": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
- "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
- "requires": {
- "get-stdin": "^4.0.1",
- "meow": "^3.3.0"
- }
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
- },
- "decode-uri-component": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
- "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
- },
- "define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
- "requires": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
- "dependencies": {
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- }
- }
- }
- },
- "defined": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
- "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
- },
- "deps-sort": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz",
- "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==",
- "requires": {
- "JSONStream": "^1.0.3",
- "shasum-object": "^1.0.0",
- "subarg": "^1.0.0",
- "through2": "^2.0.0"
- }
- },
- "des.js": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
- "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
- "requires": {
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
- }
- },
- "detect-file": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
- "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc="
- },
- "detect-indent": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
- "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
- "requires": {
- "repeating": "^2.0.0"
- }
- },
- "detect-newline": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
- "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="
- },
- "detective": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
- "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
- "requires": {
- "acorn-node": "^1.6.1",
- "defined": "^1.0.0",
- "minimist": "^1.1.1"
- }
- },
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
- },
- "diffie-hellman": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
- "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
- "requires": {
- "bn.js": "^4.1.0",
- "miller-rabin": "^4.0.0",
- "randombytes": "^2.0.0"
- }
- },
- "domain-browser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
- "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
- },
- "duplexer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
- },
- "duplexer2": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
- "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
- "requires": {
- "readable-stream": "^2.0.2"
- }
- },
- "elliptic": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
- "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
- "requires": {
- "bn.js": "^4.4.0",
- "brorand": "^1.0.1",
- "hash.js": "^1.0.0",
- "hmac-drbg": "^1.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.0"
- }
- },
- "error": {
- "version": "7.2.1",
- "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz",
- "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==",
- "requires": {
- "string-template": "~0.2.1"
- }
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "es6-promise": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-0.1.2.tgz",
- "integrity": "sha1-8RLCn+paCZhTn8tqL9IUQ9KPBfc="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
- },
- "eventemitter2": {
- "version": "0.4.14",
- "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
- "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas="
- },
- "events": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
- "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg=="
- },
- "evp_bytestokey": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
- "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
- "requires": {
- "md5.js": "^1.3.4",
- "safe-buffer": "^5.1.1"
- }
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw="
- },
- "expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
- "requires": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "expand-tilde": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
- "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
- "requires": {
- "homedir-polyfill": "^1.0.1"
- }
- },
- "extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
- },
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
- }
- },
- "extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
- "requires": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- }
- }
- }
- },
- "fast-safe-stringify": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
- "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
- },
- "faye-websocket": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
- "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
- "requires": {
- "websocket-driver": ">=0.5.1"
- }
- },
- "figures": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
- "requires": {
- "escape-string-regexp": "^1.0.5",
- "object-assign": "^4.1.0"
- }
- },
- "file-sync-cmp": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz",
- "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs="
- },
- "file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "optional": true
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
- "requires": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
- "findup-sync": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
- "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
- "requires": {
- "glob": "~5.0.0"
- },
- "dependencies": {
- "glob": {
- "version": "5.0.15",
- "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
- "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
- "requires": {
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "2 || 3",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
- }
- },
- "fined": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
- "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
- "requires": {
- "expand-tilde": "^2.0.2",
- "is-plain-object": "^2.0.3",
- "object.defaults": "^1.1.0",
- "object.pick": "^1.2.0",
- "parse-filepath": "^1.0.1"
- }
- },
- "flagged-respawn": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
- "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q=="
- },
- "for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
- },
- "for-own": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
- "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
- "requires": {
- "for-in": "^1.0.1"
- }
- },
- "fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
- "requires": {
- "map-cache": "^0.2.2"
- }
- },
- "freeice": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/freeice/-/freeice-2.2.2.tgz",
- "integrity": "sha512-XNoIxDHufqPIBSLpp4IrFPnoc+hv/0RwdOGhIoggIDC2ZKf5r6OoixbeoFJSmZOAq2aYiEUArhuQ8zVVrM5C4w==",
- "requires": {
- "normalice": "^1.0.0"
- }
- },
- "fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "requires": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
- },
- "fsevents": {
- "version": "1.2.12",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
- "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
- "optional": true,
- "requires": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1",
- "node-pre-gyp": "*"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "bundled": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "bundled": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "optional": true
- },
- "needle": {
- "version": "2.3.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.8",
- "bundled": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1",
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "bundled": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "optional": true
- }
- }
- },
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "gaze": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
- "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
- "requires": {
- "globule": "^1.0.0"
- }
- },
- "get-assigned-identifiers": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
- "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ=="
- },
- "get-stdin": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
- },
- "get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
- },
- "getobject": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
- "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw="
- },
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
- "requires": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- }
- },
- "global-modules": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
- "requires": {
- "global-prefix": "^1.0.1",
- "is-windows": "^1.0.1",
- "resolve-dir": "^1.0.0"
- }
- },
- "global-prefix": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
- "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
- "requires": {
- "expand-tilde": "^2.0.2",
- "homedir-polyfill": "^1.0.1",
- "ini": "^1.3.4",
- "is-windows": "^1.0.1",
- "which": "^1.2.14"
- }
- },
- "globule": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
- "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
- "requires": {
- "glob": "~7.1.1",
- "lodash": "~4.17.12",
- "minimatch": "~3.0.2"
- }
- },
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
- },
- "grunt": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz",
- "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==",
- "requires": {
- "coffeescript": "~1.10.0",
- "dateformat": "~1.0.12",
- "eventemitter2": "~0.4.13",
- "exit": "~0.1.1",
- "findup-sync": "~0.3.0",
- "glob": "~7.0.0",
- "grunt-cli": "~1.2.0",
- "grunt-known-options": "~1.1.0",
- "grunt-legacy-log": "~2.0.0",
- "grunt-legacy-util": "~1.1.1",
- "iconv-lite": "~0.4.13",
- "js-yaml": "~3.13.1",
- "minimatch": "~3.0.2",
- "mkdirp": "~1.0.3",
- "nopt": "~3.0.6",
- "path-is-absolute": "~1.0.0",
- "rimraf": "~2.6.2"
- },
- "dependencies": {
- "glob": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
- "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.2",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "grunt-cli": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
- "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
- "requires": {
- "findup-sync": "~0.3.0",
- "grunt-known-options": "~1.1.0",
- "nopt": "~3.0.6",
- "resolve": "~1.1.0"
- }
- },
- "resolve": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
- "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
- }
- }
- },
- "grunt-cli": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz",
- "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==",
- "requires": {
- "grunt-known-options": "~1.1.0",
- "interpret": "~1.1.0",
- "liftoff": "~2.5.0",
- "nopt": "~4.0.1",
- "v8flags": "~3.1.1"
- },
- "dependencies": {
- "nopt": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
- "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- }
- }
- },
- "grunt-contrib-copy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz",
- "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=",
- "requires": {
- "chalk": "^1.1.1",
- "file-sync-cmp": "^0.1.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "grunt-contrib-sass": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/grunt-contrib-sass/-/grunt-contrib-sass-1.0.0.tgz",
- "integrity": "sha1-gGg4JRy8DhqU1k1RXN00z2dNcBs=",
- "requires": {
- "async": "^0.9.0",
- "chalk": "^1.0.0",
- "cross-spawn": "^0.2.3",
- "dargs": "^4.0.0",
- "which": "^1.0.5"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "async": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
- "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "grunt-contrib-uglify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-4.0.1.tgz",
- "integrity": "sha512-dwf8/+4uW1+7pH72WButOEnzErPGmtUvc8p08B0eQS/6ON0WdeQu0+WFeafaPTbbY1GqtS25lsHWaDeiTQNWPg==",
- "requires": {
- "chalk": "^2.4.1",
- "maxmin": "^2.1.0",
- "uglify-js": "^3.5.0",
- "uri-path": "^1.0.0"
- }
- },
- "grunt-contrib-watch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz",
- "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==",
- "requires": {
- "async": "^2.6.0",
- "gaze": "^1.1.0",
- "lodash": "^4.17.10",
- "tiny-lr": "^1.1.1"
- },
- "dependencies": {
- "async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
- "requires": {
- "lodash": "^4.17.14"
- }
- }
- }
- },
- "grunt-known-options": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
- "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ=="
- },
- "grunt-legacy-log": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
- "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
- "requires": {
- "colors": "~1.1.2",
- "grunt-legacy-log-utils": "~2.0.0",
- "hooker": "~0.2.3",
- "lodash": "~4.17.5"
- }
- },
- "grunt-legacy-log-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
- "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
- "requires": {
- "chalk": "~2.4.1",
- "lodash": "~4.17.10"
- }
- },
- "grunt-legacy-util": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
- "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
- "requires": {
- "async": "~1.5.2",
- "exit": "~0.1.1",
- "getobject": "~0.1.0",
- "hooker": "~0.2.3",
- "lodash": "~4.17.10",
- "underscore.string": "~3.3.4",
- "which": "~1.3.0"
- }
- },
- "grunt-postcss": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.9.0.tgz",
- "integrity": "sha512-lglLcVaoOIqH0sFv7RqwUKkEFGQwnlqyAKbatxZderwZGV1nDyKHN7gZS9LUiTx1t5GOvRBx0BEalHMyVwFAIA==",
- "requires": {
- "chalk": "^2.1.0",
- "diff": "^3.0.0",
- "postcss": "^6.0.11"
- }
- },
- "grunt-string-replace": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/grunt-string-replace/-/grunt-string-replace-1.3.1.tgz",
- "integrity": "sha1-YzoDvHhIKg4OH5339kWBH8H7sWI=",
- "requires": {
- "async": "^2.0.0",
- "chalk": "^1.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
- "requires": {
- "lodash": "^4.17.14"
- }
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "grunt-ts": {
- "version": "6.0.0-beta.22",
- "resolved": "https://registry.npmjs.org/grunt-ts/-/grunt-ts-6.0.0-beta.22.tgz",
- "integrity": "sha512-g9e+ZImQ7W38dfpwhp0+GUltXWidy3YGPfIA/IyGL5HMv6wmVmMMoSgscI5swhs2HSPf8yAvXAAJbwrouijoRg==",
- "requires": {
- "chokidar": "^2.0.4",
- "csproj2ts": "^1.1.0",
- "detect-indent": "^4.0.0",
- "detect-newline": "^2.1.0",
- "es6-promise": "~0.1.1",
- "jsmin2": "^1.2.1",
- "lodash": "~4.17.10",
- "ncp": "0.5.1",
- "rimraf": "2.2.6",
- "semver": "^5.3.0",
- "strip-bom": "^2.0.0"
- },
- "dependencies": {
- "rimraf": {
- "version": "2.2.6",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz",
- "integrity": "sha1-xZWXVpsU2VatKcrMQr3d9fDqT0w="
- }
- }
- },
- "gzip-size": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz",
- "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=",
- "requires": {
- "duplexer": "^0.1.1"
- }
- },
- "handlebars": {
- "version": "4.7.6",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
- "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
- "requires": {
- "minimist": "^1.2.5",
- "neo-async": "^2.6.0",
- "source-map": "^0.6.1",
- "uglify-js": "^3.1.4",
- "wordwrap": "^1.0.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "hark": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/hark/-/hark-1.2.3.tgz",
- "integrity": "sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==",
- "requires": {
- "wildemitter": "^1.2.0"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
- },
- "has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
- "requires": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
- }
- },
- "has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
- "requires": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "hash-base": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "hash.js": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
- "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
- "requires": {
- "inherits": "^2.0.3",
- "minimalistic-assert": "^1.0.1"
- }
- },
- "highlight.js": {
- "version": "9.18.1",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz",
- "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg=="
- },
- "hmac-drbg": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
- "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
- "requires": {
- "hash.js": "^1.0.3",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.1"
- }
- },
- "homedir-polyfill": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
- "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
- "requires": {
- "parse-passwd": "^1.0.0"
- }
- },
- "hooker": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
- "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk="
- },
- "hosted-git-info": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
- "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
- },
- "htmlescape": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
- "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
- },
- "http-parser-js": {
- "version": "0.4.10",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
- "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
- },
- "https-browserify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
- "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
- },
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ieee754": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
- },
- "indent-string": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
- "requires": {
- "repeating": "^2.0.0"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
- },
- "inline-source-map": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
- "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
- "requires": {
- "source-map": "~0.5.3"
- }
- },
- "insert-module-globals": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz",
- "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==",
- "requires": {
- "JSONStream": "^1.0.3",
- "acorn-node": "^1.5.2",
- "combine-source-map": "^0.8.0",
- "concat-stream": "^1.6.1",
- "is-buffer": "^1.1.0",
- "path-is-absolute": "^1.0.1",
- "process": "~0.11.0",
- "through2": "^2.0.0",
- "undeclared-identifiers": "^1.1.2",
- "xtend": "^4.0.0"
- }
- },
- "interpret": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
- "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
- },
- "is-absolute": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
- "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
- "requires": {
- "is-relative": "^1.0.0",
- "is-windows": "^1.0.1"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
- },
- "is-binary-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
- "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
- "requires": {
- "binary-extensions": "^1.0.0"
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- }
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
- },
- "is-finite": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
- "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="
- },
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "requires": {
- "is-extglob": "^2.1.0"
- }
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "requires": {
- "isobject": "^3.0.1"
- }
- },
- "is-relative": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
- "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
- "requires": {
- "is-unc-path": "^1.0.0"
- }
- },
- "is-unc-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
- "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
- "requires": {
- "unc-path-regex": "^0.1.2"
- }
- },
- "is-utf8": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
- "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
- },
- "is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
- },
- "js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- },
- "jsmin2": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/jsmin2/-/jsmin2-1.2.1.tgz",
- "integrity": "sha1-iPvi+/dfCpH2YCD9mBzWk/S/5X4="
- },
- "json-stable-stringify": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
- "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
- "requires": {
- "jsonify": "~0.0.0"
- }
- },
- "jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
- "requires": {
- "graceful-fs": "^4.1.6"
- }
- },
- "jsonify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
- "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
- },
- "jsonparse": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
- "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
- },
- "kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
- },
- "labeled-stream-splicer": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz",
- "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==",
- "requires": {
- "inherits": "^2.0.1",
- "stream-splicer": "^2.0.0"
- }
- },
- "liftoff": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
- "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
- "requires": {
- "extend": "^3.0.0",
- "findup-sync": "^2.0.0",
- "fined": "^1.0.1",
- "flagged-respawn": "^1.0.0",
- "is-plain-object": "^2.0.4",
- "object.map": "^1.0.0",
- "rechoir": "^0.6.2",
- "resolve": "^1.1.7"
- },
- "dependencies": {
- "findup-sync": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
- "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
- "requires": {
- "detect-file": "^1.0.0",
- "is-glob": "^3.1.0",
- "micromatch": "^3.0.4",
- "resolve-dir": "^1.0.1"
- }
- }
- }
- },
- "livereload-js": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz",
- "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw=="
- },
- "load-json-file": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
- "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "strip-bom": "^2.0.0"
- }
- },
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
- },
- "lodash.memoize": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
- "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8="
- },
- "loud-rejection": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
- "requires": {
- "currently-unhandled": "^0.4.1",
- "signal-exit": "^3.0.0"
- }
- },
- "lru-cache": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
- "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
- },
- "lunr": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz",
- "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg=="
- },
- "make-iterator": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
- "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
- "requires": {
- "kind-of": "^6.0.2"
- }
- },
- "map-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
- },
- "map-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
- },
- "map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
- "requires": {
- "object-visit": "^1.0.0"
- }
- },
- "marked": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
- "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw=="
- },
- "maxmin": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz",
- "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=",
- "requires": {
- "chalk": "^1.0.0",
- "figures": "^1.0.1",
- "gzip-size": "^3.0.0",
- "pretty-bytes": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "md5.js": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
- "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
- "meow": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
- "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
- "requires": {
- "camelcase-keys": "^2.0.0",
- "decamelize": "^1.1.2",
- "loud-rejection": "^1.0.0",
- "map-obj": "^1.0.1",
- "minimist": "^1.1.3",
- "normalize-package-data": "^2.3.4",
- "object-assign": "^4.0.1",
- "read-pkg-up": "^1.0.1",
- "redent": "^1.0.0",
- "trim-newlines": "^1.0.0"
- }
- },
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- }
- },
- "miller-rabin": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
- "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
- "requires": {
- "bn.js": "^4.0.0",
- "brorand": "^1.0.1"
- }
- },
- "minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
- },
- "minimalistic-crypto-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
- },
- "mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
- "requires": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
- }
- },
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
- },
- "mkdirp-classic": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
- "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
- },
- "module-deps": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.2.tgz",
- "integrity": "sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w==",
- "requires": {
- "JSONStream": "^1.0.3",
- "browser-resolve": "^1.7.0",
- "cached-path-relative": "^1.0.2",
- "concat-stream": "~1.6.0",
- "defined": "^1.0.0",
- "detective": "^5.2.0",
- "duplexer2": "^0.1.2",
- "inherits": "^2.0.1",
- "parents": "^1.0.0",
- "readable-stream": "^2.0.2",
- "resolve": "^1.4.0",
- "stream-combiner2": "^1.1.1",
- "subarg": "^1.0.0",
- "through2": "^2.0.0",
- "xtend": "^4.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
- "nan": {
- "version": "2.14.1",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
- "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
- "optional": true
- },
- "nanomatch": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- }
- },
- "ncp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz",
- "integrity": "sha1-dDmFMW49tFkoG1hxaehFc1oFQ58="
- },
- "neo-async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
- "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
- },
- "nopt": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
- "requires": {
- "abbrev": "1"
- }
- },
- "normalice": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/normalice/-/normalice-1.0.1.tgz",
- "integrity": "sha1-A0NcLuzVYxprygLaOTDsPjRagPc="
- },
- "normalize-package-data": {
- "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==",
- "requires": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
- },
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
- },
- "object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
- },
- "object-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
- "requires": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
- "requires": {
- "isobject": "^3.0.0"
- }
- },
- "object.defaults": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
- "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
- "requires": {
- "array-each": "^1.0.1",
- "array-slice": "^1.0.0",
- "for-own": "^1.0.0",
- "isobject": "^3.0.0"
- }
- },
- "object.map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
- "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
- "requires": {
- "for-own": "^1.0.0",
- "make-iterator": "^1.0.0"
- }
- },
- "object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
- "requires": {
- "isobject": "^3.0.1"
- }
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "requires": {
- "wrappy": "1"
- }
- },
- "os-browserify": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
- "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc="
- },
- "os-homedir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
- },
- "osenv": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
- "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "pako": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
- "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
- },
- "parents": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
- "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
- "requires": {
- "path-platform": "~0.11.15"
- }
- },
- "parse-asn1": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
- "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
- "requires": {
- "asn1.js": "^4.0.0",
- "browserify-aes": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.0",
- "pbkdf2": "^3.0.3",
- "safe-buffer": "^5.1.1"
- }
- },
- "parse-filepath": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
- "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
- "requires": {
- "is-absolute": "^1.0.0",
- "map-cache": "^0.2.0",
- "path-root": "^0.1.1"
- }
- },
- "parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
- "requires": {
- "error-ex": "^1.2.0"
- }
- },
- "parse-passwd": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
- "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="
- },
- "pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
- },
- "path-browserify": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
- "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ=="
- },
- "path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
- },
- "path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
- "requires": {
- "pinkie-promise": "^2.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
- },
- "path-platform": {
- "version": "0.11.15",
- "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
- "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I="
- },
- "path-root": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
- "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
- "requires": {
- "path-root-regex": "^0.1.0"
- }
- },
- "path-root-regex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
- "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0="
- },
- "path-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
- "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
- "requires": {
- "graceful-fs": "^4.1.2",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
- "pbkdf2": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
- "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
- "requires": {
- "create-hash": "^1.1.2",
- "create-hmac": "^1.1.4",
- "ripemd160": "^2.0.1",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
- }
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
- },
- "pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
- },
- "pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
- "requires": {
- "pinkie": "^2.0.0"
- }
- },
- "platform": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
- "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
- },
- "posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "pretty-bytes": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz",
- "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=",
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "process": {
- "version": "0.11.10",
- "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
- "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
- },
- "public-encrypt": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
- "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
- "requires": {
- "bn.js": "^4.1.0",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "parse-asn1": "^5.0.0",
- "randombytes": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
- "punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
- },
- "qs": {
- "version": "6.9.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
- "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw=="
- },
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
- },
- "querystring-es3": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
- "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
- },
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "randomfill": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
- "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
- "requires": {
- "randombytes": "^2.0.5",
- "safe-buffer": "^5.1.0"
- }
- },
- "raw-body": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
- "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=",
- "requires": {
- "bytes": "1",
- "string_decoder": "0.10"
- },
- "dependencies": {
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
- }
- }
- },
- "read-only-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
- "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
- "requires": {
- "readable-stream": "^2.0.2"
- }
- },
- "read-pkg": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
- "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
- "requires": {
- "load-json-file": "^1.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^1.0.0"
- }
- },
- "read-pkg-up": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
- "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
- "requires": {
- "find-up": "^1.0.0",
- "read-pkg": "^1.0.0"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- },
- "dependencies": {
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "readdirp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
- "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
- "requires": {
- "graceful-fs": "^4.1.11",
- "micromatch": "^3.1.10",
- "readable-stream": "^2.0.2"
- }
- },
- "rechoir": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
- "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
- "requires": {
- "resolve": "^1.1.6"
- }
- },
- "redent": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
- "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
- "requires": {
- "indent-string": "^2.1.0",
- "strip-indent": "^1.0.1"
- }
- },
- "regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "requires": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
- },
- "repeat-element": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
- "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
- },
- "repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
- },
- "repeating": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
- "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
- "requires": {
- "is-finite": "^1.0.0"
- }
- },
- "resolve": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
- "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "resolve-dir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
- "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
- "requires": {
- "expand-tilde": "^2.0.0",
- "global-modules": "^1.0.0"
- }
- },
- "resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
- },
- "ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
- },
- "rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "ripemd160": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
- "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
- },
- "safe-json-parse": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
- "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c="
- },
- "safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
- "requires": {
- "ret": "~0.1.10"
- }
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "sax": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- },
- "set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "sha.js": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
- "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "shasum": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
- "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
- "requires": {
- "json-stable-stringify": "~0.0.0",
- "sha.js": "~2.4.4"
- }
- },
- "shasum-object": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz",
- "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==",
- "requires": {
- "fast-safe-stringify": "^2.0.7"
- }
- },
- "shell-quote": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
- "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg=="
- },
- "shelljs": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
- "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
- "requires": {
- "glob": "^7.0.0",
- "interpret": "^1.0.0",
- "rechoir": "^0.6.2"
- }
- },
- "signal-exit": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
- },
- "simple-concat": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
- "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
- },
- "snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
- "requires": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "requires": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- }
- }
- }
- },
- "snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
- "requires": {
- "kind-of": "^3.2.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
- },
- "source-map-resolve": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "requires": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
- }
- },
- "source-map-support": {
- "version": "0.5.18",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.18.tgz",
- "integrity": "sha512-9luZr/BZ2QeU6tO2uG8N2aZpVSli4TSAOAqFOyTO51AJcD9P99c0K1h6dD6r6qo5dyT44BR5exweOaLLeldTkQ==",
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "source-map-url": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
- "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
- },
- "spdx-correct": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
- "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
- "requires": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "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=="
- },
- "spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
- "requires": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "spdx-license-ids": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
- },
- "split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "requires": {
- "extend-shallow": "^3.0.0"
- }
- },
- "sprintf-js": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
- "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
- },
- "static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
- "requires": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- }
- }
- },
- "stream-browserify": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
- "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
- "requires": {
- "inherits": "~2.0.1",
- "readable-stream": "^2.0.2"
- }
- },
- "stream-combiner2": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
- "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
- "requires": {
- "duplexer2": "~0.1.0",
- "readable-stream": "^2.0.2"
- }
- },
- "stream-http": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.0.tgz",
- "integrity": "sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw==",
- "requires": {
- "builtin-status-codes": "^3.0.0",
- "inherits": "^2.0.1",
- "readable-stream": "^3.0.6",
- "xtend": "^4.0.0"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "stream-splicer": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz",
- "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==",
- "requires": {
- "inherits": "^2.0.1",
- "readable-stream": "^2.0.2"
- }
- },
- "string-template": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
- "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0="
- },
- "string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "requires": {
- "safe-buffer": "~5.2.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-bom": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
- "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
- "requires": {
- "is-utf8": "^0.2.0"
- }
- },
- "strip-indent": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
- "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
- "requires": {
- "get-stdin": "^4.0.1"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
- },
- "subarg": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
- "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
- "requires": {
- "minimist": "^1.1.0"
- }
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "requires": {
- "has-flag": "^3.0.0"
- }
- },
- "syntax-error": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
- "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
- "requires": {
- "acorn-node": "^1.2.0"
- }
- },
- "terser": {
- "version": "4.6.11",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz",
- "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==",
- "requires": {
- "commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
- },
- "through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "requires": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "timers-browserify": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
- "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
- "requires": {
- "process": "~0.11.0"
- }
- },
- "tiny-lr": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz",
- "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==",
- "requires": {
- "body": "^5.1.0",
- "debug": "^3.1.0",
- "faye-websocket": "~0.10.0",
- "livereload-js": "^2.3.0",
- "object-assign": "^4.1.0",
- "qs": "^6.4.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
- }
- },
- "to-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "requires": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- },
- "trim-newlines": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
- "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
- },
- "tsconfig": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz",
- "integrity": "sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=",
- "requires": {
- "any-promise": "^1.3.0",
- "parse-json": "^2.2.0",
- "strip-bom": "^2.0.0",
- "strip-json-comments": "^2.0.0"
- }
- },
- "tsify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/tsify/-/tsify-4.0.1.tgz",
- "integrity": "sha512-ClznEI+pmwY5wmD0J7HCSVERwkD+l71ch3Dqyod2JuQLEsFaiNDI+vPjaGadsuVFVvmzgoI7HghrBtWsSmCDHQ==",
- "requires": {
- "convert-source-map": "^1.1.0",
- "fs.realpath": "^1.0.0",
- "object-assign": "^4.1.0",
- "semver": "^5.6.0",
- "through2": "^2.0.0",
- "tsconfig": "^5.0.3"
- }
- },
- "tslib": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
- "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
- },
- "tslint": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz",
- "integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "builtin-modules": "^1.1.1",
- "chalk": "^2.3.0",
- "commander": "^2.12.1",
- "diff": "^4.0.1",
- "glob": "^7.1.1",
- "js-yaml": "^3.13.1",
- "minimatch": "^3.0.4",
- "mkdirp": "^0.5.3",
- "resolve": "^1.3.2",
- "semver": "^5.3.0",
- "tslib": "^1.10.0",
- "tsutils": "^2.29.0"
- },
- "dependencies": {
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
- },
- "mkdirp": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "requires": {
- "minimist": "^1.2.5"
- }
- }
- }
- },
- "tsutils": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
- "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
- "requires": {
- "tslib": "^1.8.1"
- }
- },
- "tty-browserify": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
- "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="
- },
- "typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
- },
- "typedoc": {
- "version": "0.17.4",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.4.tgz",
- "integrity": "sha512-4Lotef1l6lNU5Fulpux809WPlF9CkmcXfv5QFyanrjYlxMFxSdARRdsy8Jv1OU3z0vjR4JsvUQT0YpiPqztcOA==",
- "requires": {
- "fs-extra": "^8.1.0",
- "handlebars": "^4.7.6",
- "highlight.js": "^9.18.1",
- "lodash": "^4.17.15",
- "lunr": "^2.3.8",
- "marked": "0.8.2",
- "minimatch": "^3.0.0",
- "progress": "^2.0.3",
- "shelljs": "^0.8.3",
- "typedoc-default-themes": "^0.10.0"
- }
- },
- "typedoc-default-themes": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.1.tgz",
- "integrity": "sha512-SuqAQI0CkwhqSJ2kaVTgl37cWs733uy9UGUqwtcds8pkFK8oRF4rZmCq+FXTGIb9hIUOu40rf5Kojg0Ha6akeg==",
- "requires": {
- "lunr": "^2.3.8"
- }
- },
- "typescript": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
- "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
- },
- "uglify-js": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz",
- "integrity": "sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==",
- "requires": {
- "commander": "~2.20.3"
- }
- },
- "umd": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
- "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow=="
- },
- "unc-path-regex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
- "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
- },
- "undeclared-identifiers": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz",
- "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==",
- "requires": {
- "acorn-node": "^1.3.0",
- "dash-ast": "^1.0.0",
- "get-assigned-identifiers": "^1.2.0",
- "simple-concat": "^1.0.0",
- "xtend": "^4.0.1"
- }
- },
- "underscore.string": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
- "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
- "requires": {
- "sprintf-js": "^1.0.3",
- "util-deprecate": "^1.0.2"
- }
- },
- "union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
- "requires": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
- }
- },
- "universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
- },
- "unset-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
- "requires": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
- "requires": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
- "requires": {
- "isarray": "1.0.0"
- }
- }
- }
- },
- "has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
- }
- }
- },
- "upath": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
- "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="
- },
- "uri-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
- "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI="
- },
- "urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
- },
- "url": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
- },
- "dependencies": {
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
- }
- }
- },
- "use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
- },
- "util": {
- "version": "0.10.4",
- "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
- "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
- "requires": {
- "inherits": "2.0.3"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- }
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ "version": "14.14.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
+ "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg=="
},
"uuid": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
- "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
- },
- "v8flags": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz",
- "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==",
- "requires": {
- "homedir-polyfill": "^1.0.1"
- }
- },
- "validate-npm-package-license": {
- "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==",
- "requires": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "vm-browserify": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
- "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
- },
- "websocket-driver": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
- "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
- "requires": {
- "http-parser-js": ">=0.4.0 <0.4.11",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
- }
- },
- "websocket-extensions": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
- "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg=="
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "wildemitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/wildemitter/-/wildemitter-1.2.1.tgz",
- "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw=="
- },
- "wolfy87-eventemitter": {
- "version": "5.2.9",
- "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz",
- "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw=="
- },
- "wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- },
- "xml2js": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
- "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
- "requires": {
- "sax": ">=0.6.0",
- "xmlbuilder": "~11.0.0"
- }
- },
- "xmlbuilder": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
- "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
- },
- "xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
+ "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
}
}
},
@@ -12410,60 +9703,35 @@
}
}
},
- "optimist": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
- "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
- "dev": true,
- "requires": {
- "minimist": "~0.0.1",
- "wordwrap": "~0.0.2"
- },
- "dependencies": {
- "minimist": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
- "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
- "dev": true
- }
- }
- },
"ora": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz",
- "integrity": "sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz",
+ "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==",
"dev": true,
"requires": {
- "chalk": "^3.0.0",
+ "chalk": "^4.1.0",
"cli-cursor": "^3.1.0",
- "cli-spinners": "^2.2.0",
+ "cli-spinners": "^2.4.0",
"is-interactive": "^1.0.0",
- "log-symbols": "^3.0.0",
+ "log-symbols": "^4.0.0",
"mute-stream": "0.0.8",
"strip-ansi": "^6.0.0",
"wcwidth": "^1.0.1"
},
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
"ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
- "@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -12491,19 +9759,10 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
"supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
@@ -12532,17 +9791,6 @@
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
- "os-locale": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
- "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
- "dev": true,
- "requires": {
- "execa": "^1.0.0",
- "lcid": "^2.0.0",
- "mem": "^4.0.0"
- }
- },
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@@ -12559,46 +9807,34 @@
"os-tmpdir": "^1.0.0"
}
},
- "p-defer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
- "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
- "dev": true
- },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
- "p-is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
- "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
- "dev": true
- },
"p-limit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
- "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
- "p-try": "^1.0.0"
+ "p-try": "^2.0.0"
}
},
"p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
- "p-limit": "^1.1.0"
+ "p-limit": "^2.0.0"
}
},
"p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
@@ -12614,9 +9850,9 @@
}
},
"p-try": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
- "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"pacote": {
@@ -12680,6 +9916,12 @@
"y18n": "^4.0.0"
}
},
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
@@ -12695,6 +9937,15 @@
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
@@ -12801,15 +10052,31 @@
"readable-stream": "^2.1.5"
}
},
- "parse-asn1": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
- "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
- "asn1.js": "^4.0.0",
+ "callsites": "^3.0.0"
+ },
+ "dependencies": {
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ }
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+ "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^5.2.0",
"browserify-aes": "^1.0.0",
- "create-hash": "^1.1.0",
"evp_bytestokey": "^1.0.0",
"pbkdf2": "^3.0.3",
"safe-buffer": "^5.1.1"
@@ -12831,6 +10098,41 @@
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true
},
+ "parse5-html-rewriting-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
+ "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1",
+ "parse5-sax-parser": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
+ "parse5-sax-parser": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
+ "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
@@ -12910,26 +10212,15 @@
"dev": true
},
"path-type": {
- "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"
- },
- "dependencies": {
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- }
- }
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
},
"pbkdf2": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
- "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
+ "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
"dev": true,
"requires": {
"create-hash": "^1.1.2",
@@ -12979,71 +10270,31 @@
"dev": true,
"requires": {
"find-up": "^3.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- }
}
},
- "pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
- "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "platform": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
+ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
+ },
+ "pnp-webpack-plugin": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
+ "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==",
"dev": true,
"requires": {
- "find-up": "^2.1.0"
+ "ts-pnp": "^1.1.6"
}
},
"portfinder": {
- "version": "1.0.25",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz",
- "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==",
+ "version": "1.0.28",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+ "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
"dev": true,
"requires": {
"async": "^2.6.2",
"debug": "^3.1.1",
- "mkdirp": "^0.5.1"
+ "mkdirp": "^0.5.5"
},
"dependencies": {
"debug": {
@@ -13064,9 +10315,9 @@
"dev": true
},
"postcss": {
- "version": "7.0.27",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
- "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
+ "version": "7.0.32",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
+ "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
@@ -13092,9 +10343,9 @@
}
},
"postcss-calc": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz",
- "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
+ "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
"dev": true,
"requires": {
"postcss": "^7.0.27",
@@ -13197,57 +10448,69 @@
}
}
},
- "postcss-load-config": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
- "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
- "dev": true,
- "requires": {
- "cosmiconfig": "^5.0.0",
- "import-cwd": "^2.0.0"
- }
- },
"postcss-loader": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
- "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.0.4.tgz",
+ "integrity": "sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw==",
"dev": true,
"requires": {
- "loader-utils": "^1.1.0",
- "postcss": "^7.0.0",
- "postcss-load-config": "^2.0.0",
- "schema-utils": "^1.0.0"
+ "cosmiconfig": "^7.0.0",
+ "klona": "^2.0.4",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0",
+ "semver": "^7.3.2"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "cosmiconfig": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
+ "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
}
},
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "import-fresh": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
+ "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
"dev": true,
"requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
}
},
+ "parse-json": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
+ "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
"schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
}
}
@@ -13384,6 +10647,41 @@
}
}
},
+ "postcss-modules-extract-imports": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+ "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "dev": true
+ },
+ "postcss-modules-local-by-default": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
+ "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "postcss-modules-scope": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
+ "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.4"
+ }
+ },
+ "postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0"
+ }
+ },
"postcss-normalize-charset": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
@@ -13599,14 +10897,15 @@
}
},
"postcss-selector-parser": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
- "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+ "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
"indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
+ "uniq": "^1.0.1",
+ "util-deprecate": "^1.0.2"
}
},
"postcss-svgo": {
@@ -13641,21 +10940,9 @@
}
},
"postcss-value-parser": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
- "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
- "dev": true
- },
- "prepend-http": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
- "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
- "dev": true
- },
- "private": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
- "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
"dev": true
},
"process": {
@@ -13670,16 +10957,6 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
},
- "promise": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
- "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
- "dev": true,
- "optional": true,
- "requires": {
- "asap": "~2.0.3"
- }
- },
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -13714,9 +10991,9 @@
}
},
"protractor": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.3.tgz",
- "integrity": "sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz",
+ "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==",
"dev": true,
"requires": {
"@types/q": "^0.0.32",
@@ -13727,13 +11004,13 @@
"glob": "^7.0.3",
"jasmine": "2.8.0",
"jasminewd2": "^2.1.0",
- "optimist": "~0.6.0",
"q": "1.4.1",
"saucelabs": "^1.5.0",
"selenium-webdriver": "3.6.0",
"source-map-support": "~0.4.0",
"webdriver-js-extender": "2.1.0",
- "webdriver-manager": "^12.0.6"
+ "webdriver-manager": "^12.1.7",
+ "yargs": "^15.3.1"
},
"dependencies": {
"@types/q": {
@@ -13742,12 +11019,33 @@
"integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
"dev": true
},
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@@ -13761,6 +11059,49 @@
"supports-color": "^2.0.0"
}
},
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"del": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
@@ -13776,6 +11117,16 @@
"rimraf": "^2.2.8"
}
},
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
"globby": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
@@ -13814,6 +11165,30 @@
"path-is-inside": "^1.0.1"
}
},
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -13856,6 +11231,15 @@
"source-map": "^0.5.6"
}
},
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -13880,6 +11264,72 @@
"semver": "^5.3.0",
"xml2js": "^0.4.17"
}
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
}
}
},
@@ -13917,6 +11367,14 @@
"parse-asn1": "^5.0.0",
"randombytes": "^2.0.1",
"safe-buffer": "^5.1.2"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
}
},
"pump": {
@@ -13971,21 +11429,11 @@
"dev": true
},
"qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"dev": true
},
- "query-string": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
- "dev": true,
- "requires": {
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
- }
- },
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -13999,9 +11447,9 @@
"dev": true
},
"querystringify": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
- "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"dev": true
},
"randombytes": {
@@ -14050,33 +11498,24 @@
}
},
"raw-loader": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz",
- "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+ "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
"dev": true,
"requires": {
- "loader-utils": "^1.2.3",
- "schema-utils": "^2.5.0"
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
}
}
@@ -14098,30 +11537,6 @@
}
}
},
- "read-package-json": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
- "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
- "dev": true,
- "requires": {
- "glob": "^7.1.1",
- "graceful-fs": "^4.1.2",
- "json-parse-better-errors": "^1.0.1",
- "normalize-package-data": "^2.0.0",
- "npm-normalize-package-bin": "^1.0.0"
- }
- },
- "read-package-tree": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
- "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
- "dev": true,
- "requires": {
- "read-package-json": "^2.0.0",
- "readdir-scoped-modules": "^1.0.0",
- "util-promisify": "^2.1.0"
- }
- },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -14137,25 +11552,13 @@
"util-deprecate": "~1.0.1"
}
},
- "readdir-scoped-modules": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
- "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
- "dev": true,
- "requires": {
- "debuglog": "^1.0.1",
- "dezalgo": "^1.0.0",
- "graceful-fs": "^4.1.2",
- "once": "^1.3.0"
- }
- },
"readdirp": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
- "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+ "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"dev": true,
"requires": {
- "picomatch": "^2.0.7"
+ "picomatch": "^2.2.1"
}
},
"reflect-metadata": {
@@ -14165,9 +11568,9 @@
"dev": true
},
"regenerate": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
- "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
"dev": true
},
"regenerate-unicode-properties": {
@@ -14180,19 +11583,18 @@
}
},
"regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
+ "version": "0.13.7",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
+ "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
},
"regenerator-transform": {
- "version": "0.14.4",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz",
- "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==",
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
+ "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
"dev": true,
"requires": {
- "@babel/runtime": "^7.8.4",
- "private": "^0.1.8"
+ "@babel/runtime": "^7.8.4"
}
},
"regex-not": {
@@ -14205,6 +11607,12 @@
"safe-regex": "^1.1.0"
}
},
+ "regex-parser": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
+ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+ "dev": true
+ },
"regexp.prototype.flags": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
@@ -14216,9 +11624,9 @@
}
},
"regexpu-core": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
- "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
+ "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
"dev": true,
"requires": {
"regenerate": "^1.4.0",
@@ -14230,9 +11638,9 @@
}
},
"regjsgen": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz",
- "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==",
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz",
+ "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==",
"dev": true
},
"regjsparser": {
@@ -14296,6 +11704,14 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ }
}
},
"require-directory": {
@@ -14305,9 +11721,9 @@
"dev": true
},
"require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"requires-port": {
@@ -14346,6 +11762,84 @@
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
"dev": true
},
+ "resolve-url-loader": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz",
+ "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==",
+ "dev": true,
+ "requires": {
+ "adjust-sourcemap-loader": "3.0.0",
+ "camelcase": "5.3.1",
+ "compose-function": "3.0.3",
+ "convert-source-map": "1.7.0",
+ "es6-iterator": "2.0.3",
+ "loader-utils": "1.2.3",
+ "postcss": "7.0.21",
+ "rework": "1.0.1",
+ "rework-visit": "1.0.0",
+ "source-map": "0.6.1"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "postcss": {
+ "version": "7.0.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
+ "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -14368,6 +11862,36 @@
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
},
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rework": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz",
+ "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^0.3.3",
+ "css": "^2.0.0"
+ },
+ "dependencies": {
+ "convert-source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
+ "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=",
+ "dev": true
+ }
+ }
+ },
+ "rework-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
+ "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=",
+ "dev": true
+ },
"rfdc": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
@@ -14406,22 +11930,25 @@
}
},
"rollup": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz",
- "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==",
+ "version": "2.32.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.32.1.tgz",
+ "integrity": "sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw==",
"dev": true,
"requires": {
"fsevents": "~2.1.2"
}
},
"run-async": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
- "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
- "dev": true,
- "requires": {
- "is-promise": "^2.1.0"
- }
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
+ },
+ "run-parallel": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz",
+ "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==",
+ "dev": true
},
"run-queue": {
"version": "1.0.3",
@@ -14438,6 +11965,13 @@
"integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
"requires": {
"tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
}
},
"safe-buffer": {
@@ -14462,52 +11996,37 @@
"dev": true
},
"sass": {
- "version": "1.26.3",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz",
- "integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==",
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz",
+ "integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
}
},
"sass-loader": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz",
- "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==",
+ "version": "10.0.5",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.5.tgz",
+ "integrity": "sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==",
"dev": true,
"requires": {
- "clone-deep": "^4.0.1",
- "loader-utils": "^1.2.3",
- "neo-async": "^2.6.1",
- "schema-utils": "^2.6.1",
- "semver": "^6.3.0"
+ "klona": "^2.0.4",
+ "loader-utils": "^2.0.0",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.0.0",
+ "semver": "^7.3.2"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
}
}
},
@@ -14527,13 +12046,14 @@
"dev": true
},
"schema-utils": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
- "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+ "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"requires": {
- "ajv": "^6.12.0",
- "ajv-keywords": "^3.4.1"
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
}
},
"select-hose": {
@@ -14575,18 +12095,18 @@
}
},
"selfsigned": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
- "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+ "version": "1.10.8",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
+ "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
"dev": true,
"requires": {
- "node-forge": "0.9.0"
+ "node-forge": "^0.10.0"
}
},
"semver": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz",
- "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==",
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true
},
"semver-dsl": {
@@ -14670,10 +12190,13 @@
}
},
"serialize-javascript": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
- "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
- "dev": true
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+ "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
},
"serve-index": {
"version": "1.9.1",
@@ -14848,9 +12371,9 @@
}
},
"slash": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
- "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
"smart-buffer": {
@@ -14964,6 +12487,12 @@
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
}
}
},
@@ -14988,34 +12517,17 @@
}
},
"socket.io": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
- "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
+ "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"dev": true,
"requires": {
- "debug": "~3.1.0",
- "engine.io": "~3.2.0",
+ "debug": "~4.1.0",
+ "engine.io": "~3.4.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
- "socket.io-client": "2.1.1",
- "socket.io-parser": "~3.2.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- }
+ "socket.io-client": "2.3.0",
+ "socket.io-parser": "~3.4.0"
}
},
"socket.io-adapter": {
@@ -15025,76 +12537,39 @@
"dev": true
},
"socket.io-client": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
- "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
+ "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"dev": true,
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
- "debug": "~3.1.0",
- "engine.io-client": "~3.2.0",
+ "debug": "~4.1.0",
+ "engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
- "socket.io-parser": "~3.2.0",
+ "socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "dev": true
+ },
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
"dev": true
},
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- }
- }
- },
- "socket.io-parser": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
- "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
- "dev": true,
- "requires": {
- "component-emitter": "1.2.1",
- "debug": "~3.1.0",
- "isarray": "2.0.1"
- },
- "dependencies": {
- "component-emitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
- "dev": true
- },
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
@@ -15106,17 +12581,71 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
+ },
+ "socket.io-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
+ "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "~1.3.0",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
+ "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~4.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
}
}
},
"sockjs": {
- "version": "0.3.19",
- "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
- "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
+ "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==",
"dev": true,
"requires": {
"faye-websocket": "^0.10.0",
- "uuid": "^3.0.1"
+ "uuid": "^3.4.0",
+ "websocket-driver": "0.6.5"
}
},
"sockjs-client": {
@@ -15184,15 +12713,6 @@
}
}
},
- "sort-keys": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
- "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
- "dev": true,
- "requires": {
- "is-plain-obj": "^1.0.0"
- }
- },
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -15206,34 +12726,44 @@
"dev": true
},
"source-map-loader": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
- "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.2.tgz",
+ "integrity": "sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA==",
"dev": true,
"requires": {
- "async": "^2.5.0",
- "loader-utils": "^1.1.0"
+ "abab": "^2.0.5",
+ "iconv-lite": "^0.6.2",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0",
+ "source-map": "^0.6.1",
+ "whatwg-mimetype": "^2.3.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "iconv-lite": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+ "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
}
}
},
@@ -15281,9 +12811,9 @@
"dev": true
},
"spdx-correct": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
- "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "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",
@@ -15291,15 +12821,15 @@
}
},
"spdx-exceptions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
- "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "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",
@@ -15307,9 +12837,9 @@
}
},
"spdx-license-ids": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
+ "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
"dev": true
},
"spdy": {
@@ -15353,9 +12883,9 @@
}
},
"speed-measure-webpack-plugin": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz",
- "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz",
+ "integrity": "sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ==",
"dev": true,
"requires": {
"chalk": "^2.0.1"
@@ -15475,113 +13005,108 @@
"dev": true
},
"streamroller": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz",
- "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==",
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
+ "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
"dev": true,
"requires": {
- "async": "^2.6.2",
- "date-format": "^2.0.0",
- "debug": "^3.2.6",
- "fs-extra": "^7.0.1",
- "lodash": "^4.17.14"
+ "date-format": "^2.1.0",
+ "debug": "^4.1.1",
+ "fs-extra": "^8.1.0"
},
"dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
+ "date-format": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
+ "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
+ "dev": true
},
"fs-extra": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
- "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"requires": {
- "graceful-fs": "^4.1.2",
+ "graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
}
}
},
- "strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
- "dev": true
- },
"string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true,
"requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz",
+ "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
},
"dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
- "ansi-regex": "^3.0.0"
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
}
}
}
},
- "string.prototype.trimend": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
- "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- }
- },
- "string.prototype.trimleft": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
- "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimstart": "^1.0.0"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
- "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimend": "^1.0.0"
- }
- },
"string.prototype.trimstart": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
- "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz",
+ "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
+ "es-abstract": "^1.18.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"string_decoder": {
@@ -15594,20 +13119,14 @@
}
},
"strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
- "ansi-regex": "^2.0.0"
+ "ansi-regex": "^5.0.0"
}
},
- "strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
- "dev": true
- },
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
@@ -15615,33 +13134,24 @@
"dev": true
},
"style-loader": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz",
- "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz",
+ "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==",
"dev": true,
"requires": {
- "loader-utils": "^1.2.3",
- "schema-utils": "^2.6.4"
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
}
}
@@ -15671,18 +13181,18 @@
}
},
"stylus": {
- "version": "0.54.7",
- "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz",
- "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==",
+ "version": "0.54.8",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
+ "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
"dev": true,
"requires": {
"css-parse": "~2.0.0",
"debug": "~3.1.0",
- "glob": "^7.1.3",
- "mkdirp": "~0.5.x",
+ "glob": "^7.1.6",
+ "mkdirp": "~1.0.4",
"safer-buffer": "^2.1.2",
"sax": "~1.2.4",
- "semver": "^6.0.0",
+ "semver": "^6.3.0",
"source-map": "^0.7.3"
},
"dependencies": {
@@ -15695,6 +13205,12 @@
"ms": "2.0.0"
}
},
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -15710,34 +13226,27 @@
}
},
"stylus-loader": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
- "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.1.1.tgz",
+ "integrity": "sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA==",
"dev": true,
"requires": {
- "loader-utils": "^1.0.2",
- "lodash.clonedeep": "^4.5.0",
- "when": "~3.6.x"
+ "fast-glob": "^3.2.4",
+ "klona": "^2.0.4",
+ "loader-utils": "^2.0.0",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^3.0.0"
},
"dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
}
}
@@ -15773,27 +13282,27 @@
}
},
"symbol-observable": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
- "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz",
+ "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==",
"dev": true
},
"tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.1.1.tgz",
+ "integrity": "sha512-Wib1S8m2wdpLbmQz0RBEVosIyvb/ykfKXf3ZIDqvWoMg/zTNm6G/tDSuUM61J1kNCDXWJrLHGSFeMhAG+gAGpQ==",
"dev": true
},
"tar": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz",
- "integrity": "sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz",
+ "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==",
"dev": true,
"requires": {
- "chownr": "^1.1.3",
+ "chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^3.0.0",
- "minizlib": "^2.1.0",
+ "minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
@@ -15807,89 +13316,71 @@
}
},
"terser": {
- "version": "4.6.10",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.10.tgz",
- "integrity": "sha512-qbF/3UOo11Hggsbsqm2hPa6+L4w7bkr+09FNseEe8xrcVD3APGLFqE+Oz1ZKAxjYnFsj80rLOfgAtJ0LNJjtTA==",
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.7.tgz",
+ "integrity": "sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w==",
"dev": true,
"requires": {
"commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
+ "source-map": "~0.7.2",
+ "source-map-support": "~0.5.19"
},
"dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
}
}
},
"terser-webpack-plugin": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz",
- "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz",
+ "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==",
"dev": true,
"requires": {
- "cacache": "^13.0.1",
- "find-cache-dir": "^3.2.0",
- "jest-worker": "^25.1.0",
- "p-limit": "^2.2.2",
- "schema-utils": "^2.6.4",
- "serialize-javascript": "^2.1.2",
+ "cacache": "^15.0.5",
+ "find-cache-dir": "^3.3.1",
+ "jest-worker": "^26.5.0",
+ "p-limit": "^3.0.2",
+ "schema-utils": "^3.0.0",
+ "serialize-javascript": "^5.0.1",
"source-map": "^0.6.1",
- "terser": "^4.4.3",
+ "terser": "^5.3.4",
"webpack-sources": "^1.4.3"
},
"dependencies": {
- "cacache": {
- "version": "13.0.1",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
- "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==",
- "dev": true,
- "requires": {
- "chownr": "^1.1.2",
- "figgy-pudding": "^3.5.1",
- "fs-minipass": "^2.0.0",
- "glob": "^7.1.4",
- "graceful-fs": "^4.2.2",
- "infer-owner": "^1.0.4",
- "lru-cache": "^5.1.1",
- "minipass": "^3.0.0",
- "minipass-collect": "^1.0.2",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.2",
- "mkdirp": "^0.5.1",
- "move-concurrently": "^1.0.1",
- "p-map": "^3.0.0",
- "promise-inflight": "^1.0.1",
- "rimraf": "^2.7.1",
- "ssri": "^7.0.0",
- "unique-filename": "^1.1.1"
- }
- },
"p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dev": true,
"requires": {
- "glob": "^7.1.3"
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
}
},
"source-map": {
@@ -15898,18 +13389,24 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
- "ssri": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
- "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==",
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"dev": true,
"requires": {
- "figgy-pudding": "^3.5.1",
- "minipass": "^3.1.1"
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
}
}
}
},
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -15933,9 +13430,9 @@
"dev": true
},
"timers-browserify": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
- "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+ "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
"dev": true,
"requires": {
"setimmediate": "^1.0.4"
@@ -16050,10 +13547,16 @@
"yn": "3.1.1"
}
},
+ "ts-pnp": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
+ "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==",
+ "dev": true
+ },
"tslib": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
- "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
+ "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
},
"tslint": {
"version": "6.1.1",
@@ -16081,6 +13584,12 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
}
}
},
@@ -16091,6 +13600,14 @@
"dev": true,
"requires": {
"tslib": "^1.8.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
}
},
"tty-browserify": {
@@ -16114,6 +13631,12 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
"type-fest": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
@@ -16137,9 +13660,9 @@
"dev": true
},
"typescript": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
- "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
+ "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"dev": true
},
"ua-parser-js": {
@@ -16148,12 +13671,6 @@
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
"dev": true
},
- "ultron": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
- "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
- "dev": true
- },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -16225,25 +13742,14 @@
}
},
"universal-analytics": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.20.tgz",
- "integrity": "sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw==",
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz",
+ "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==",
"dev": true,
"requires": {
- "debug": "^3.0.0",
- "request": "^2.88.0",
+ "debug": "^4.1.1",
+ "request": "^2.88.2",
"uuid": "^3.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- }
}
},
"universalify": {
@@ -16301,6 +13807,12 @@
"resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
"integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
"dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
}
}
},
@@ -16311,9 +13823,9 @@
"dev": true
},
"uri-js": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
- "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
@@ -16382,15 +13894,6 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
- "util-promisify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
- "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
- "dev": true,
- "requires": {
- "object.getownpropertydescriptors": "^2.0.3"
- }
- },
"util.promisify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz",
@@ -16470,14 +13973,25 @@
"dev": true
},
"watchpack": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
- "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+ "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
"dev": true,
"requires": {
- "chokidar": "^2.1.8",
+ "chokidar": "^3.4.1",
"graceful-fs": "^4.1.2",
- "neo-async": "^2.5.0"
+ "neo-async": "^2.5.0",
+ "watchpack-chokidar2": "^2.0.1"
+ }
+ },
+ "watchpack-chokidar2": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
+ "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chokidar": "^2.1.8"
},
"dependencies": {
"anymatch": {
@@ -16485,6 +13999,7 @@
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
"dev": true,
+ "optional": true,
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
@@ -16495,6 +14010,7 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
+ "optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
@@ -16505,13 +14021,15 @@
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
+ "optional": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
@@ -16523,6 +14041,18 @@
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
}
},
"chokidar": {
@@ -16530,6 +14060,7 @@
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
+ "optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
@@ -16545,573 +14076,58 @@
"upath": "^1.1.1"
}
},
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"dev": true,
+ "optional": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
}
},
"fsevents": {
- "version": "1.2.12",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
- "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"dev": true,
"optional": true,
"requires": {
- "node-pre-gyp": "*"
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
},
"dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"dev": true,
"optional": true,
"requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
+ "is-extglob": "^2.1.0"
}
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1",
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
}
}
},
@@ -17120,6 +14136,7 @@
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
"dev": true,
+ "optional": true,
"requires": {
"binary-extensions": "^1.0.0"
}
@@ -17129,17 +14146,50 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"dev": true,
+ "optional": true,
"requires": {
"kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true,
+ "optional": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "optional": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
}
},
"readdirp": {
@@ -17147,6 +14197,7 @@
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
"dev": true,
+ "optional": true,
"requires": {
"graceful-fs": "^4.1.11",
"micromatch": "^3.1.10",
@@ -17158,6 +14209,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
"integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
"dev": true,
+ "optional": true,
"requires": {
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
@@ -17194,36 +14246,65 @@
}
},
"webpack": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz",
- "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz",
+ "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-module-context": "1.8.5",
- "@webassemblyjs/wasm-edit": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5",
- "acorn": "^6.2.1",
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/wasm-edit": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "acorn": "^6.4.1",
"ajv": "^6.10.2",
"ajv-keywords": "^3.4.1",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^4.1.0",
+ "enhanced-resolve": "^4.3.0",
"eslint-scope": "^4.0.3",
"json-parse-better-errors": "^1.0.2",
"loader-runner": "^2.4.0",
"loader-utils": "^1.2.3",
"memory-fs": "^0.4.1",
"micromatch": "^3.1.10",
- "mkdirp": "^0.5.1",
+ "mkdirp": "^0.5.3",
"neo-async": "^2.6.1",
"node-libs-browser": "^2.2.1",
"schema-utils": "^1.0.0",
"tapable": "^1.1.3",
"terser-webpack-plugin": "^1.4.3",
- "watchpack": "^1.6.0",
+ "watchpack": "^1.7.4",
"webpack-sources": "^1.4.1"
},
"dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
"cacache": {
"version": "12.0.4",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
@@ -17247,6 +14328,58 @@
"y18n": "^4.0.0"
}
},
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "enhanced-resolve": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
+ "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.5.0",
+ "tapable": "^1.0.0"
+ },
+ "dependencies": {
+ "memory-fs": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+ "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
"find-cache-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
@@ -17258,12 +14391,38 @@
"pkg-dir": "^3.0.0"
}
},
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
"is-wsl": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
"dev": true
},
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
@@ -17284,14 +14443,34 @@
"json5": "^1.0.1"
}
},
- "memory-fs": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
- "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
+ "yallist": "^3.0.2"
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
}
},
"rimraf": {
@@ -17314,6 +14493,15 @@
"ajv-keywords": "^3.1.0"
}
},
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -17329,22 +14517,65 @@
"figgy-pudding": "^3.5.1"
}
},
+ "tapable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "dev": true
+ },
+ "terser": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+ "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ }
+ },
"terser-webpack-plugin": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
- "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+ "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
"dev": true,
"requires": {
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
- "serialize-javascript": "^2.1.2",
+ "serialize-javascript": "^4.0.0",
"source-map": "^0.6.1",
"terser": "^4.1.2",
"webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0"
}
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
}
}
},
@@ -17361,28 +14592,18 @@
"webpack-log": "^2.0.0"
},
"dependencies": {
- "memory-fs": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
- "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
- "dev": true,
- "requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
- }
- },
"mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
"dev": true
}
}
},
"webpack-dev-server": {
- "version": "3.10.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz",
- "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==",
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz",
+ "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
@@ -17393,33 +14614,39 @@
"debug": "^4.1.1",
"del": "^4.1.1",
"express": "^4.17.1",
- "html-entities": "^1.2.1",
+ "html-entities": "^1.3.1",
"http-proxy-middleware": "0.19.1",
"import-local": "^2.0.0",
"internal-ip": "^4.3.0",
"ip": "^1.1.5",
"is-absolute-url": "^3.0.3",
"killable": "^1.0.1",
- "loglevel": "^1.6.6",
+ "loglevel": "^1.6.8",
"opn": "^5.5.0",
"p-retry": "^3.0.1",
- "portfinder": "^1.0.25",
+ "portfinder": "^1.0.26",
"schema-utils": "^1.0.0",
"selfsigned": "^1.10.7",
"semver": "^6.3.0",
"serve-index": "^1.9.1",
- "sockjs": "0.3.19",
+ "sockjs": "0.3.20",
"sockjs-client": "1.4.0",
- "spdy": "^4.0.1",
+ "spdy": "^4.0.2",
"strip-ansi": "^3.0.1",
"supports-color": "^6.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "^3.7.2",
"webpack-log": "^2.0.0",
"ws": "^6.2.1",
- "yargs": "12.0.5"
+ "yargs": "^13.3.2"
},
"dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
@@ -17463,6 +14690,17 @@
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
}
},
"chokidar": {
@@ -17485,15 +14723,6 @@
"upath": "^1.1.1"
}
},
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -17504,554 +14733,44 @@
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
}
},
"fsevents": {
- "version": "1.2.12",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
- "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"dev": true,
- "optional": true,
"requires": {
- "node-pre-gyp": "*"
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
},
"dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
"requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
+ "is-extglob": "^2.1.0"
}
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1",
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
}
}
},
@@ -18077,15 +14796,44 @@
"dev": true,
"requires": {
"kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
}
},
"readdirp": {
@@ -18116,6 +14864,15 @@
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@@ -18145,25 +14902,34 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
+ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
+ "dev": true
+ }
}
},
"webpack-merge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
- "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.2.0.tgz",
+ "integrity": "sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA==",
"dev": true,
"requires": {
- "lodash": "^4.17.15"
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
}
},
"webpack-sources": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
- "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.1.tgz",
+ "integrity": "sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw==",
"dev": true,
"requires": {
- "source-list-map": "^2.0.0",
- "source-map": "~0.6.1"
+ "source-list-map": "^2.0.1",
+ "source-map": "^0.6.1"
},
"dependencies": {
"source-map": {
@@ -18175,35 +14941,51 @@
}
},
"webpack-subresource-integrity": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.0.tgz",
- "integrity": "sha512-GB1kB/LwAWC3CxwcedGhMkxGpNZxSheCe1q+KJP1bakuieAdX/rGHEcf5zsEzhKXpqsGqokgsDoD9dIkr61VDQ==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz",
+ "integrity": "sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q==",
"dev": true,
"requires": {
"webpack-sources": "^1.3.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ }
}
},
"websocket-driver": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
- "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
+ "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=",
"dev": true,
"requires": {
- "http-parser-js": ">=0.4.0 <0.4.11",
- "safe-buffer": ">=5.1.0",
"websocket-extensions": ">=0.1.1"
}
},
"websocket-extensions": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
- "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
"dev": true
},
- "when": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
- "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=",
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
"dev": true
},
"which": {
@@ -18221,12 +15003,22 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
- "wordwrap": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
- "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
"dev": true
},
+ "wildemitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/wildemitter/-/wildemitter-1.2.1.tgz",
+ "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw=="
+ },
+ "wolfy87-eventemitter": {
+ "version": "5.2.9",
+ "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz",
+ "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw=="
+ },
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@@ -18237,9 +15029,9 @@
}
},
"worker-plugin": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-4.0.2.tgz",
- "integrity": "sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-5.0.0.tgz",
+ "integrity": "sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ==",
"dev": true,
"requires": {
"loader-utils": "^1.1.0"
@@ -18268,33 +15060,52 @@
}
},
"wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
},
"dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
"is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
},
"string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
}
}
}
@@ -18354,79 +15165,86 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
+ "yaml": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
+ "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==",
+ "dev": true
+ },
"yargs": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
- "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true,
"requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.2.0",
+ "cliui": "^5.0.0",
"find-up": "^3.0.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^3.0.0",
+ "get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
+ "require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
+ "string-width": "^3.0.0",
"which-module": "^2.0.0",
- "y18n": "^3.2.1 || ^4.0.0",
- "yargs-parser": "^11.1.1"
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
},
"dependencies": {
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
}
}
},
"yargs-parser": {
- "version": "11.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
- "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ }
}
},
"yeast": {
diff --git a/openvidu-server/src/dashboard/package.json b/openvidu-server/src/dashboard/package.json
index 0591bb1c..0b7a2bea 100644
--- a/openvidu-server/src/dashboard/package.json
+++ b/openvidu-server/src/dashboard/package.json
@@ -1,55 +1,55 @@
{
- "dependencies": {
- "@angular/animations": "9.1.2",
- "@angular/cdk": "9.2.1",
- "@angular/common": "9.1.2",
- "@angular/compiler": "9.1.2",
- "@angular/core": "9.1.2",
- "@angular/flex-layout": "9.0.0-beta.29",
- "@angular/forms": "9.1.2",
- "@angular/material": "9.2.1",
- "@angular/platform-browser": "9.1.2",
- "@angular/platform-browser-dynamic": "9.1.2",
- "@angular/router": "9.1.2",
- "core-js": "3.6.5",
- "jquery": "3.5.0",
- "openvidu-browser": "2.15.0",
- "rxjs": "6.5.5",
- "tslib": "1.11.1",
- "zone.js": "0.10.3"
- },
- "devDependencies": {
- "@angular-devkit/build-angular": "0.901.1",
- "@angular/cli": "9.1.1",
- "@angular/compiler-cli": "9.1.2",
- "@angular/language-service": "9.1.2",
- "@types/jasmine": "3.5.10",
- "@types/node": "13.11.1",
- "codelyzer": "5.2.2",
- "jasmine-core": "3.5.0",
- "jasmine-spec-reporter": "5.0.1",
- "karma": "5.0.1",
- "karma-chrome-launcher": "3.1.0",
- "karma-coverage-istanbul-reporter": "2.1.1",
- "karma-jasmine": "3.1.1",
- "karma-jasmine-html-reporter": "1.5.3",
- "protractor": "5.4.3",
- "ts-node": "8.8.2",
- "tslint": "6.1.1",
- "typescript": "3.8.3"
- },
- "license": "Apache-2.0",
- "name": "frontend",
- "private": true,
- "scripts": {
- "build": "./node_modules/@angular/cli/bin/ng build --base-href /dashboard/ --output-path ../main/resources/static/dashboard",
- "build-prod": "./node_modules/@angular/cli/bin/ng build --prod --base-href /dashboard/ --output-path ../main/resources/static/dashboard",
- "e2e": "ng e2e",
- "lint": "ng lint",
- "ng": "ng",
- "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
- "start": "ng serve",
- "test": "ng test"
- },
- "version": "0.0.0"
+ "dependencies": {
+ "@angular/animations": "11.0.0",
+ "@angular/cdk": "11.0.0",
+ "@angular/common": "11.0.0",
+ "@angular/compiler": "11.0.0",
+ "@angular/core": "11.0.0",
+ "@angular/flex-layout": "11.0.0-beta.33",
+ "@angular/forms": "11.0.0",
+ "@angular/material": "11.0.0",
+ "@angular/platform-browser": "11.0.0",
+ "@angular/platform-browser-dynamic": "11.0.0",
+ "@angular/router": "11.0.0",
+ "core-js": "3.7.0",
+ "jquery": "3.5.1",
+ "openvidu-browser": "2.16.0",
+ "rxjs": "6.6.3",
+ "tslib": "2.0.3",
+ "zone.js": "0.11.3"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "0.1100.1",
+ "@angular/cli": "11.0.1",
+ "@angular/compiler-cli": "11.0.0",
+ "@angular/language-service": "11.0.0",
+ "@types/jasmine": "3.6.1",
+ "@types/node": "14.14.7",
+ "codelyzer": "6.0.1",
+ "jasmine-core": "3.6.0",
+ "jasmine-spec-reporter": "6.0.0",
+ "karma": "5.2.3",
+ "karma-chrome-launcher": "3.1.0",
+ "karma-coverage-istanbul-reporter": "3.0.3",
+ "karma-jasmine": "4.0.1",
+ "karma-jasmine-html-reporter": "1.5.4",
+ "protractor": "7.0.0",
+ "ts-node": "9.0.0",
+ "tslint": "6.1.3",
+ "typescript": "4.0.5"
+ },
+ "license": "Apache-2.0",
+ "name": "frontend",
+ "private": true,
+ "scripts": {
+ "build": "./node_modules/@angular/cli/bin/ng build --base-href /dashboard/ --output-path ../main/resources/static/dashboard",
+ "build-prod": "./node_modules/@angular/cli/bin/ng build --prod --base-href /dashboard/ --output-path ../main/resources/static/dashboard",
+ "e2e": "ng e2e",
+ "lint": "ng lint",
+ "ng": "ng",
+ "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
+ "start": "ng serve",
+ "test": "ng test"
+ },
+ "version": "0.0.0"
}
diff --git a/openvidu-server/src/dashboard/src/app/app.component.spec.ts b/openvidu-server/src/dashboard/src/app/app.component.spec.ts
index c740bcd7..ce614fb2 100644
--- a/openvidu-server/src/dashboard/src/app/app.component.spec.ts
+++ b/openvidu-server/src/dashboard/src/app/app.component.spec.ts
@@ -1,9 +1,9 @@
-import { TestBed, async } from '@angular/core/testing';
+import { TestBed, waitForAsync } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
- beforeEach(async(() => {
+ beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
@@ -11,19 +11,19 @@ describe('AppComponent', () => {
}).compileComponents();
}));
- it('should create the app', async(() => {
+ it('should create the app', waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
- it(`should have as title 'app works!'`, async(() => {
+ it(`should have as title 'app works!'`, waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app works!');
}));
- it('should render title in a h1 tag', async(() => {
+ it('should render title in a h1 tag', waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
diff --git a/openvidu-server/src/dashboard/src/app/app.routing.ts b/openvidu-server/src/dashboard/src/app/app.routing.ts
index 4b0f7cb1..e508eab1 100644
--- a/openvidu-server/src/dashboard/src/app/app.routing.ts
+++ b/openvidu-server/src/dashboard/src/app/app.routing.ts
@@ -41,4 +41,4 @@ const appRoutes: Routes = [
}
];
-export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });
+export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true, relativeLinkResolution: 'legacy' });
diff --git a/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.spec.ts b/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.spec.ts
index 9c996c37..eae86971 100644
--- a/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.spec.ts
+++ b/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.spec.ts
@@ -1,4 +1,4 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
@@ -6,7 +6,7 @@ describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture;
- beforeEach(async(() => {
+ beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ DashboardComponent ]
})
diff --git a/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.ts b/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.ts
index 4e896042..fe0b5787 100644
--- a/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.ts
+++ b/openvidu-server/src/dashboard/src/app/components/dashboard/dashboard.component.ts
@@ -47,7 +47,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
const protocol = location.protocol.includes('https') ? 'wss://' : 'ws://';
const port = (location.port) ? (':' + location.port) : '';
- this.websocket = new WebSocket(protocol + location.hostname + port + '/info');
+ this.websocket = new WebSocket(protocol + location.hostname + port + '/openvidu/info');
this.websocket.onopen = (event) => {
console.log('Info websocket connected');
diff --git a/openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.spec.ts b/openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.spec.ts
index 0aafa469..c8be0254 100644
--- a/openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.spec.ts
+++ b/openvidu-server/src/dashboard/src/app/components/layouts/layout-base/layout-base.component.spec.ts
@@ -1,4 +1,4 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { LayoutBaseComponent } from './layout-base.component';
@@ -6,7 +6,7 @@ describe('LayoutBaseComponent', () => {
let component: LayoutBaseComponent;
let fixture: ComponentFixture;
- beforeEach(async(() => {
+ beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ LayoutBaseComponent ]
})
diff --git a/openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.spec.ts b/openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.spec.ts
index 1f1d2fb7..5afebbb3 100644
--- a/openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.spec.ts
+++ b/openvidu-server/src/dashboard/src/app/components/session-details/session-details.component.spec.ts
@@ -1,4 +1,4 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { SessionDetailsComponent } from './session-details.component';
@@ -6,7 +6,7 @@ describe('SessionDetailsComponent', () => {
let component: SessionDetailsComponent;
let fixture: ComponentFixture;
- beforeEach(async(() => {
+ beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ SessionDetailsComponent ]
})
diff --git a/openvidu-server/src/dashboard/src/app/services/rest.service.ts b/openvidu-server/src/dashboard/src/app/services/rest.service.ts
index 0e2425d6..0abfea57 100644
--- a/openvidu-server/src/dashboard/src/app/services/rest.service.ts
+++ b/openvidu-server/src/dashboard/src/app/services/rest.service.ts
@@ -7,6 +7,7 @@ import { throwError } from 'rxjs';
export class RestService {
private openviduPublicUrl: string;
+ private API_PATH: string = 'openvidu/api';
constructor(private httpClient: HttpClient) { }
@@ -16,7 +17,7 @@ export class RestService {
resolve(this.openviduPublicUrl);
} else {
this.httpClient.get(location.protocol + '//' + location.hostname + ((!!location.port) ? (':' + location.port) : '') +
- '/config/openvidu-publicurl', { responseType: 'text' }).pipe(
+ '/' + this.API_PATH + '/config/openvidu-publicurl', { responseType: 'text' }).pipe(
catchError(error => {
reject(error);
return throwError(error);
@@ -44,7 +45,7 @@ export class RestService {
'Content-Type': 'application/json'
})
};
- this.httpClient.post(this.openviduPublicUrl + 'api/sessions', body, options)
+ this.httpClient.post(this.openviduPublicUrl + this.API_PATH + '/sessions', body, options)
.pipe(
catchError(error => {
reject(error);
@@ -66,7 +67,7 @@ export class RestService {
'Content-Type': 'application/json'
})
};
- this.httpClient.post(this.openviduPublicUrl + 'api/tokens', body, options)
+ this.httpClient.post(this.openviduPublicUrl + this.API_PATH + '/tokens', body, options)
.pipe(
catchError(error => {
reject(error);
diff --git a/openvidu-server/src/dashboard/tsconfig.json b/openvidu-server/src/dashboard/tsconfig.json
index a94b3484..83d411fb 100644
--- a/openvidu-server/src/dashboard/tsconfig.json
+++ b/openvidu-server/src/dashboard/tsconfig.json
@@ -1,7 +1,7 @@
{
"compileOnSave": false,
"compilerOptions": {
- "module": "esnext",
+ "module": "es2020",
"outDir": "./dist/out-tsc",
"baseUrl": "src",
"sourceMap": true,
@@ -9,7 +9,7 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
- "target": "es5",
+ "target": "es2018",
"typeRoots": [
"node_modules/@types"
],
diff --git a/openvidu-server/src/dashboard/tslint.json b/openvidu-server/src/dashboard/tslint.json
index bb84fcf3..eb983623 100644
--- a/openvidu-server/src/dashboard/tslint.json
+++ b/openvidu-server/src/dashboard/tslint.json
@@ -12,6 +12,9 @@
"curly": true,
"eofline": true,
"forin": true,
+ "deprecation": {
+ "severity": "warning"
+ },
"import-blacklist": [true],
"import-spacing": true,
"indent": [
@@ -53,7 +56,6 @@
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
- "no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
diff --git a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
index a81943f1..ee044ff1 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
@@ -34,7 +34,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
@@ -50,7 +52,6 @@ import io.openvidu.server.config.OpenviduConfig.Error;
import io.openvidu.server.core.SessionEventsHandler;
import io.openvidu.server.core.SessionManager;
import io.openvidu.server.core.TokenGenerator;
-import io.openvidu.server.core.TokenGeneratorDefault;
import io.openvidu.server.coturn.CoturnCredentialsService;
import io.openvidu.server.coturn.CoturnCredentialsServiceFactory;
import io.openvidu.server.kurento.core.KurentoParticipantEndpointConfig;
@@ -61,13 +62,21 @@ import io.openvidu.server.kurento.kms.FixedOneKmsManager;
import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.kurento.kms.LoadManager;
import io.openvidu.server.recording.DummyRecordingDownloader;
+import io.openvidu.server.recording.DummyRecordingUploader;
import io.openvidu.server.recording.RecordingDownloader;
+import io.openvidu.server.recording.RecordingUploader;
import io.openvidu.server.recording.service.RecordingManager;
+import io.openvidu.server.recording.service.RecordingManagerUtils;
+import io.openvidu.server.recording.service.RecordingManagerUtilsLocalStorage;
+import io.openvidu.server.rest.ApiRestPathRewriteFilter;
+import io.openvidu.server.rest.RequestMappings;
import io.openvidu.server.rpc.RpcHandler;
import io.openvidu.server.rpc.RpcNotificationService;
import io.openvidu.server.utils.CommandExecutor;
import io.openvidu.server.utils.GeoLocationByIp;
import io.openvidu.server.utils.GeoLocationByIpDummy;
+import io.openvidu.server.utils.LocalCustomFileManager;
+import io.openvidu.server.utils.LocalDockerManager;
import io.openvidu.server.utils.MediaNodeStatusManager;
import io.openvidu.server.utils.MediaNodeStatusManagerDummy;
import io.openvidu.server.utils.QuarantineKiller;
@@ -86,25 +95,12 @@ public class OpenViduServer implements JsonRpcConfigurer {
private static final Logger log = LoggerFactory.getLogger(OpenViduServer.class);
- public static final String WS_PATH = "/openvidu";
public static String wsUrl;
public static String httpUrl;
@Autowired
OpenviduConfig config;
- @Bean
- @ConditionalOnMissingBean
- @DependsOn("openviduConfig")
- public KmsManager kmsManager(OpenviduConfig openviduConfig) {
- if (openviduConfig.getKmsUris().isEmpty()) {
- throw new IllegalArgumentException("'KMS_URIS' should contain at least one KMS url");
- }
- String firstKmsWsUri = openviduConfig.getKmsUris().get(0);
- log.info("OpenVidu Server using one KMS: {}", firstKmsWsUri);
- return new FixedOneKmsManager();
- }
-
@Bean
@ConditionalOnMissingBean
@DependsOn("openviduConfig")
@@ -118,7 +114,8 @@ public class OpenViduServer implements JsonRpcConfigurer {
}
if (openviduConfig.isWebhookEnabled()) {
log.info("OpenVidu Webhook service is enabled");
- loggers.add(new CDRLoggerWebhook(openviduConfig));
+ loggers.add(new CDRLoggerWebhook(openviduConfig.getOpenViduWebhookEndpoint(),
+ openviduConfig.getOpenViduWebhookHeaders(), openviduConfig.getOpenViduWebhookEvents()));
} else {
log.info("OpenVidu Webhook service is disabled (may be enabled with 'OPENVIDU_WEBHOOK=true')");
}
@@ -139,6 +136,18 @@ public class OpenViduServer implements JsonRpcConfigurer {
return new KurentoSessionManager();
}
+ @Bean
+ @ConditionalOnMissingBean
+ @DependsOn({ "openviduConfig", "sessionManager", "mediaNodeStatusManager" })
+ public KmsManager kmsManager(OpenviduConfig openviduConfig, SessionManager sessionManager) {
+ if (openviduConfig.getKmsUris().isEmpty()) {
+ throw new IllegalArgumentException("'KMS_URIS' should contain at least one KMS url");
+ }
+ String firstKmsWsUri = openviduConfig.getKmsUris().get(0);
+ log.info("OpenVidu Server using one KMS: {}", firstKmsWsUri);
+ return new FixedOneKmsManager(sessionManager);
+ }
+
@Bean
@ConditionalOnMissingBean
@DependsOn("openviduConfig")
@@ -157,14 +166,14 @@ public class OpenViduServer implements JsonRpcConfigurer {
@ConditionalOnMissingBean
@DependsOn("openviduConfig")
public TokenGenerator tokenGenerator() {
- return new TokenGeneratorDefault();
+ return new TokenGenerator();
}
@Bean
@ConditionalOnMissingBean
@DependsOn("openviduConfig")
public RecordingManager recordingManager() {
- return new RecordingManager();
+ return new RecordingManager(new LocalDockerManager(false), new LocalCustomFileManager());
}
@Bean
@@ -185,6 +194,20 @@ public class OpenViduServer implements JsonRpcConfigurer {
return new KurentoParticipantEndpointConfig();
}
+ @Bean
+ @ConditionalOnMissingBean
+ @DependsOn({ "openviduConfig", "recordingManager" })
+ public RecordingManagerUtils recordingManagerUtils(OpenviduConfig openviduConfig,
+ RecordingManager recordingManager) {
+ return new RecordingManagerUtilsLocalStorage(openviduConfig, recordingManager);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public RecordingUploader recordingUpload() {
+ return new DummyRecordingUploader();
+ }
+
@Bean
@ConditionalOnMissingBean
public RecordingDownloader recordingDownload() {
@@ -203,6 +226,12 @@ public class OpenViduServer implements JsonRpcConfigurer {
return new SDPMunging();
}
+ @Bean
+ @ConditionalOnMissingBean
+ public SDPMunging sdpMunging() {
+ return new SDPMunging();
+ }
+
@Bean
@ConditionalOnMissingBean
public QuarantineKiller quarantineKiller() {
@@ -215,10 +244,20 @@ public class OpenViduServer implements JsonRpcConfigurer {
return new MediaNodeStatusManagerDummy();
}
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(name = "SUPPORT_DEPRECATED_API", havingValue = "true")
+ public FilterRegistrationBean filterRegistrationBean() {
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+ ApiRestPathRewriteFilter apiRestPathRewriteFilter = new ApiRestPathRewriteFilter();
+ registrationBean.setFilter(apiRestPathRewriteFilter);
+ return registrationBean;
+ }
+
@Override
public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
registry.addHandler(rpcHandler().withPingWatchdog(true).withInterceptors(new HttpHandshakeInterceptor()),
- WS_PATH);
+ RequestMappings.WS_RPC);
}
public static String getContainerIp() throws IOException, InterruptedException {
@@ -324,11 +363,11 @@ public class OpenViduServer implements JsonRpcConfigurer {
@EventListener(ApplicationReadyEvent.class)
public void whenReady() {
- String dashboardUrl = httpUrl + "dashboard/";
+ String dashboardUrl = httpUrl + config.getOpenViduFrontendDefaultPath().replaceAll("^/", "");
// @formatter:off
String msg = "\n\n----------------------------------------------------\n" + "\n" + " OpenVidu is ready!\n"
- + " ---------------------------\n" + "\n" + " * OpenVidu Server: " + httpUrl + "\n" + "\n"
+ + " ---------------------------\n" + "\n" + " * OpenVidu Server URL: " + httpUrl + "\n" + "\n"
+ " * OpenVidu Dashboard: " + dashboardUrl + "\n" + "\n"
+ "----------------------------------------------------\n";
// @formatter:on
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventMediaServerCrashed.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventMediaServerCrashed.java
new file mode 100644
index 00000000..fec4a96a
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventMediaServerCrashed.java
@@ -0,0 +1,31 @@
+package io.openvidu.server.cdr;
+
+import com.google.gson.JsonObject;
+
+import io.openvidu.server.kurento.kms.Kms;
+
+public class CDREventMediaServerCrashed extends CDREvent {
+
+ private Kms kms;
+ private String environmentId;
+
+ public CDREventMediaServerCrashed(CDREventName eventName, String sessionId, Long timeStamp, Kms kms,
+ String environmentId) {
+ super(eventName, sessionId, timeStamp);
+ this.kms = kms;
+ this.environmentId = environmentId;
+ }
+
+ @Override
+ public JsonObject toJson() {
+ JsonObject json = super.toJson();
+ json.addProperty("id", this.kms.getId());
+ if (this.environmentId != null) {
+ json.addProperty("environmentId", this.environmentId);
+ }
+ json.addProperty("ip", this.kms.getIp());
+ json.addProperty("uri", this.kms.getUri());
+ return json;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java
index 2144b588..6020e3e1 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventName.java
@@ -21,6 +21,6 @@ public enum CDREventName {
sessionCreated, sessionDestroyed, participantJoined, participantLeft, webrtcConnectionCreated,
webrtcConnectionDestroyed, recordingStarted, recordingStopped, recordingStatusChanged, filterEventDispatched,
- mediaNodeStatusChanged, autoscaling
+ signalSent, mediaNodeStatusChanged, autoscaling, mediaServerCrashed
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventParticipant.java
index 8c4fc5b5..6081aab1 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventParticipant.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventParticipant.java
@@ -28,7 +28,7 @@ public class CDREventParticipant extends CDREventEnd {
// participantJoined
public CDREventParticipant(Participant participant) {
- super(CDREventName.participantJoined, participant.getSessionId(), participant.getCreatedAt());
+ super(CDREventName.participantJoined, participant.getSessionId(), participant.getActiveAt());
this.participant = participant;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java
new file mode 100644
index 00000000..9f3d232d
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventSignal.java
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package io.openvidu.server.cdr;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+public class CDREventSignal extends CDREvent {
+
+ private String from;
+ private String[] to;
+ private String type;
+ private String data;
+
+ public CDREventSignal(String sessionId, String from, String[] to, String type, String data) {
+ super(CDREventName.signalSent, sessionId, System.currentTimeMillis());
+ this.from = from;
+ this.to = to;
+ this.type = type;
+ this.data = data;
+ }
+
+ @Override
+ public JsonObject toJson() {
+ JsonObject json = super.toJson();
+ json.addProperty("from", this.from);
+ JsonArray toArray = new JsonArray();
+ for (String id : this.to) {
+ toArray.add(id);
+ }
+ json.add("to", toArray);
+ json.addProperty("type", this.type);
+ json.addProperty("data", this.data);
+ return json;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventWebrtcConnection.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventWebrtcConnection.java
index 4536c9a2..eaa8e9a5 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventWebrtcConnection.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventWebrtcConnection.java
@@ -69,6 +69,7 @@ public class CDREventWebrtcConnection extends CDREventEnd implements Comparable<
if (kMediaOptions.rtspUri != null) {
json.addProperty("rtspUri", kMediaOptions.rtspUri);
json.addProperty("adaptativeBitrate", kMediaOptions.adaptativeBitrate);
+ json.addProperty("networkCache", kMediaOptions.networkCache);
json.addProperty("onlyPlayWithSubscribers", kMediaOptions.onlyPlayWithSubscribers);
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java
index 9228d8fd..990fa4f9 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java
@@ -36,58 +36,15 @@ import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionManager;
import io.openvidu.server.kurento.endpoint.KmsEvent;
+import io.openvidu.server.kurento.kms.Kms;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.service.RecordingManager;
import io.openvidu.server.summary.SessionSummary;
import io.openvidu.server.webhook.CDRLoggerWebhook;
/**
- * CDR logger to register all information of a Session.
- * Enabled by property 'OPENVIDU_CDR=true'
- *
- * - 'sessionCreated': {sessionId, timestamp}
- * - 'sessionDestroyed': {sessionId, timestamp, startTime, duration, reason}
- * - 'participantJoined': {sessionId, timestamp, participantId, location, platform}
- * - 'participantLeft': {sessionId, timestamp, participantId, startTime, duration, reason}
- * - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate]}
- * - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, startTime, duration, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason}
- * - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size}
- * - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size}
- * - 'recordingStatusChanged' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size, status}
- * - 'filterEventDispatched' {sessionId, timestamp, participantId, streamId, filterType, eventType, data}
- *
- * PROPERTIES VALUES:
- *
- * - sessionId: string
- * - timestamp: number
- * - startTime: number
- * - duration: number
- * - participantId: string
- * - connection: "INBOUND", "OUTBOUND"
- * - receivingFrom: string
- * - audioEnabled: boolean
- * - videoEnabled: boolean
- * - videoSource: "CAMERA", "SCREEN", "CUSTOM", "IPCAM"
- * - videoFramerate: number
- * - videoDimensions: string
- * - id: string
- * - name: string
- * - hasAudio: boolean
- * - hasVideo: boolean
- * - resolution string
- * - recordingLayout: string
- * - size: number
- * - status: string
- * - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "mediaServerDisconnect", "openviduServerStopped"
- * - participantLeft.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "mediaServerDisconnect", "openviduServerStopped"
- * - sessionDestroyed.reason: "lastParticipantLeft", "mediaServerDisconnect", "openviduServerStopped"
- * - recordingStopped.reason: "recordingStoppedByServer", "lastParticipantLeft", "sessionClosedByServer", "automaticStop", "mediaServerDisconnect", "openviduServerStopped"
- *
- * [OPTIONAL_PROPERTIES]:
- * - receivingFrom: only if connection = "INBOUND"
- * - videoSource: only if videoEnabled = true
- * - videoFramerate: only if videoEnabled = true
- * - videoDimensions: only if videoEnabled = true
+ * CDR logger to register all information of a Session. Enabled by property
+ * 'OPENVIDU_CDR=true'
*
* @author Pablo Fuente (pablofuenteperez@gmail.com)
*/
@@ -228,6 +185,13 @@ public class CallDetailRecord {
this.log(new CDREventFilterEvent(sessionId, participantId, streamId, filterType, event));
}
+ public void recordSignalSent(String sessionId, String from, String[] to, String type, String data) {
+ if (from != null) {
+ type = type.replaceFirst("^signal:", "");
+ }
+ this.log(new CDREventSignal(sessionId, from, to, type, data));
+ }
+
protected void log(CDREvent event) {
this.loggers.forEach(logger -> {
@@ -253,4 +217,10 @@ public class CallDetailRecord {
});
}
+ public void recordMediaServerCrashed(Kms kms, String environmentId, long timeOfKurentoDisconnection) {
+ CDREvent e = new CDREventMediaServerCrashed(CDREventName.mediaServerCrashed, null, timeOfKurentoDisconnection,
+ kms, environmentId);
+ this.log(e);
+ }
+
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/InfoSocketConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/InfoSocketConfig.java
index 6e39357b..6620a3b5 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/config/InfoSocketConfig.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/config/InfoSocketConfig.java
@@ -24,13 +24,15 @@ import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
+import io.openvidu.server.rest.RequestMappings;
+
@Configuration
@EnableWebSocket
public class InfoSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(infoHandler(), "/info").setAllowedOrigins("*");
+ registry.addHandler(infoHandler(), RequestMappings.WS_INFO).setAllowedOrigins("*");
}
@Bean
diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java
index e2b27fe0..40b89bab 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java
@@ -34,14 +34,18 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.kurento.jsonrpc.JsonUtils;
@@ -58,6 +62,7 @@ import io.openvidu.server.OpenViduServer;
import io.openvidu.server.cdr.CDREventName;
import io.openvidu.server.config.Dotenv.DotenvFormatException;
import io.openvidu.server.recording.RecordingNotification;
+import io.openvidu.server.rest.RequestMappings;
@Component
public class OpenviduConfig {
@@ -103,7 +108,7 @@ public class OpenviduConfig {
private List userConfigProps;
- private Map propertiesSource;
+ protected Map propertiesSource;
@Autowired
protected Environment env;
@@ -131,6 +136,8 @@ public class OpenviduConfig {
private String openviduRecordingCustomLayout;
+ private boolean openviduRecordingComposedBasicauth;
+
private String openviduRecordingVersion;
private Integer openviduStreamsVideoMaxRecvBandwidth;
@@ -243,6 +250,10 @@ public class OpenviduConfig {
return openViduRecordingDebug;
}
+ public boolean isRecordingComposedExternal() {
+ return false;
+ }
+
public String getOpenViduRecordingPath() {
return this.openviduRecordingPath;
}
@@ -259,6 +270,10 @@ public class OpenviduConfig {
return this.openviduRecordingCustomLayout;
}
+ public boolean isOpenviduRecordingComposedBasicauth() {
+ return this.openviduRecordingComposedBasicauth;
+ }
+
public String getOpenViduRecordingVersion() {
return this.openviduRecordingVersion;
}
@@ -319,6 +334,14 @@ public class OpenviduConfig {
return openviduSessionsGarbageThreshold;
}
+ public VideoCodec getOpenviduForcedCodec() {
+ return openviduForcedCodec;
+ }
+
+ public boolean isOpenviduAllowingTranscoding() {
+ return openviduAllowTranscoding;
+ }
+
public String getDotenvPath() {
return dotenvPath;
}
@@ -380,7 +403,7 @@ public class OpenviduConfig {
}
public String getOpenViduFrontendDefaultPath() {
- return "dashboard";
+ return RequestMappings.FRONTEND_CE;
}
// Properties management methods
@@ -488,7 +511,8 @@ public class OpenviduConfig {
coturnRedisConnectTimeout = getValue("COTURN_REDIS_CONNECT_TIMEOUT");
- openviduSecret = asNonEmptyString("OPENVIDU_SECRET");
+ openviduSecret = asNonEmptyAlphanumericString("OPENVIDU_SECRET",
+ "Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens (\"-\") and underscores (\"_\")");
openviduCdr = asBoolean("OPENVIDU_CDR");
openviduCdrPath = openviduCdr ? asWritableFileSystemPath("OPENVIDU_CDR_PATH")
@@ -501,6 +525,7 @@ public class OpenviduConfig {
openviduRecordingPublicAccess = asBoolean("OPENVIDU_RECORDING_PUBLIC_ACCESS");
openviduRecordingAutostopTimeout = asNonNegativeInteger("OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT");
openviduRecordingCustomLayout = asFileSystemPath("OPENVIDU_RECORDING_CUSTOM_LAYOUT");
+ openviduRecordingComposedBasicauth = asBoolean("OPENVIDU_RECORDING_COMPOSED_BASICAUTH");
openviduRecordingVersion = asNonEmptyString("OPENVIDU_RECORDING_VERSION");
openviduRecordingComposedUrl = asOptionalURL("OPENVIDU_RECORDING_COMPOSED_URL");
checkOpenviduRecordingNotification();
@@ -513,9 +538,9 @@ public class OpenviduConfig {
openviduSessionsGarbageInterval = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_INTERVAL");
openviduSessionsGarbageThreshold = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_THRESHOLD");
- openviduForcedCodec = asEnumValue("OPENVIDU_FORCED_CODEC", VideoCodec.class);
- openviduAllowTranscoding = asBoolean("OPENVIDU_ALLOW_TRANSCODING");
-
+ openviduForcedCodec = asEnumValue("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", VideoCodec.class);
+ openviduAllowTranscoding = asBoolean("OPENVIDU_STREAMS_ALLOW_TRANSCODING");
+
kmsUrisList = checkKmsUris();
checkCoturnIp();
@@ -748,6 +773,17 @@ public class OpenviduConfig {
}
}
+ protected String asNonEmptyAlphanumericString(String property, String errorMessage) {
+ final String REGEX = "^[a-zA-Z0-9_-]+$";
+ String stringValue = getValue(property);
+ if (stringValue != null && !stringValue.isEmpty() && stringValue.matches(REGEX)) {
+ return stringValue;
+ } else {
+ addError(property, errorMessage);
+ return null;
+ }
+ }
+
protected String asOptionalString(String property) {
return getValue(property);
}
@@ -881,9 +917,28 @@ public class OpenviduConfig {
}
}
+ protected Map asOptionalStringMap(String property) {
+ Map map = new HashMap<>();
+ String str = getValue(property);
+ if (str != null && !str.isEmpty()) {
+ try {
+ Gson gson = new Gson();
+ JsonObject jsonObject = gson.fromJson(str, JsonObject.class);
+ for (Entry entry : jsonObject.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().getAsString());
+ }
+ return map;
+ } catch (JsonSyntaxException e) {
+ addError(property, "Is not a valid map of strings. " + e.getMessage());
+ return map;
+ }
+ }
+ return map;
+ }
+
public URI checkWebsocketUri(String uri) throws Exception {
try {
- if (!uri.startsWith("ws://") || uri.startsWith("wss://")) {
+ if (!StringUtils.startsWithAny(uri, "ws://", "wss://")) {
throw new Exception("WebSocket protocol not found");
}
String parsedUri = uri.replaceAll("^ws://", "http://").replaceAll("^wss://", "https://");
@@ -953,4 +1008,4 @@ public class OpenviduConfig {
return null;
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/SecurityConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/SecurityConfig.java
index 74042b51..c06b1a6e 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/config/SecurityConfig.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/config/SecurityConfig.java
@@ -20,8 +20,12 @@ package io.openvidu.server.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -31,41 +35,45 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
-@Configuration
+import io.openvidu.server.rest.ApiRestPathRewriteFilter;
+import io.openvidu.server.rest.RequestMappings;
+
+@Configuration()
+@ConditionalOnMissingBean(name = "securityConfigPro")
+@Order(Ordered.LOWEST_PRECEDENCE)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
- OpenviduConfig openviduConf;
+ protected OpenviduConfig openviduConf;
+
+ @Autowired
+ protected Environment environment;
@Override
protected void configure(HttpSecurity http) throws Exception {
- // Security for API REST
ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry conf = http.cors().and()
.csrf().disable().authorizeRequests()
- // /api
- .antMatchers("/api/**").authenticated()
- // /config
- .antMatchers(HttpMethod.GET, "/config/openvidu-publicurl").permitAll()
- .antMatchers(HttpMethod.GET, "/config/**").authenticated()
- // /cdr
- .antMatchers(HttpMethod.GET, "/cdr/**").authenticated()
- // /accept-certificate
- .antMatchers(HttpMethod.GET, "/accept-certificate").permitAll()
- // Dashboard
- .antMatchers(HttpMethod.GET, "/dashboard/**").authenticated();
+ .antMatchers(HttpMethod.GET, RequestMappings.API + "/config/openvidu-publicurl").permitAll()
+ .antMatchers(HttpMethod.GET, RequestMappings.ACCEPT_CERTIFICATE).permitAll()
+ .antMatchers(RequestMappings.API + "/**").authenticated()
+ .antMatchers(HttpMethod.GET, RequestMappings.CDR + "/**").authenticated()
+ .antMatchers(HttpMethod.GET, RequestMappings.FRONTEND_CE + "/**").authenticated()
+ .antMatchers(HttpMethod.GET, RequestMappings.CUSTOM_LAYOUTS + "/**").authenticated();
- // Security for recording layouts
- conf.antMatchers("/layouts/**").authenticated();
-
- // Security for recorded video files
+ // Secure recordings depending on OPENVIDU_RECORDING_PUBLIC_ACCESS
if (openviduConf.getOpenViduRecordingPublicAccess()) {
- conf = conf.antMatchers("/recordings/**").permitAll();
+ conf = conf.antMatchers(HttpMethod.GET, RequestMappings.RECORDINGS + "/**").permitAll();
} else {
- conf = conf.antMatchers("/recordings/**").authenticated();
+ conf = conf.antMatchers(HttpMethod.GET, RequestMappings.RECORDINGS + "/**").authenticated();
}
conf.and().httpBasic();
+
+ // TODO: remove this when deprecating SUPPORT_DEPRECATED_API
+ if (Boolean.valueOf(environment.getProperty("SUPPORT_DEPRECATED_API"))) {
+ ApiRestPathRewriteFilter.protectOldPathsCe(conf, openviduConf);
+ }
}
@Bean
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/EndReason.java b/openvidu-server/src/main/java/io/openvidu/server/core/EndReason.java
index 34300a83..220a789a 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/EndReason.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/EndReason.java
@@ -20,7 +20,7 @@ package io.openvidu.server.core;
public enum EndReason {
unsubscribe, unpublish, disconnect, forceUnpublishByUser, forceUnpublishByServer, forceDisconnectByUser,
- forceDisconnectByServer, lastParticipantLeft, networkDisconnect, mediaServerDisconnect, openviduServerStopped,
- recordingStoppedByServer, automaticStop, sessionClosedByServer
+ forceDisconnectByServer, lastParticipantLeft, networkDisconnect, mediaServerDisconnect, mediaServerCrashed,
+ openviduServerStopped, recordingStoppedByServer, automaticStop, sessionClosedByServer
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java
index 4c17cebe..8869815b 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java
@@ -27,18 +27,43 @@ import io.openvidu.server.utils.GeoLocation;
public class Participant {
+ enum ParticipantStatus {
+
+ /**
+ * The participant has not called Session.publish in the client side yet. The
+ * internal token is available.
+ */
+ pending,
+
+ /**
+ * The participant has called Session.publish in the client side and a WebSocket
+ * connection is established. The internal token has been consumed and cannot be
+ * used again.
+ */
+ active
+ }
+
protected String finalUserId; // ID to match this connection with a final user (HttpSession id)
protected String participantPrivatetId; // ID to identify the user on server (org.kurento.jsonrpc.Session.id)
protected String participantPublicId; // ID to identify the user on clients
- private String sessionId; // ID of the session to which the participant belongs
- protected Long createdAt; // Timestamp when this connection was established
+ protected String sessionId; // ID of the session to which the participant belongs
+ protected ParticipantStatus status; // Status of the connection
+ protected Long activeAt; // Timestamp when this connection entered status "active"
protected String clientMetadata = ""; // Metadata provided on client side
- protected String serverMetadata = ""; // Metadata provided on server side
protected Token token; // Token associated to this participant
protected GeoLocation location; // Location of the participant
protected String platform; // Platform used by the participant to connect to the session
protected EndpointType endpointType; // Type of participant (web participant, IP cam participant...)
+ // TODO
+ // Unify with "PublisherEndpoint.MediaOptions"
+ // Also unify "streamPropertyChanged" and "videoData" RPCs when possible
+ protected Integer videoWidth = 0;
+ protected Integer videoHeight = 0;
+ protected Boolean videoActive = false;
+ protected Boolean audioActive = false;
+ protected Long publishedAt = null; // Timestamp when this participant was published
+
protected boolean streaming = false;
protected volatile boolean closed = false;
@@ -52,23 +77,21 @@ public class Participant {
public Participant(String finalUserId, String participantPrivatetId, String participantPublicId, String sessionId,
Token token, String clientMetadata, GeoLocation location, String platform, EndpointType endpointType,
- Long createdAt) {
+ Long activeAt) {
this.finalUserId = finalUserId;
this.participantPrivatetId = participantPrivatetId;
this.participantPublicId = participantPublicId;
this.sessionId = sessionId;
- if (createdAt != null) {
- this.createdAt = createdAt;
- } else {
- this.createdAt = System.currentTimeMillis();
- }
+ this.status = ParticipantStatus.active;
this.token = token;
+ if (activeAt != null) {
+ this.activeAt = activeAt;
+ } else {
+ this.activeAt = System.currentTimeMillis();
+ }
if (clientMetadata != null) {
this.clientMetadata = clientMetadata;
}
- if (!token.getServerMetadata().isEmpty()) {
- this.serverMetadata = token.getServerMetadata();
- }
this.location = location;
this.platform = platform;
this.endpointType = endpointType;
@@ -98,8 +121,8 @@ public class Participant {
return sessionId;
}
- public Long getCreatedAt() {
- return this.createdAt;
+ public Long getActiveAt() {
+ return this.activeAt;
}
public String getClientMetadata() {
@@ -111,21 +134,13 @@ public class Participant {
}
public String getServerMetadata() {
- return serverMetadata;
- }
-
- public void setServerMetadata(String serverMetadata) {
- this.serverMetadata = serverMetadata;
+ return this.token.getServerMetadata();
}
public Token getToken() {
return this.token;
}
- public void setToken(Token token) {
- this.token = token;
- }
-
public GeoLocation getLocation() {
return this.location;
}
@@ -146,6 +161,46 @@ public class Participant {
return this.endpointType;
}
+ public Integer getVideoWidth() {
+ return videoWidth;
+ }
+
+ public void setVideoWidth(Integer videoWidth) {
+ this.videoWidth = videoWidth;
+ }
+
+ public Integer getVideoHeight() {
+ return videoHeight;
+ }
+
+ public void setVideoHeight(Integer videoHeight) {
+ this.videoHeight = videoHeight;
+ }
+
+ public Boolean isVideoActive() {
+ return videoActive;
+ }
+
+ public void setVideoActive(Boolean videoActive) {
+ this.videoActive = videoActive;
+ }
+
+ public Boolean isAudioActive() {
+ return audioActive;
+ }
+
+ public void setPublishedAt(Long publishedAt) {
+ this.publishedAt = publishedAt;
+ }
+
+ public Long getPublishedAt() {
+ return publishedAt;
+ }
+
+ public void setAudioActive(Boolean audioActive) {
+ this.audioActive = audioActive;
+ }
+
public boolean isStreaming() {
return streaming;
}
@@ -155,7 +210,7 @@ public class Participant {
}
public boolean isIpcam() {
- return this.platform.equals("IPCAM") && this.participantPrivatetId.startsWith(IdentifierPrefixes.IPCAM_ID);
+ return this.platform != null && this.platform.equals("IPCAM") && this.participantPrivatetId.startsWith(IdentifierPrefixes.IPCAM_ID);
}
public String getPublisherStreamId() {
@@ -163,17 +218,26 @@ public class Participant {
}
public String getFullMetadata() {
- String fullMetadata;
- if ((!this.clientMetadata.isEmpty()) && (!this.serverMetadata.isEmpty())) {
- fullMetadata = this.clientMetadata + METADATA_SEPARATOR + this.serverMetadata;
- } else {
- fullMetadata = this.clientMetadata + this.serverMetadata;
+ String fullMetadata = "";
+ if (this.clientMetadata != null && !this.clientMetadata.isEmpty()) {
+ // Client data defined
+ fullMetadata += this.clientMetadata;
+ }
+ if (this.token.getServerMetadata() != null && !this.token.getServerMetadata().isEmpty()) {
+ // Server data defined
+ if (fullMetadata.isEmpty()) {
+ // Only server data
+ fullMetadata += this.token.getServerMetadata();
+ } else {
+ // Both client data and server data
+ fullMetadata += METADATA_SEPARATOR + this.token.getServerMetadata();
+ }
}
return fullMetadata;
}
public void deleteIpcamProperties() {
- this.clientMetadata = "";
+ this.clientMetadata = null;
this.token.setToken(null);
}
@@ -235,17 +299,32 @@ public class Participant {
public JsonObject toJson() {
JsonObject json = new JsonObject();
- json.addProperty("connectionId", this.participantPublicId);
- json.addProperty("createdAt", this.createdAt);
+ // COMMON
+ json.addProperty("id", this.participantPublicId);
+ json.addProperty("object", "connection");
+ json.addProperty("status", this.status.name());
+ json.addProperty("connectionId", this.participantPublicId); // TODO: deprecated. Better use only "id"
+ json.addProperty("sessionId", this.sessionId);
+ json.addProperty("createdAt", this.token.getCreatedAt());
+ json.addProperty("activeAt", this.activeAt);
json.addProperty("location", this.location != null ? this.location.toString() : "unknown");
json.addProperty("platform", this.platform);
if (this.token.getToken() != null) {
json.addProperty("token", this.token.getToken());
+ } else {
+ json.add("token", null);
}
- json.addProperty("role", this.token.getRole().name());
- json.addProperty("serverData", this.serverMetadata);
+ // Add all ConnectionProperties
+ JsonObject connectionPropertiesJson = this.token.getConnectionPropertiesWithFinalJsonFormat();
+ connectionPropertiesJson.entrySet().forEach(entry -> {
+ json.add(entry.getKey(), entry.getValue());
+ });
json.addProperty("clientData", this.clientMetadata);
return json;
}
+ public JsonObject withStatsToJson() {
+ return null;
+ }
+
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java
index bf0c0807..5436be4d 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java
@@ -18,6 +18,8 @@
package io.openvidu.server.core;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -27,7 +29,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Function;
+import java.util.stream.Collectors;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -41,7 +43,6 @@ import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.config.OpenviduConfig;
-import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.recording.service.RecordingManager;
import io.openvidu.server.utils.RecordingUtils;
@@ -60,6 +61,7 @@ public class Session implements SessionInterface {
protected volatile boolean closed = false;
protected AtomicInteger activePublishers = new AtomicInteger(0);
+ protected AtomicInteger activeIndividualRecordedPublishers = new AtomicInteger(0);
/**
* This lock protects the following operations with read lock: [REST API](POST
@@ -145,28 +147,50 @@ public class Session implements SessionInterface {
return activePublishers.get();
}
- public void registerPublisher() {
- this.activePublishers.incrementAndGet();
+ public int getActiveIndividualRecordedPublishers() {
+ return activeIndividualRecordedPublishers.get();
}
- public void deregisterPublisher() {
+ public void registerPublisher(Participant participant) {
+ this.activePublishers.incrementAndGet();
+ if (participant.getToken().record()) {
+ activeIndividualRecordedPublishers.incrementAndGet();
+ }
+ }
+
+ public void deregisterPublisher(Participant participant) {
this.activePublishers.decrementAndGet();
+ if (participant.getToken().record()) {
+ activeIndividualRecordedPublishers.decrementAndGet();
+ }
}
public void storeToken(Token token) {
this.tokens.put(token.getToken(), token);
}
- public boolean isTokenValid(String token) {
- return this.tokens.containsKey(token);
+ public boolean deleteTokenFromConnectionId(String connectionId) {
+ boolean deleted = false;
+ Iterator> iterator = this.tokens.entrySet().iterator();
+ while (iterator.hasNext() && !deleted) {
+ Entry entry = iterator.next();
+ if (connectionId.equals(entry.getValue().getConnectionId())) {
+ iterator.remove();
+ deleted = true;
+ }
+ }
+ return deleted;
}
public Token consumeToken(String token) {
Token tokenObj = this.tokens.remove(token);
- showTokens("Token consumed");
return tokenObj;
}
+ public Iterator> getTokenIterator() {
+ return this.tokens.entrySet().iterator();
+ }
+
public void showTokens(String preMessage) {
log.info("{} { Session: {} | Tokens: {} }", preMessage, this.sessionId, this.tokens.keySet().toString());
}
@@ -185,17 +209,34 @@ public class Session implements SessionInterface {
}
}
- public JsonObject toJson() {
- return this.sharedJson(KurentoParticipant::toJson);
+ public JsonArray getSnapshotOfConnectionsAsJsonArray(boolean withPendingConnections, boolean withWebrtcStats) {
+
+ Set snapshotOfActiveConnections = this.getParticipants().stream().collect(Collectors.toSet());
+ JsonArray jsonArray = new JsonArray();
+ snapshotOfActiveConnections.forEach(participant -> {
+ // Filter recorder participant
+ if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
+ jsonArray.add(withWebrtcStats ? participant.withStatsToJson() : participant.toJson());
+ }
+ });
+
+ if (withPendingConnections) {
+ Set snapshotOfPendingConnections = this.tokens.values().stream().collect(Collectors.toSet());
+ // Eliminate duplicates in case some concurrent situation took place
+ Set activeConnectionIds = snapshotOfActiveConnections.stream()
+ .map(participant -> participant.getParticipantPublicId()).collect(Collectors.toSet());
+ snapshotOfPendingConnections.removeIf(token -> activeConnectionIds.contains(token.getConnectionId()));
+ snapshotOfPendingConnections.forEach(token -> jsonArray.add(token.toJsonAsParticipant()));
+ }
+
+ return jsonArray;
}
- public JsonObject withStatsToJson() {
- return this.sharedJson(KurentoParticipant::withStatsToJson);
- }
-
- private JsonObject sharedJson(Function toJsonFunction) {
+ public JsonObject toJson(boolean withPendingConnections, boolean withWebrtcStats) {
JsonObject json = new JsonObject();
- json.addProperty("sessionId", this.sessionId);
+ json.addProperty("id", this.sessionId);
+ json.addProperty("object", "session");
+ json.addProperty("sessionId", this.sessionId); // TODO: deprecated. Better use only "id"
json.addProperty("createdAt", this.startTime);
json.addProperty("mediaMode", this.sessionProperties.mediaMode().name());
json.addProperty("recordingMode", this.sessionProperties.recordingMode().name());
@@ -212,16 +253,15 @@ public class Session implements SessionInterface {
json.addProperty("customSessionId", this.sessionProperties.customSessionId());
}
JsonObject connections = new JsonObject();
- JsonArray participants = new JsonArray();
- this.participants.values().forEach(p -> {
- if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(p.getParticipantPublicId())) {
- participants.add(toJsonFunction.apply((KurentoParticipant) p));
- }
- });
+ JsonArray participants = this.getSnapshotOfConnectionsAsJsonArray(withPendingConnections, withWebrtcStats);
connections.addProperty("numberOfElements", participants.size());
connections.add("content", participants);
json.add("connections", connections);
json.addProperty("recording", this.recordingManager.sessionIsBeingRecorded(this.sessionId));
+ if (this.sessionProperties.forcedVideoCodec() != null) {
+ json.addProperty("forcedVideoCodec", this.sessionProperties.forcedVideoCodec().name());
+ }
+ json.addProperty("allowTranscoding", this.sessionProperties.isTranscodingAllowed());
return json;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java
index 48c6b555..453033ab 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java
@@ -22,7 +22,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.kurento.client.GenericMediaEvent;
@@ -39,11 +38,11 @@ import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.OpenViduRole;
import io.openvidu.server.cdr.CallDetailRecord;
-import io.openvidu.server.config.InfoHandler;
import io.openvidu.server.config.OpenviduBuildInfo;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.kurento.endpoint.KurentoFilter;
+import io.openvidu.server.kurento.kms.Kms;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.rpc.RpcNotificationService;
@@ -54,9 +53,6 @@ public class SessionEventsHandler {
@Autowired
protected RpcNotificationService rpcNotificationService;
- @Autowired
- protected InfoHandler infoHandler;
-
@Autowired
protected CallDetailRecord CDR;
@@ -66,9 +62,7 @@ public class SessionEventsHandler {
@Autowired
protected OpenviduBuildInfo openviduBuildConfig;
- Map recordingsStarted = new ConcurrentHashMap<>();
-
- ReentrantLock lock = new ReentrantLock();
+ protected Map recordingsToSendClientEvents = new ConcurrentHashMap<>();
public void onSessionCreated(Session session) {
CDR.recordSessionCreated(session);
@@ -93,7 +87,7 @@ public class SessionEventsHandler {
participantJson.addProperty(ProtocolElements.JOINROOM_PEERID_PARAM,
existingParticipant.getParticipantPublicId());
participantJson.addProperty(ProtocolElements.JOINROOM_PEERCREATEDAT_PARAM,
- existingParticipant.getCreatedAt());
+ existingParticipant.getActiveAt());
// Metadata associated to each existing participant
participantJson.addProperty(ProtocolElements.JOINROOM_METADATA_PARAM,
@@ -149,7 +143,7 @@ public class SessionEventsHandler {
// Metadata associated to new participant
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM,
participant.getParticipantPublicId());
- notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_CREATEDAT_PARAM, participant.getCreatedAt());
+ notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_CREATEDAT_PARAM, participant.getActiveAt());
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM,
participant.getFullMetadata());
@@ -158,11 +152,27 @@ public class SessionEventsHandler {
}
}
result.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, participant.getParticipantPublicId());
- result.addProperty(ProtocolElements.PARTICIPANTJOINED_CREATEDAT_PARAM, participant.getCreatedAt());
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_CREATEDAT_PARAM, participant.getActiveAt());
result.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata());
- result.addProperty(ProtocolElements.JOINROOM_OPENVIDUSERVERVERSION_PARAM,
+ result.add(ProtocolElements.PARTICIPANTJOINED_VALUE_PARAM, resultArray);
+
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_SESSION_PARAM, participant.getSessionId());
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_VERSION_PARAM,
openviduBuildConfig.getOpenViduServerVersion());
- result.add("value", resultArray);
+ if (participant.getToken() != null) {
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORD_PARAM, participant.getToken().record());
+ if (participant.getToken().getRole() != null) {
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_ROLE_PARAM,
+ participant.getToken().getRole().name());
+ }
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_COTURNIP_PARAM, openviduConfig.getCoturnIp());
+ if (participant.getToken().getTurnCredentials() != null) {
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_TURNUSERNAME_PARAM,
+ participant.getToken().getTurnCredentials().getUsername());
+ result.addProperty(ProtocolElements.PARTICIPANTJOINED_TURNCREDENTIAL_PARAM,
+ participant.getToken().getTurnCredentials().getCredential());
+ }
+ }
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
}
@@ -306,16 +316,10 @@ public class SessionEventsHandler {
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
- lock.lock();
- try {
- Recording recording = this.recordingsStarted.remove(session.getSessionId());
- if (recording != null) {
- // RECORDER participant is now receiving video from the first publisher
- this.sendRecordingStartedNotification(session, recording);
- }
- } finally {
- lock.unlock();
- }
+ recordingsToSendClientEvents.computeIfPresent(session.getSessionId(), (key, value) -> {
+ sendRecordingStartedNotification(session, value);
+ return null;
+ });
}
}
@@ -328,7 +332,7 @@ public class SessionEventsHandler {
}
public void onSendMessage(Participant participant, JsonObject message, Set participants,
- Integer transactionId, OpenViduException error) {
+ String sessionId, Integer transactionId, OpenViduException error) {
boolean isRpcCall = transactionId != null;
if (isRpcCall) {
@@ -339,16 +343,22 @@ public class SessionEventsHandler {
}
}
+ String from = null;
+ String type = null;
+ String data = null;
+
JsonObject params = new JsonObject();
if (message.has("data")) {
- params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, message.get("data").getAsString());
+ data = message.get("data").getAsString();
+ params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, data);
}
if (message.has("type")) {
- params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, message.get("type").getAsString());
+ type = message.get("type").getAsString();
+ params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, type);
}
if (participant != null) {
- params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM,
- participant.getParticipantPublicId());
+ from = participant.getParticipantPublicId();
+ params.addProperty(ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, from);
}
Set toSet = new HashSet();
@@ -367,6 +377,7 @@ public class SessionEventsHandler {
if (toSet.isEmpty()) {
for (Participant p : participants) {
+ toSet.add(p.getParticipantPublicId());
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD, params);
}
@@ -389,6 +400,8 @@ public class SessionEventsHandler {
if (isRpcCall) {
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
}
+
+ CDR.recordSignalSent(sessionId, from, toSet.toArray(new String[toSet.size()]), type, data);
}
public void onStreamPropertyChanged(Participant participant, Integer transactionId, Set participants,
@@ -472,7 +485,7 @@ public class SessionEventsHandler {
public void sendRecordingStoppedNotification(Session session, Recording recording, EndReason reason) {
// Be sure to clean this map (this should return null)
- this.recordingsStarted.remove(session.getSessionId());
+ recordingsToSendClientEvents.remove(session.getSessionId());
// Filter participants by roles according to "OPENVIDU_RECORDING_NOTIFICATION"
Set existingParticipants;
@@ -578,15 +591,37 @@ public class SessionEventsHandler {
}
}
+ public void onVideoData(Participant participant, Integer transactionId, Integer height, Integer width,
+ Boolean videoActive, Boolean audioActive) {
+ participant.setVideoHeight(height);
+ participant.setVideoWidth(width);
+ participant.setVideoActive(videoActive);
+ participant.setAudioActive(audioActive);
+ log.info(
+ "Video data of participant {} was initialized. height:{}, width:{}, isVideoActive: {}, isAudioActive: {}",
+ participant.getParticipantPublicId(), height, width, videoActive, audioActive);
+ rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
+ }
+
public void closeRpcSession(String participantPrivateId) {
this.rpcNotificationService.closeRpcSession(participantPrivateId);
}
- public void setRecordingStarted(String sessionId, Recording recording) {
- this.recordingsStarted.put(sessionId, recording);
+ public void storeRecordingToSendClientEvent(Recording recording) {
+ recordingsToSendClientEvents.put(recording.getSessionId(), recording);
}
- private Set filterParticipantsByRole(OpenViduRole[] roles, Set participants) {
+ public void onNetworkQualityLevelChanged(Session session, JsonObject params) {
+ }
+
+ public void onConnectionPropertyChanged(Participant participant, String property, Object newValue) {
+ }
+
+ public void onMediaServerCrashed(Kms kms, long timeOfKurentoDisconnection) {
+ CDR.recordMediaServerCrashed(kms, null, timeOfKurentoDisconnection);
+ }
+
+ protected Set filterParticipantsByRole(OpenViduRole[] roles, Set participants) {
return participants.stream().filter(part -> {
if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(part.getParticipantPublicId())) {
return false;
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionInterface.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionInterface.java
index fa398f22..08d1d805 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionInterface.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionInterface.java
@@ -47,9 +47,7 @@ public interface SessionInterface {
String getMediaNodeId();
- JsonObject toJson();
-
- JsonObject withStatsToJson();
+ JsonObject toJson(boolean withPendingConnections, boolean withWebrtcStats);
Long getStartTime();
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java
index a72f1166..9a795886 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java
@@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
@@ -37,7 +38,6 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
-import org.apache.commons.lang3.RandomStringUtils;
import org.kurento.jsonrpc.message.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,13 +46,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.Recording;
import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.cdr.CDREventRecording;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.coturn.CoturnCredentialsService;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
import io.openvidu.server.kurento.endpoint.EndpointType;
import io.openvidu.server.recording.service.RecordingManager;
import io.openvidu.server.utils.FormatChecker;
@@ -88,6 +89,8 @@ public abstract class SessionManager {
public FormatChecker formatChecker = new FormatChecker();
+ private UpdatableTimerTask sessionGarbageCollectorTimer;
+
final protected ConcurrentMap sessions = new ConcurrentHashMap<>();
final protected ConcurrentMap sessionsNotActive = new ConcurrentHashMap<>();
protected ConcurrentMap> sessionidParticipantpublicidParticipant = new ConcurrentHashMap<>();
@@ -115,7 +118,7 @@ public abstract class SessionManager {
public void sendMessage(String message, String sessionId) {
try {
JsonObject messageJson = JsonParser.parseString(message).getAsJsonObject();
- sessionEventsHandler.onSendMessage(null, messageJson, getParticipants(sessionId), null, null);
+ sessionEventsHandler.onSendMessage(null, messageJson, getParticipants(sessionId), sessionId, null, null);
} catch (JsonSyntaxException | IllegalStateException e) {
throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE,
"Provided signal object '" + message + "' has not a valid JSON format");
@@ -126,7 +129,7 @@ public abstract class SessionManager {
try {
JsonObject messageJson = JsonParser.parseString(message).getAsJsonObject();
sessionEventsHandler.onSendMessage(participant, messageJson, getParticipants(participant.getSessionId()),
- transactionId, null);
+ participant.getSessionId(), transactionId, null);
} catch (JsonSyntaxException | IllegalStateException e) {
throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE,
"Provided signal object '" + message + "' has not a valid JSON format");
@@ -160,8 +163,8 @@ public abstract class SessionManager {
public abstract void removeFilterEventListener(Session session, Participant subscriber, String streamId,
String eventType);
- public abstract Participant publishIpcam(Session session, MediaOptions mediaOptions, String serverMetadata)
- throws Exception;
+ public abstract Participant publishIpcam(Session session, MediaOptions mediaOptions,
+ ConnectionProperties connectionProperties) throws Exception;
public abstract void reconnectStream(Participant participant, String streamId, String sdpOffer,
Integer transactionId);
@@ -169,6 +172,9 @@ public abstract class SessionManager {
public abstract String getParticipantPrivateIdFromStreamId(String sessionId, String streamId)
throws OpenViduException;
+ public abstract void onVideoData(Participant participant, Integer transactionId, Integer height, Integer width,
+ Boolean videoActive, Boolean audioActive);
+
/**
* Returns a Session given its id
*
@@ -297,22 +303,23 @@ public abstract class SessionManager {
return sessionNotActive;
}
- public String newToken(Session session, OpenViduRole role, String serverMetadata,
- KurentoTokenOptions kurentoTokenOptions) throws Exception {
+ public Token newToken(Session session, OpenViduRole role, String serverMetadata, boolean record,
+ KurentoOptions kurentoOptions) throws Exception {
if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) {
log.error("Data invalid format");
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format");
}
- Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), role, serverMetadata,
- kurentoTokenOptions);
+ Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), serverMetadata, record, role,
+ kurentoOptions);
session.storeToken(tokenObj);
session.showTokens("Token created");
- return tokenObj.getToken();
+ return tokenObj;
}
- public Token newTokenForInsecureUser(Session session, String token, String serverMetadata) throws Exception {
- Token tokenObj = new Token(token, OpenViduRole.PUBLISHER, serverMetadata != null ? serverMetadata : "",
- this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null, null);
+ public Token newTokenForInsecureUser(Session session, String token, ConnectionProperties connectionProperties)
+ throws Exception {
+ Token tokenObj = new Token(token, session.getSessionId(), connectionProperties,
+ this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null);
session.storeToken(tokenObj);
session.showTokens("Token created for insecure user");
return tokenObj;
@@ -357,17 +364,13 @@ public abstract class SessionManager {
public Participant newParticipant(String sessionId, String participantPrivatetId, Token token,
String clientMetadata, GeoLocation location, String platform, String finalUserId) {
+
if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) {
- String participantPublicId = IdentifierPrefixes.PARTICIPANT_PUBLIC_ID
- + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(9);
- Participant p = new Participant(finalUserId, participantPrivatetId, participantPublicId, sessionId, token,
- clientMetadata, location, platform, EndpointType.WEBRTC_ENDPOINT, null);
- while (this.sessionidParticipantpublicidParticipant.get(sessionId).putIfAbsent(participantPublicId,
- p) != null) {
- participantPublicId = IdentifierPrefixes.PARTICIPANT_PUBLIC_ID
- + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(9);
- p.setParticipantPublicId(participantPublicId);
- }
+
+ Participant p = new Participant(finalUserId, participantPrivatetId, token.getConnectionId(), sessionId,
+ token, clientMetadata, location, platform, EndpointType.WEBRTC_ENDPOINT, null);
+
+ this.sessionidParticipantpublicidParticipant.get(sessionId).put(p.getParticipantPublicId(), p);
this.sessionidFinalUsers.get(sessionId).computeIfAbsent(finalUserId, k -> {
log.info("Participant {} of session {} is a final user connecting to this session for the first time",
@@ -376,6 +379,7 @@ public abstract class SessionManager {
}).addConnectionIfAbsent(p);
return p;
+
} else {
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, sessionId);
}
@@ -425,6 +429,9 @@ public abstract class SessionManager {
log.warn("Error closing session '{}': {}", sessionId, e.getMessage());
}
}
+ if (this.sessionGarbageCollectorTimer != null) {
+ this.sessionGarbageCollectorTimer.cancelTimer();
+ }
}
@PostConstruct
@@ -434,44 +441,18 @@ public abstract class SessionManager {
"Garbage collector for non active sessions is disabled (property 'OPENVIDU_SESSIONS_GARBAGE_INTERVAL' is 0)");
return;
}
- new UpdatableTimerTask(() -> {
+
+ this.sessionGarbageCollectorTimer = new UpdatableTimerTask(() -> {
// Remove all non active sessions created more than the specified time
log.info("Running non active sessions garbage collector...");
final long currentMillis = System.currentTimeMillis();
- // Loop through all non active sessions. Safely remove them and clean all of
- // their data if their threshold has elapsed
- for (Iterator> iter = sessionsNotActive.entrySet().iterator(); iter.hasNext();) {
- final Session sessionNotActive = iter.next().getValue();
- final String sessionId = sessionNotActive.getSessionId();
- long sessionExistsSince = currentMillis - sessionNotActive.getStartTime();
- if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) {
- try {
- if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
- try {
- if (sessions.containsKey(sessionId)) {
- // The session passed to active during lock wait
- continue;
- }
- iter.remove();
- cleanCollections(sessionId);
- log.info("Non active session {} cleaned up by garbage collector", sessionId);
- } finally {
- sessionNotActive.closingLock.writeLock().unlock();
- }
- } else {
- log.error(
- "Timeout waiting for Session closing lock to be available for garbage collector to clean session {}",
- sessionId);
- }
- } catch (InterruptedException e) {
- log.error(
- "InterruptedException while waiting for Session closing lock to be available for garbage collector to clean session {}",
- sessionId);
- }
- }
- }
+ this.closeNonActiveSessions(sessionNotActive -> {
+ // Remove non active session if threshold has elapsed
+ return (currentMillis - sessionNotActive.getStartTime()) > (openviduConfig.getSessionGarbageThreshold()
+ * 1000);
+ });
// Warn about possible ghost sessions
for (Iterator> iter = sessions.entrySet().iterator(); iter.hasNext();) {
@@ -480,7 +461,9 @@ public abstract class SessionManager {
log.warn("Possible ghost session {}", sessionActive.getSessionId());
}
}
- }, () -> new Long(openviduConfig.getSessionGarbageInterval() * 1000)).updateTimer();
+ }, () -> Long.valueOf(openviduConfig.getSessionGarbageInterval() * 1000));
+
+ this.sessionGarbageCollectorTimer.updateTimer();
log.info(
"Garbage collector for non active sessions initialized. Running every {} seconds and cleaning up non active Sessions more than {} seconds old",
@@ -543,6 +526,40 @@ public abstract class SessionManager {
}
}
+ public void closeNonActiveSessions(Function conditionToRemove) {
+ // Loop through all non active sessions. Safely remove and clean all of
+ // the data for each non active session meeting the condition
+ for (Iterator> iter = sessionsNotActive.entrySet().iterator(); iter.hasNext();) {
+ final Session sessionNotActive = iter.next().getValue();
+ final String sessionId = sessionNotActive.getSessionId();
+ if (conditionToRemove.apply(sessionNotActive)) {
+ try {
+ if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
+ try {
+ if (sessions.containsKey(sessionId)) {
+ // The session passed to active during lock wait
+ continue;
+ }
+ iter.remove();
+ cleanCollections(sessionId);
+ log.info("Non active session {} cleaned up", sessionId);
+ } finally {
+ sessionNotActive.closingLock.writeLock().unlock();
+ }
+ } else {
+ log.error(
+ "Timeout waiting for Session closing lock to be available to clean up non active session {}",
+ sessionId);
+ }
+ } catch (InterruptedException e) {
+ log.error(
+ "InterruptedException while waiting for non active Session closing lock to be available to clean up non active session session {}",
+ sessionId);
+ }
+ }
+ }
+ }
+
public void closeSessionAndEmptyCollections(Session session, EndReason reason, boolean stopRecording) {
if (openviduConfig.isRecordingModuleEnabled()) {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java
index feeb99af..a44b9d6a 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java
@@ -17,60 +17,193 @@
package io.openvidu.server.core;
+import org.apache.commons.lang3.RandomStringUtils;
+
+import com.google.gson.JsonObject;
+
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.ConnectionType;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole;
+import io.openvidu.server.core.Participant.ParticipantStatus;
import io.openvidu.server.coturn.TurnCredentials;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
public class Token {
private String token;
- private OpenViduRole role;
- private String serverMetadata = "";
+ private String sessionId;
+ private Long createdAt;
+ private ConnectionProperties connectionProperties;
private TurnCredentials turnCredentials;
- private KurentoTokenOptions kurentoTokenOptions;
+ private final String connectionId = IdentifierPrefixes.PARTICIPANT_PUBLIC_ID
+ + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(9);
- public Token(String token) {
+ public Token(String token, String sessionId, ConnectionProperties connectionProperties,
+ TurnCredentials turnCredentials) {
this.token = token;
+ this.sessionId = sessionId;
+ this.createdAt = System.currentTimeMillis();
+ this.connectionProperties = connectionProperties;
+ this.turnCredentials = turnCredentials;
}
- public Token(String token, OpenViduRole role, String serverMetadata, TurnCredentials turnCredentials,
- KurentoTokenOptions kurentoTokenOptions) {
- this.token = token;
- this.role = role;
- this.serverMetadata = serverMetadata;
- this.turnCredentials = turnCredentials;
- this.kurentoTokenOptions = kurentoTokenOptions;
+ public ConnectionType getType() {
+ return this.connectionProperties.getType();
}
public String getToken() {
return token;
}
- public void setToken(String token) {
- this.token = token;
+ public void setToken(String newToken) {
+ this.token = newToken;
}
-
- public OpenViduRole getRole() {
- return role;
+
+ public Long getCreatedAt() {
+ return this.createdAt;
}
public String getServerMetadata() {
- return serverMetadata;
+ return this.connectionProperties.getData();
+ }
+
+ public Boolean record() {
+ return this.connectionProperties.record();
+ }
+
+ public void setRecord(boolean newRecord) {
+ this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(), newRecord,
+ connectionProperties.getRole(), connectionProperties.getKurentoOptions(),
+ connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(),
+ connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache());
+ }
+
+ public OpenViduRole getRole() {
+ return this.connectionProperties.getRole();
+ }
+
+ public void setRole(OpenViduRole newRole) {
+ this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(),
+ connectionProperties.record(), newRole, connectionProperties.getKurentoOptions(),
+ connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(),
+ connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache());
+ }
+
+ public KurentoOptions getKurentoOptions() {
+ return this.connectionProperties.getKurentoOptions();
+ }
+
+ public String getRtspUri() {
+ return this.connectionProperties.getRtspUri();
+ }
+
+ public Boolean adaptativeBitrate() {
+ return this.connectionProperties.adaptativeBitrate();
+ }
+
+ public Boolean onlyPlayWithSubscribers() {
+ return this.connectionProperties.onlyPlayWithSubscribers();
+ }
+
+ public Integer getNetworkCache() {
+ return this.connectionProperties.getNetworkCache();
}
public TurnCredentials getTurnCredentials() {
return turnCredentials;
}
- public KurentoTokenOptions getKurentoTokenOptions() {
- return kurentoTokenOptions;
+ public String getConnectionId() {
+ return connectionId;
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ json.addProperty("id", this.getToken());
+ json.addProperty("token", this.getToken());
+ json.addProperty("createdAt", this.getCreatedAt());
+ json.addProperty("connectionId", this.getConnectionId());
+ json.addProperty("session", this.sessionId);
+ json.addProperty("data", this.getServerMetadata());
+ json.addProperty("role", this.getRole().toString());
+ if (this.getKurentoOptions() != null) {
+ json.add("kurentoOptions", this.getKurentoOptions().toJson());
+ }
+ return json;
+ }
+
+ public JsonObject toJsonAsParticipant() {
+ JsonObject json = new JsonObject();
+ json.addProperty("id", this.getConnectionId());
+ json.addProperty("object", "connection");
+ json.addProperty("status", ParticipantStatus.pending.name());
+ json.addProperty("connectionId", this.getConnectionId()); // DEPRECATED: better use id
+ json.addProperty("sessionId", this.sessionId);
+ json.addProperty("createdAt", this.createdAt);
+
+ // Add all ConnectionProperties
+ JsonObject connectionPropertiesJson = this.getConnectionPropertiesWithFinalJsonFormat();
+ connectionPropertiesJson.entrySet().forEach(entry -> {
+ json.add(entry.getKey(), entry.getValue());
+ });
+
+ json.addProperty("token", this.getToken());
+ json.add("activeAt", null);
+ json.add("location", null);
+ json.add("platform", null);
+ json.add("clientData", null);
+ json.add("publishers", null);
+ json.add("subscribers", null);
+ return json;
+ }
+
+ protected JsonObject getConnectionPropertiesWithFinalJsonFormat() {
+ JsonObject json = this.connectionProperties.toJson(this.sessionId);
+ json.remove("session");
+ json.add("serverData", json.get("data"));
+ json.remove("data");
+ return json;
+ }
+
+ private void updateConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role,
+ KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers,
+ Integer networkCache) {
+ ConnectionProperties.Builder builder = new ConnectionProperties.Builder();
+ if (type != null) {
+ builder.type(type);
+ }
+ if (data != null) {
+ builder.data(data);
+ }
+ if (record != null) {
+ builder.record(record);
+ }
+ if (role != null) {
+ builder.role(role);
+ }
+ if (kurentoOptions != null) {
+ builder.kurentoOptions(kurentoOptions);
+ }
+ if (rtspUri != null) {
+ builder.rtspUri(rtspUri);
+ }
+ if (adaptativeBitrate != null) {
+ builder.adaptativeBitrate(adaptativeBitrate);
+ }
+ if (onlyPlayWithSubscribers != null) {
+ builder.onlyPlayWithSubscribers(onlyPlayWithSubscribers);
+ }
+ if (networkCache != null) {
+ builder.networkCache(networkCache);
+ }
+ this.connectionProperties = builder.build();
}
@Override
public String toString() {
- if (this.role != null)
- return this.role.name();
+ if (this.connectionProperties.getRole() != null)
+ return this.connectionProperties.getRole().name();
else
return this.token;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java
index 472f4ee8..35192b52 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java
@@ -17,12 +17,43 @@
package io.openvidu.server.core;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.ConnectionType;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
+import io.openvidu.server.OpenViduServer;
+import io.openvidu.server.config.OpenviduBuildInfo;
+import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.coturn.CoturnCredentialsService;
+import io.openvidu.server.coturn.TurnCredentials;
-public interface TokenGenerator {
+public class TokenGenerator {
- public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata,
- KurentoTokenOptions kurentoTokenOptions) throws Exception;
+ @Autowired
+ private CoturnCredentialsService coturnCredentialsService;
+
+ @Autowired
+ protected OpenviduConfig openviduConfig;
+
+ @Autowired
+ protected OpenviduBuildInfo openviduBuildConfig;
+
+ public Token generateToken(String sessionId, String serverMetadata, boolean record, OpenViduRole role,
+ KurentoOptions kurentoOptions) throws Exception {
+ String token = OpenViduServer.wsUrl;
+ token += "?sessionId=" + sessionId;
+ token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ + RandomStringUtils.randomAlphanumeric(15);
+ TurnCredentials turnCredentials = null;
+ if (this.openviduConfig.isTurnadminAvailable()) {
+ turnCredentials = coturnCredentialsService.createUser();
+ }
+ ConnectionProperties connectionProperties = new ConnectionProperties.Builder().type(ConnectionType.WEBRTC)
+ .data(serverMetadata).record(record).role(role).kurentoOptions(kurentoOptions).build();
+ return new Token(token, sessionId, connectionProperties, turnCredentials);
+ }
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java
deleted file mode 100644
index eeb46563..00000000
--- a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package io.openvidu.server.core;
-
-import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import io.openvidu.java.client.OpenViduRole;
-import io.openvidu.server.OpenViduServer;
-import io.openvidu.server.config.OpenviduBuildInfo;
-import io.openvidu.server.config.OpenviduConfig;
-import io.openvidu.server.coturn.CoturnCredentialsService;
-import io.openvidu.server.coturn.TurnCredentials;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
-
-public class TokenGeneratorDefault implements TokenGenerator {
-
- @Autowired
- private CoturnCredentialsService coturnCredentialsService;
-
- @Autowired
- protected OpenviduConfig openviduConfig;
-
- @Autowired
- protected OpenviduBuildInfo openviduBuildConfig;
-
- @Override
- public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata,
- KurentoTokenOptions kurentoTokenOptions) throws Exception {
- String token = OpenViduServer.wsUrl;
- token += "?sessionId=" + sessionId;
- token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
- + RandomStringUtils.randomAlphanumeric(15);
- token += "&role=" + role.name();
- token += "&version=" + openviduBuildConfig.getOpenViduServerVersion();
- TurnCredentials turnCredentials = null;
- if (this.openviduConfig.isTurnadminAvailable()) {
- turnCredentials = coturnCredentialsService.createUser();
- if (turnCredentials != null) {
- token += "&coturnIp=" + openviduConfig.getCoturnIp();
- token += "&turnUsername=" + turnCredentials.getUsername();
- token += "&turnCredential=" + turnCredentials.getCredential();
- }
- }
- return new Token(token, role, serverMetadata, turnCredentials, kurentoTokenOptions);
- }
-}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java
index 7684b821..1e477356 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java
@@ -32,6 +32,7 @@ public class KurentoMediaOptions extends MediaOptions {
public String rtspUri;
public Boolean adaptativeBitrate;
public Boolean onlyPlayWithSubscribers;
+ public Integer networkCache;
public KurentoMediaOptions(boolean isOffer, String sdpOffer, Boolean hasAudio, Boolean hasVideo,
Boolean audioActive, Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions,
@@ -45,7 +46,7 @@ public class KurentoMediaOptions extends MediaOptions {
public KurentoMediaOptions(boolean isOffer, String sdpOffer, Boolean hasAudio, Boolean hasVideo,
Boolean audioActive, Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions,
KurentoFilter filter, boolean doLoopback, String rtspUri, Boolean adaptativeBitrate,
- Boolean onlyPlayWithSubscribers) {
+ Boolean onlyPlayWithSubscribers, Integer networkCache) {
super(hasAudio, hasVideo, audioActive, videoActive, typeOfVideo, frameRate, videoDimensions, filter);
this.isOffer = isOffer;
this.sdpOffer = sdpOffer;
@@ -53,6 +54,7 @@ public class KurentoMediaOptions extends MediaOptions {
this.rtspUri = rtspUri;
this.adaptativeBitrate = adaptativeBitrate;
this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
+ this.networkCache = networkCache;
}
public KurentoMediaOptions(Boolean hasAudio, Boolean hasVideo, Boolean audioActive, Boolean videoActive,
@@ -65,6 +67,7 @@ public class KurentoMediaOptions extends MediaOptions {
this.rtspUri = streamProperties.rtspUri;
this.adaptativeBitrate = streamProperties.adaptativeBitrate;
this.onlyPlayWithSubscribers = streamProperties.onlyPlayWithSubscribers;
+ this.networkCache = streamProperties.networkCache;
}
@Override
@@ -76,6 +79,9 @@ public class KurentoMediaOptions extends MediaOptions {
if (onlyPlayWithSubscribers != null) {
json.addProperty("onlyPlayWithSubscribers", onlyPlayWithSubscribers);
}
+ if (networkCache != null) {
+ json.addProperty("networkCache", networkCache);
+ }
return json;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java
index 45017756..720bd9de 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java
@@ -37,6 +37,7 @@ import org.kurento.client.MediaElement;
import org.kurento.client.MediaPipeline;
import org.kurento.client.PassThrough;
import org.kurento.client.internal.server.KurentoServerException;
+import org.kurento.jsonrpc.JsonRpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -79,7 +80,7 @@ public class KurentoParticipant extends Participant {
super(participant.getFinalUserId(), participant.getParticipantPrivateId(), participant.getParticipantPublicId(),
kurentoSession.getSessionId(), participant.getToken(), participant.getClientMetadata(),
participant.getLocation(), participant.getPlatform(), participant.getEndpointType(),
- participant.getCreatedAt());
+ participant.getActiveAt());
this.endpointConfig = endpointConfig;
this.openviduConfig = openviduConfig;
this.recordingManager = recordingManager;
@@ -87,11 +88,15 @@ public class KurentoParticipant extends Participant {
if (!OpenViduRole.SUBSCRIBER.equals(participant.getToken().getRole())) {
// Initialize a PublisherEndpoint
- this.publisher = new PublisherEndpoint(endpointType, this, participant.getParticipantPublicId(),
- this.session.getPipeline(), this.openviduConfig, null);
+ initPublisherEndpoint();
}
}
+ public void initPublisherEndpoint() {
+ this.publisher = new PublisherEndpoint(endpointType, this, this.participantPublicId, this.session.getPipeline(),
+ this.openviduConfig, null);
+ }
+
public void createPublishingEndpoint(MediaOptions mediaOptions, String streamId) {
String type = mediaOptions.hasVideo() ? mediaOptions.getTypeOfVideo() : "MICRO";
if (streamId == null) {
@@ -135,6 +140,10 @@ public class KurentoParticipant extends Participant {
}
}
+ public boolean isPublisherEndpointDefined() {
+ return this.publisher != null;
+ }
+
public PublisherEndpoint getPublisher() {
try {
if (!publisherLatch.await(KurentoSession.ASYNC_LATCH_TIMEOUT, TimeUnit.SECONDS)) {
@@ -180,9 +189,9 @@ public class KurentoParticipant extends Participant {
log.info("PARTICIPANT {}: Is now publishing video in room {}", this.getParticipantPublicId(),
this.session.getSessionId());
- if (this.openviduConfig.isRecordingModuleEnabled()
+ if (this.openviduConfig.isRecordingModuleEnabled() && this.token.record()
&& this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) {
- this.recordingManager.startOneIndividualStreamRecording(session, null, null, this);
+ this.recordingManager.startOneIndividualStreamRecording(session, this);
}
if (!silent) {
@@ -193,7 +202,7 @@ public class KurentoParticipant extends Participant {
return sdpAnswer;
}
- public void unpublishMedia(EndReason reason, long kmsDisconnectionTime) {
+ public void unpublishMedia(EndReason reason, Long kmsDisconnectionTime) {
log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(),
this.session.getSessionId());
final MediaOptions mediaOptions = this.getPublisher().getMediaOptions();
@@ -390,7 +399,7 @@ public class KurentoParticipant extends Participant {
}
}
- public void close(EndReason reason, boolean definitelyClosed, long kmsDisconnectionTime) {
+ public void close(EndReason reason, boolean definitelyClosed, Long kmsDisconnectionTime) {
log.debug("PARTICIPANT {}: Closing user", this.getParticipantPublicId());
if (isClosed()) {
log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId());
@@ -405,11 +414,16 @@ public class KurentoParticipant extends Participant {
it.remove();
if (subscriber != null && subscriber.getEndpoint() != null) {
- releaseSubscriberEndpoint(remoteParticipantName,
- (KurentoParticipant) this.session.getParticipantByPublicId(remoteParticipantName), subscriber,
- reason, false);
- log.debug("PARTICIPANT {}: Released subscriber endpoint to {}", this.getParticipantPublicId(),
- remoteParticipantName);
+ try {
+ releaseSubscriberEndpoint(remoteParticipantName,
+ (KurentoParticipant) this.session.getParticipantByPublicId(remoteParticipantName),
+ subscriber, reason, false);
+ log.debug("PARTICIPANT {}: Released subscriber endpoint to {}", this.getParticipantPublicId(),
+ remoteParticipantName);
+ } catch (JsonRpcException e) {
+ log.error("Error releasing subscriber endpoint of participant {}: {}", this.participantPublicId,
+ e.getMessage());
+ }
} else {
log.warn(
"PARTICIPANT {}: Trying to close subscriber endpoint to {}. "
@@ -463,7 +477,7 @@ public class KurentoParticipant extends Participant {
session.sendMediaError(this.getParticipantPrivateId(), desc);
}
- private void releasePublisherEndpoint(EndReason reason, long kmsDisconnectionTime) {
+ private void releasePublisherEndpoint(EndReason reason, Long kmsDisconnectionTime) {
if (publisher != null && publisher.getEndpoint() != null) {
final ReadWriteLock closingLock = publisher.closingLock;
try {
@@ -486,26 +500,34 @@ public class KurentoParticipant extends Participant {
}
}
- private void releasePublisherEndpointAux(EndReason reason, long kmsDisconnectionTime) {
- // Remove streamId from publisher's map
- this.session.publishedStreamIds.remove(this.getPublisherStreamId());
+ private void releasePublisherEndpointAux(EndReason reason, Long kmsDisconnectionTime) {
+ try {
+ // Remove streamId from publisher's map
+ this.session.publishedStreamIds.remove(this.getPublisherStreamId());
- if (this.openviduConfig.isRecordingModuleEnabled()
- && this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) {
- this.recordingManager.stopOneIndividualStreamRecording(session, this.getPublisherStreamId(),
- kmsDisconnectionTime);
+ this.streaming = false;
+ this.session.deregisterPublisher(this);
+
+ if (this.openviduConfig.isRecordingModuleEnabled() && this.token.record()
+ && this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) {
+ this.recordingManager.stopOneIndividualStreamRecording(session, this.getPublisherStreamId(),
+ kmsDisconnectionTime);
+ }
+
+ publisher.cancelStatsLoop.set(true);
+
+ // These operations are all remote
+ publisher.unregisterErrorListeners();
+ for (MediaElement el : publisher.getMediaElements()) {
+ releaseElement(getParticipantPublicId(), el);
+ }
+ releaseElement(getParticipantPublicId(), publisher.getEndpoint());
+
+ } catch (JsonRpcException e) {
+ log.error("Error releasing publisher endpoint of participant {}: {}", this.participantPublicId,
+ e.getMessage());
}
- publisher.unregisterErrorListeners();
- publisher.cancelStatsLoop.set(true);
-
- for (MediaElement el : publisher.getMediaElements()) {
- releaseElement(getParticipantPublicId(), el);
- }
- releaseElement(getParticipantPublicId(), publisher.getEndpoint());
- this.streaming = false;
- this.session.deregisterPublisher();
-
endpointConfig.getCdr().stopPublisher(this.getParticipantPublicId(), publisher.getStreamId(), reason);
publisher = null;
}
@@ -603,6 +625,7 @@ public class KurentoParticipant extends Participant {
return this.sharedJson(MediaEndpoint::toJson);
}
+ @Override
public JsonObject withStatsToJson() {
return this.sharedJson(MediaEndpoint::withStatsToJson);
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java
index e0ad8cb4..e1771c7f 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java
@@ -88,7 +88,7 @@ public class KurentoSession extends Session {
}
public void newPublisher(Participant participant) {
- registerPublisher();
+ registerPublisher(participant);
log.debug("SESSION {}: Virtually subscribed other participants {} to new publisher {}", sessionId,
participants.values(), participant.getParticipantPublicId());
}
@@ -122,7 +122,7 @@ public class KurentoSession extends Session {
log.info("PARTICIPANT {}: Leaving session {}", participant.getParticipantPublicId(), this.sessionId);
this.removeParticipant(participant, reason);
- participant.close(reason, true, 0);
+ participant.close(reason, true, null);
}
@Override
@@ -133,7 +133,7 @@ public class KurentoSession extends Session {
for (Participant participant : participants.values()) {
((KurentoParticipant) participant).releaseAllFilters();
- ((KurentoParticipant) participant).close(reason, true, 0);
+ ((KurentoParticipant) participant).close(reason, true, null);
}
participants.clear();
@@ -280,7 +280,7 @@ public class KurentoSession extends Session {
return this.publishedStreamIds.get(streamId);
}
- public void restartStatusInKurento(long kmsDisconnectionTime) {
+ public void restartStatusInKurento(Long kmsDisconnectionTime) {
log.info("Resetting process: resetting remote media objects for active session {}", this.sessionId);
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionEventsHandler.java
index a47f18ce..ebf4aa0d 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionEventsHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionEventsHandler.java
@@ -29,9 +29,6 @@ import io.openvidu.server.core.SessionEventsHandler;
public class KurentoSessionEventsHandler extends SessionEventsHandler {
- public KurentoSessionEventsHandler() {
- }
-
public void onIceCandidate(String roomName, String participantPrivateId, String senderPublicId, String endpointName,
IceCandidate candidate) {
JsonObject params = new JsonObject();
@@ -59,11 +56,4 @@ public class KurentoSessionEventsHandler extends SessionEventsHandler {
rpcNotificationService.sendNotification(participantId, ProtocolElements.MEDIAERROR_METHOD, notifParams);
}
- public void updateFilter(String roomName, Participant participant, String filterId, String state) {
- }
-
- public String getNextFilterState(String filterId, String state) {
- return null;
- }
-
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java
index 5859ffeb..32085990 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
+import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -47,6 +48,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.Recording;
import io.openvidu.java.client.RecordingLayout;
@@ -76,14 +79,14 @@ public class KurentoSessionManager extends SessionManager {
private static final Logger log = LoggerFactory.getLogger(KurentoSessionManager.class);
@Autowired
- private KmsManager kmsManager;
+ protected KmsManager kmsManager;
@Autowired
- private KurentoSessionEventsHandler kurentoSessionEventsHandler;
+ protected KurentoSessionEventsHandler kurentoSessionEventsHandler;
@Autowired
- private KurentoParticipantEndpointConfig kurentoEndpointConfig;
-
+ protected KurentoParticipantEndpointConfig kurentoEndpointConfig;
+
@Autowired
private SDPMunging sdpMunging;
@@ -111,28 +114,14 @@ public class KurentoSessionManager extends SessionManager {
if (KmsManager.selectAndRemoveKmsLock.tryLock(KmsManager.MAX_SECONDS_LOCK_WAIT, TimeUnit.SECONDS)) {
try {
kSession = (KurentoSession) sessions.get(sessionId);
-
if (kSession == null) {
// Session still null. It was not created by other thread while waiting for lock
- Kms lessLoadedKms = null;
- try {
- lessLoadedKms = this.kmsManager.getLessLoadedConnectedAndRunningKms();
- } catch (NoSuchElementException e) {
- // Restore session not active
- this.cleanCollections(sessionId);
- this.storeSessionNotActive(sessionNotActive);
- throw new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE,
- "There is no available Media Node where to initialize session '" + sessionId
- + "'");
- }
- log.info("KMS less loaded is {} with a load of {}", lessLoadedKms.getUri(),
- lessLoadedKms.getLoad());
- kSession = createSession(sessionNotActive, lessLoadedKms);
+ Kms selectedMediaNode = this.selectMediaNode(sessionNotActive);
+ kSession = createSession(sessionNotActive, selectedMediaNode);
}
} finally {
KmsManager.selectAndRemoveKmsLock.unlock();
}
-
} else {
String error = "Timeout of " + KmsManager.MAX_SECONDS_LOCK_WAIT
+ " seconds waiting to acquire lock";
@@ -151,7 +140,8 @@ public class KurentoSessionManager extends SessionManager {
// If Recording default layout is COMPOSED_QUICK_START
Recording.OutputMode defaultOutputMode = kSession.getSessionProperties().defaultOutputMode();
- if (openviduConfig.isRecordingModuleEnabled() && defaultOutputMode.equals(Recording.OutputMode.COMPOSED_QUICK_START)) {
+ if (openviduConfig.isRecordingModuleEnabled()
+ && defaultOutputMode.equals(Recording.OutputMode.COMPOSED_QUICK_START)) {
recordingManager.startComposedQuickStartContainer(kSession);
}
@@ -309,10 +299,12 @@ public class KurentoSessionManager extends SessionManager {
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
- && session.getSessionProperties().defaultOutputMode().equals(Recording.OutputMode.COMPOSED_QUICK_START)
+ && session.getSessionProperties().defaultOutputMode()
+ .equals(Recording.OutputMode.COMPOSED_QUICK_START)
&& ProtocolElements.RECORDER_PARTICIPANT_PUBLICID
- .equals(remainingParticipants.iterator().next().getParticipantPublicId())) {
- // If no recordings are active in COMPOSED_QUICK_START output mode, stop container
+ .equals(remainingParticipants.iterator().next().getParticipantPublicId())) {
+ // If no recordings are active in COMPOSED_QUICK_START output mode, stop
+ // container
recordingManager.stopComposedQuickStartContainer(session, reason);
}
}
@@ -371,30 +363,15 @@ public class KurentoSessionManager extends SessionManager {
KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions;
KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession kSession = kParticipant.getSession();
+ SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER;
boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed();
VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec();
- log.debug("PARTICIPANT '{}' in Session '{}' publishing SDP before munging: \n {}",
- participant.getParticipantPublicId(), kSession.getSessionId(), kurentoOptions.sdpOffer);
- // Modify sdp if forced codec is defined and this is not an IP camera
+ // Modify sdp if forced codec is defined
if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) {
- String sdpOffer = kurentoOptions.sdpOffer;
- try {
- kurentoOptions.sdpOffer = this.sdpMunging.setCodecPreference(forcedVideoCodec, sdpOffer, true);
- log.debug("PARTICIPANT '{}' in Session '{}' publishing SDP Offer after munging: \n {}",
- participant.getParticipantPublicId(), kSession.getSessionId(), kurentoOptions.sdpOffer);
- } catch (OpenViduException e) {
- String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT"
- + participant.getParticipantPublicId() + "' publishing in Session: '"
- + kSession.getSessionId() + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpAnswer;
- if(!isTranscodingAllowed) {
- throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage);
- }
- log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId()
- + " publishing in Session: '" + kSession.getSessionId() + "'. Transcoding will be allowed");
- }
+ kurentoOptions.sdpOffer = sdpMunging.forceCodec(participant, kurentoOptions.sdpOffer,
+ kurentoOptions.isOffer, kSession, true, false, isTranscodingAllowed, forcedVideoCodec);
}
-
log.debug(
"Request [PUBLISH_MEDIA] isOffer={} sdp={} "
+ "loopbackAltSrc={} lpbkConnType={} doLoopback={} rtspUri={} ({})",
@@ -408,7 +385,7 @@ public class KurentoSessionManager extends SessionManager {
* kurentoParticipant.getPublisher().apply(elem); }
*/
- KurentoTokenOptions kurentoTokenOptions = participant.getToken().getKurentoTokenOptions();
+ KurentoOptions kurentoTokenOptions = participant.getToken().getKurentoOptions();
if (kurentoOptions.getFilter() != null && kurentoTokenOptions != null) {
if (kurentoTokenOptions.isFilterAllowed(kurentoOptions.getFilter().getType())) {
this.applyFilterInPublisher(kParticipant, kurentoOptions.getFilter());
@@ -424,9 +401,9 @@ public class KurentoSessionManager extends SessionManager {
throw e;
}
}
-
+
sdpAnswer = kParticipant.publishToRoom(kurentoOptions.sdpOffer, kurentoOptions.doLoopback, false);
-
+
if (sdpAnswer == null) {
OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE,
"Error generating SDP response for publishing user " + participant.getParticipantPublicId());
@@ -454,7 +431,10 @@ public class KurentoSessionManager extends SessionManager {
recordingManager.startRecording(kSession, new RecordingProperties.Builder().name("")
.outputMode(kSession.getSessionProperties().defaultOutputMode())
.recordingLayout(kSession.getSessionProperties().defaultRecordingLayout())
- .customLayout(kSession.getSessionProperties().defaultCustomLayout()).build());
+ .customLayout(kSession.getSessionProperties().defaultCustomLayout())
+ .resolution(/*
+ * kSession.getSessionProperties().defaultRecordingResolution()
+ */"1920x1080").mediaNode(kSession.getMediaNodeId()).build());
}).start();
} else if (recordingManager.sessionIsBeingRecorded(kSession.getSessionId())) {
// Abort automatic recording stop thread for any recorded session in which a
@@ -489,11 +469,13 @@ public class KurentoSessionManager extends SessionManager {
}
+ participant.setPublishedAt(new Timestamp(System.currentTimeMillis()).getTime());
kSession.newPublisher(participant);
participants = kParticipant.getSession().getParticipants();
if (sdpAnswer != null) {
+ log.debug("SDP Answer for publishing PARTICIPANT {}: {}", participant.getParticipantPublicId(), sdpAnswer);
sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(),
kParticipant.getPublisher().createdAt(), kSession.getSessionId(), mediaOptions, sdpAnswer,
participants, transactionId, null);
@@ -517,7 +499,7 @@ public class KurentoSessionManager extends SessionManager {
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"Participant '" + participant.getParticipantPublicId() + "' is not streaming media");
}
- kParticipant.unpublishMedia(reason, 0);
+ kParticipant.unpublishMedia(reason, null);
session.cancelPublisher(participant, reason);
Set participants = session.getParticipants();
@@ -566,7 +548,7 @@ public class KurentoSessionManager extends SessionManager {
sdpOffer = kParticipant.prepareReceiveMediaFrom(senderParticipant);
VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec();
boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed();
-
+
if (forcedVideoCodec != VideoCodec.NONE) {
try {
log.debug("PARTICIPANT '{}' in Session '{}' SDP Offer before munging: \n {}",
@@ -576,20 +558,20 @@ public class KurentoSessionManager extends SessionManager {
sdpOffer = this.sdpMunging.setfmtpH264(sdpOffer);
}
} catch (OpenViduException e) {
- String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
- + participant.getParticipantPublicId() + "' subscribing in Session: '"
+ String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
+ + participant.getParticipantPublicId() + "' subscribing in Session: '"
+ session.getSessionId() + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpOffer;
-
+
if(!isTranscodingAllowed) {
throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage);
}
- log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId()
+ log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId()
+ " subscribing in Session: '" + session.getSessionId() + "'. Transcoding will be allowed");
}
}
log.debug("Request [SUBSCRIBE] remoteParticipant={} sdpOffer={} ({})", senderPublicId, sdpOffer,
participant.getParticipantPublicId());
-
+
if (sdpOffer == null) {
throw new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, "Unable to generate SDP offer when subscribing '"
+ participant.getParticipantPublicId() + "' to '" + senderPublicId + "'");
@@ -616,28 +598,12 @@ public class KurentoSessionManager extends SessionManager {
Participant senderParticipant = session.getParticipantByPublicId(senderName);
boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed();
VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec();
-
- log.debug("PARTICIPANT '{}' subscribing in Session '{}' SDP Answer before munging: \n {}",
- participant.getParticipantPublicId(), session.getSessionId(), sdpAnswer);
+
// Modify sdp if forced codec is defined
- if (forcedVideoCodec != VideoCodec.NONE) {
- try {
- sdpAnswer = this.sdpMunging.setCodecPreference(forcedVideoCodec, sdpAnswer, true);
- log.debug("PARTICIPANT '{}' subscribing in Session '{}' SDP Answer after munging: \n {}",
- participant.getParticipantPublicId(), session.getSessionId(), sdpAnswer);
- } catch (OpenViduException e) {
- String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
- + participant.getParticipantPublicId() + "' subscribing in Session: '"
- + session.getSessionId() + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpAnswer;
-
- if(!isTranscodingAllowed) {
- throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage);
- }
- log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId()
- + " subscribing in Session: '" + session.getSessionId() + "'. Transcoding will be allowed");
- }
+ if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) {
+ sdpOffer = sdpMunging.forceCodec(participant, sdpOffer, true, session, false, false,
+ isTranscodingAllowed, forcedVideoCodec);
}
-
if (senderParticipant == null) {
log.warn(
@@ -660,7 +626,11 @@ public class KurentoSessionManager extends SessionManager {
sessionEventsHandler.onSubscribe(participant, session, transactionId, null);
} catch (OpenViduException e) {
log.error("PARTICIPANT {}: Error subscribing to {}", participant.getParticipantPublicId(), senderName, e);
- sessionEventsHandler.onSubscribe(participant, session, transactionId, e);
+ sessionEventsHandler.onSubscribe(participant, session, null, transactionId, e);
+ }
+ if (sdpAnswer != null) {
+ log.debug("SDP Answer for subscribing PARTICIPANT {}: {}", participant.getParticipantPublicId(), sdpAnswer);
+ sessionEventsHandler.onSubscribe(participant, session, sdpAnswer, transactionId, null);
}
}
@@ -740,7 +710,7 @@ public class KurentoSessionManager extends SessionManager {
/**
* Creates a session with the already existing not-active session in the
* indicated KMS, if it doesn't already exist
- *
+ *
* @throws OpenViduException in case of error while creating the session
*/
public KurentoSession createSession(Session sessionNotActive, Kms kms) throws OpenViduException {
@@ -1099,8 +1069,8 @@ public class KurentoSessionManager extends SessionManager {
@Override
/* Protected by Session.closingLock.readLock */
- public Participant publishIpcam(Session session, MediaOptions mediaOptions, String serverMetadata)
- throws Exception {
+ public Participant publishIpcam(Session session, MediaOptions mediaOptions,
+ ConnectionProperties connectionProperties) throws Exception {
final String sessionId = session.getSessionId();
final KurentoMediaOptions kMediaOptions = (KurentoMediaOptions) mediaOptions;
@@ -1133,7 +1103,8 @@ public class KurentoSessionManager extends SessionManager {
}
String rtspConnectionId = kMediaOptions.getTypeOfVideo() + "_" + protocol + "_"
- + RandomStringUtils.randomAlphanumeric(4).toUpperCase() + "_" + url.getAuthority() + url.getPath();
+ + RandomStringUtils.randomAlphanumeric(4).toUpperCase() + "_" + url.getHost()
+ + (url.getPort() != -1 ? (":" + url.getPort()) : "") + url.getPath();
rtspConnectionId = rtspConnectionId.replace("/", "_").replace("-", "").replace(".", "_").replace(":", "_");
rtspConnectionId = IdentifierPrefixes.IPCAM_ID + rtspConnectionId;
@@ -1141,7 +1112,8 @@ public class KurentoSessionManager extends SessionManager {
this.newInsecureParticipant(rtspConnectionId);
String token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ RandomStringUtils.randomAlphanumeric(15);
- this.newTokenForInsecureUser(session, token, serverMetadata);
+
+ this.newTokenForInsecureUser(session, token, connectionProperties);
final Token tokenObj = session.consumeToken(token);
Participant ipcamParticipant = this.newIpcamParticipant(sessionId, rtspConnectionId, tokenObj, location,
@@ -1172,26 +1144,11 @@ public class KurentoSessionManager extends SessionManager {
boolean isPublisher = streamId.equals(participant.getPublisherStreamId());
boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed();
VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec();
-
- log.debug("PARTICIPANT '{}' in Session '{}' reconnecting SDP before munging: \n {}",
- participant.getParticipantPublicId(), kSession.getSessionId(), sdpString);
+
// Modify sdp if forced codec is defined
- if (forcedVideoCodec != VideoCodec.NONE) {
- try {
- sdpString = sdpMunging.setCodecPreference(forcedVideoCodec, sdpString, true);
- log.debug("PARTICIPANT '{}' in Session '{}' reconnecting SDP after munging: \n {}",
- participant.getParticipantPublicId(), kSession.getSessionId(), sdpString);
- } catch (OpenViduException e) {
- String errorMessage = "Error in reconnect and forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
- + participant.getParticipantPublicId() + "' " + (isPublisher ? "publishing" : "subscribing")
- + " in Session: '" + kSession.getSessionId() + "'\nException: "
- + e.getMessage() + "\nSDP:\n" + sdpString;
- if(!isTranscodingAllowed) {
- throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage);
- }
- log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId()
- + "' " + (isPublisher ? "publishing" : "subscribing") + " in Session: '" + kSession.getSessionId() + "'. Transcoding will be allowed");
- }
+ if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) {
+ sdpOffer = sdpMunging.forceCodec(participant, sdpOffer, true, kSession, isPublisher, true,
+ isTranscodingAllowed, forcedVideoCodec);
}
if (isPublisher) {
@@ -1211,8 +1168,10 @@ public class KurentoSessionManager extends SessionManager {
// 3) Create a new PublisherEndpoint connecting it to the previous PassThrough
kParticipant.resetPublisherEndpoint(kurentoOptions, passThru);
kParticipant.createPublishingEndpoint(kurentoOptions, streamId);
- String sdpAnswer = kParticipant.publishToRoom(sdpString, kurentoOptions.doLoopback, true);
-
+ SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER;
+ String sdpAnswer = kParticipant.publishToRoom(sdpType, sdpOffer, kurentoOptions.doLoopback, true);
+ log.debug("SDP Answer for publishing reconnection PARTICIPANT {}: {}", participant.getParticipantPublicId(),
+ sdpAnswer);
sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(),
kParticipant.getPublisher().createdAt(), kSession.getSessionId(), kurentoOptions, sdpAnswer,
new HashSet(), transactionId, null);
@@ -1223,8 +1182,16 @@ public class KurentoSessionManager extends SessionManager {
String senderPrivateId = kSession.getParticipantPrivateIdFromStreamId(streamId);
if (senderPrivateId != null) {
KurentoParticipant sender = (KurentoParticipant) kSession.getParticipantByPrivateId(senderPrivateId);
- kParticipant.receiveMediaFrom(sender, sdpString, true);
- sessionEventsHandler.onSubscribe(participant, kSession, transactionId, null);
+ kParticipant.cancelReceivingMedia(sender, null, true);
+ String sdpAnswer = kParticipant.receiveMediaFrom(sender, sdpOffer, true);
+ if (sdpAnswer == null) {
+ throw new OpenViduException(Code.MEDIA_SDP_ERROR_CODE,
+ "Unable to generate SDP answer when reconnecting subscriber to '" + streamId + "'");
+ }
+
+ log.debug("SDP Answer for subscribing reconnection PARTICIPANT {}: {}",
+ participant.getParticipantPublicId(), sdpAnswer);
+ sessionEventsHandler.onSubscribe(participant, kSession, sdpAnswer, transactionId, null);
} else {
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"Stream '" + streamId + "' does not exist in Session '" + kSession.getSessionId() + "'");
@@ -1238,7 +1205,13 @@ public class KurentoSessionManager extends SessionManager {
return ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
}
- private void applyFilterInPublisher(KurentoParticipant kParticipant, KurentoFilter filter)
+ @Override
+ public void onVideoData(Participant participant, Integer transactionId, Integer height, Integer width,
+ Boolean videoActive, Boolean audioActive) {
+ sessionEventsHandler.onVideoData(participant, transactionId, height, width, videoActive, audioActive);
+ }
+
+ protected void applyFilterInPublisher(KurentoParticipant kParticipant, KurentoFilter filter)
throws OpenViduException {
GenericMediaElement.Builder builder = new GenericMediaElement.Builder(kParticipant.getPipeline(),
filter.getType());
@@ -1250,13 +1223,13 @@ public class KurentoSessionManager extends SessionManager {
kParticipant.getPublisher().getMediaOptions().setFilter(filter);
}
- private void removeFilterInPublisher(KurentoParticipant kParticipant) {
+ protected void removeFilterInPublisher(KurentoParticipant kParticipant) {
kParticipant.getPublisher().cleanAllFilterListeners();
kParticipant.getPublisher().revert(kParticipant.getPublisher().getFilter());
kParticipant.getPublisher().getMediaOptions().setFilter(null);
}
- private KurentoFilter execFilterMethodInPublisher(KurentoParticipant kParticipant, String method,
+ protected KurentoFilter execFilterMethodInPublisher(KurentoParticipant kParticipant, String method,
JsonObject params) {
kParticipant.getPublisher().execMethod(method, params);
KurentoFilter filter = kParticipant.getPublisher().getMediaOptions().getFilter();
@@ -1265,7 +1238,7 @@ public class KurentoSessionManager extends SessionManager {
return updatedFilter;
}
- private void addFilterEventListenerInPublisher(KurentoParticipant kParticipant, String eventType)
+ protected void addFilterEventListenerInPublisher(KurentoParticipant kParticipant, String eventType)
throws OpenViduException {
PublisherEndpoint pub = kParticipant.getPublisher();
if (!pub.isListenerAddedToFilterEvent(eventType)) {
@@ -1289,11 +1262,27 @@ public class KurentoSessionManager extends SessionManager {
}
}
- private void removeFilterEventListenerInPublisher(KurentoParticipant kParticipant, String eventType) {
+ protected void removeFilterEventListenerInPublisher(KurentoParticipant kParticipant, String eventType) {
PublisherEndpoint pub = kParticipant.getPublisher();
if (pub.isListenerAddedToFilterEvent(eventType)) {
GenericMediaElement filter = kParticipant.getPublisher().getFilter();
filter.removeEventListener(pub.removeListener(eventType));
}
}
+
+ protected Kms selectMediaNode(Session session) throws OpenViduException {
+ Kms lessLoadedKms = null;
+ try {
+ lessLoadedKms = this.kmsManager.getLessLoadedConnectedAndRunningKms();
+ } catch (NoSuchElementException e) {
+ // Restore session not active
+ this.cleanCollections(session.getSessionId());
+ this.storeSessionNotActive(session);
+ throw new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE,
+ "There is no available Media Node where to initialize session '" + session.getSessionId() + "'");
+ }
+ log.info("KMS less loaded is {} with a load of {}", lessLoadedKms.getUri(), lessLoadedKms.getLoad());
+ return lessLoadedKms;
+ }
+
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java
deleted file mode 100644
index cc785116..00000000
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package io.openvidu.server.kurento.core;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
-public class KurentoTokenOptions {
-
- private Integer videoMaxRecvBandwidth;
- private Integer videoMinRecvBandwidth;
- private Integer videoMaxSendBandwidth;
- private Integer videoMinSendBandwidth;
- private Map allowedFilters = new ConcurrentHashMap<>();
-
- public KurentoTokenOptions(JsonObject options) {
- if (options.has("videoMaxRecvBandwidth")) {
- this.videoMaxRecvBandwidth = options.get("videoMaxRecvBandwidth").getAsInt();
- }
- if (options.has("videoMinRecvBandwidth")) {
- this.videoMinRecvBandwidth = options.get("videoMinRecvBandwidth").getAsInt();
- }
- if (options.has("videoMaxSendBandwidth")) {
- this.videoMaxSendBandwidth = options.get("videoMaxSendBandwidth").getAsInt();
- }
- if (options.has("videoMinSendBandwidth")) {
- this.videoMinSendBandwidth = options.get("videoMinSendBandwidth").getAsInt();
- }
- if (options.has("allowedFilters")) {
- JsonArray filters = options.get("allowedFilters").getAsJsonArray();
- Iterator it = filters.iterator();
- while (it.hasNext()) {
- this.allowedFilters.put(it.next().getAsString(), true);
- }
- }
- }
-
- public Integer getVideoMaxRecvBandwidth() {
- return videoMaxRecvBandwidth;
- }
-
- public Integer getVideoMinRecvBandwidth() {
- return videoMinRecvBandwidth;
- }
-
- public Integer getVideoMaxSendBandwidth() {
- return videoMaxSendBandwidth;
- }
-
- public Integer getVideoMinSendBandwidth() {
- return videoMinSendBandwidth;
- }
-
- public String[] getAllowedFilters() {
- return allowedFilters.keySet().stream().toArray(String[]::new);
- }
-
- public boolean isFilterAllowed(String filterType) {
- return this.allowedFilters.containsKey(filterType);
- }
-
-}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java
index ff6dfe36..19bc9899 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java
@@ -54,11 +54,11 @@ import com.google.gson.JsonObject;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.Participant;
import io.openvidu.server.kurento.core.KurentoMediaOptions;
import io.openvidu.server.kurento.core.KurentoParticipant;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
/**
* {@link Endpoint} wrapper. Can be based on WebRtcEndpoint (that supports
@@ -129,7 +129,7 @@ public abstract class MediaEndpoint {
this.openviduConfig = openviduConfig;
- KurentoTokenOptions kurentoTokenOptions = this.owner.getToken().getKurentoTokenOptions();
+ KurentoOptions kurentoTokenOptions = this.owner.getToken().getKurentoOptions();
if (kurentoTokenOptions != null) {
this.maxRecvKbps = kurentoTokenOptions.getVideoMaxRecvBandwidth() != null
? kurentoTokenOptions.getVideoMaxRecvBandwidth()
@@ -365,6 +365,9 @@ public abstract class MediaEndpoint {
if (!mediaOptions.adaptativeBitrate) {
playerBuilder = playerBuilder.useEncodedMedia();
}
+ if (mediaOptions.networkCache != null) {
+ playerBuilder = playerBuilder.withNetworkCache(mediaOptions.networkCache);
+ }
playerBuilder.buildAsync(new Continuation() {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/FixedOneKmsManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/FixedOneKmsManager.java
index 192859a1..fd4e23c6 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/FixedOneKmsManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/FixedOneKmsManager.java
@@ -20,24 +20,37 @@ package io.openvidu.server.kurento.kms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
import javax.annotation.PostConstruct;
-import org.apache.commons.lang3.RandomStringUtils;
import org.kurento.client.KurentoClient;
import org.kurento.commons.exception.KurentoException;
+import org.kurento.jsonrpc.client.JsonRpcClientNettyWebSocket;
+import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener;
-import io.openvidu.server.core.IdentifierPrefixes;
+import io.openvidu.java.client.RecordingProperties;
+import io.openvidu.server.core.Session;
+import io.openvidu.server.core.SessionManager;
public class FixedOneKmsManager extends KmsManager {
+ public FixedOneKmsManager(SessionManager sessionManager) {
+ super(sessionManager);
+ }
+
@Override
- public List initializeKurentoClients(List kmsProperties, boolean disconnectUponFailure) throws Exception {
+ public List initializeKurentoClients(List kmsProperties, boolean disconnectUponFailure)
+ throws Exception {
KmsProperties firstProps = kmsProperties.get(0);
KurentoClient kClient = null;
- Kms kms = new Kms(firstProps, loadManager);
+ Kms kms = new Kms(firstProps, loadManager, quarantineKiller);
try {
- kClient = KurentoClient.create(firstProps.getUri(), this.generateKurentoConnectionListener(kms.getId()));
+ JsonRpcWSConnectionListener listener = this.generateKurentoConnectionListener(kms.getId());
+ JsonRpcClientNettyWebSocket client = new JsonRpcClientNettyWebSocket(firstProps.getUri(), listener);
+ client.setTryReconnectingMaxTime(0);
+ client.setTryReconnectingForever(false);
+ kClient = KurentoClient.createFromJsonRpcClient(client);
this.addKms(kms);
kms.setKurentoClient(kClient);
@@ -55,6 +68,25 @@ public class FixedOneKmsManager extends KmsManager {
return Arrays.asList(kms);
}
+ @Override
+ public void incrementActiveRecordings(RecordingProperties properties, String recordingId, Session session) {
+ try {
+ this.getKmss().iterator().next().incrementActiveRecordings(recordingId, session.getSessionId());
+ } catch (NoSuchElementException e) {
+ log.error("There is no KMS available when incrementing active recordings");
+ }
+ }
+
+ @Override
+ public void decrementActiveRecordings(RecordingProperties recordingProperties, String recordingId,
+ Session session) {
+ try {
+ this.getKmss().iterator().next().decrementActiveRecordings(recordingId);
+ } catch (NoSuchElementException e) {
+ log.error("There is no KMS available when decrementing active recordings");
+ }
+ }
+
@Override
@PostConstruct
protected void postConstructInitKurentoClients() {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java
index 72b3cca4..bcdd10f9 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java
@@ -21,9 +21,10 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.kurento.client.KurentoClient;
@@ -36,6 +37,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.openvidu.server.kurento.core.KurentoSession;
+import io.openvidu.server.utils.QuarantineKiller;
/**
* Abstraction of a KMS instance: an object of this class corresponds to a KMS
@@ -57,15 +59,16 @@ public class Kms {
private String ip;
private KurentoClient client;
private LoadManager loadManager;
+ private QuarantineKiller quarantineKiller;
private AtomicBoolean isKurentoClientConnected = new AtomicBoolean(false);
private AtomicLong timeOfKurentoClientConnection = new AtomicLong(0);
private AtomicLong timeOfKurentoClientDisconnection = new AtomicLong(0);
private Map kurentoSessions = new ConcurrentHashMap<>();
- private AtomicInteger activeRecordings = new AtomicInteger(0);
+ private Map activeRecordings = new ConcurrentHashMap<>();
- public Kms(KmsProperties props, LoadManager loadManager) {
+ public Kms(KmsProperties props, LoadManager loadManager, QuarantineKiller quarantineKiller) {
this.id = props.getId();
this.uri = props.getUri();
@@ -79,6 +82,7 @@ public class Kms {
this.ip = url.getHost();
this.loadManager = loadManager;
+ this.quarantineKiller = quarantineKiller;
}
public void setKurentoClient(KurentoClient client) {
@@ -145,16 +149,25 @@ public class Kms {
this.kurentoSessions.remove(sessionId);
}
- public AtomicInteger getActiveRecordings() {
- return this.activeRecordings;
+ public synchronized Set> getActiveRecordings() {
+ return this.activeRecordings.entrySet();
+ }
+
+ public synchronized void incrementActiveRecordings(String recordingId, String sessionId) {
+ this.activeRecordings.put(recordingId, sessionId);
+ }
+
+ public synchronized void decrementActiveRecordings(String recordingId) {
+ this.activeRecordings.remove(recordingId);
+ this.quarantineKiller.dropMediaNode(this.id);
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("id", this.id);
+ json.addProperty("object", "mediaNode");
json.addProperty("ip", this.ip);
json.addProperty("uri", this.uri);
-
final boolean connected = this.isKurentoClientConnected();
json.addProperty("connected", connected);
json.addProperty("connectionTime", this.getTimeOfKurentoClientConnection());
@@ -164,18 +177,26 @@ public class Kms {
return json;
}
- public JsonObject toJsonExtended(boolean withSessions, boolean withExtraInfo) {
+ public JsonObject toJsonExtended(boolean withSessions, boolean withRecordings, boolean withExtraInfo) {
JsonObject json = this.toJson();
if (withSessions) {
JsonArray sessions = new JsonArray();
for (KurentoSession session : this.kurentoSessions.values()) {
- sessions.add(session.toJson());
+ sessions.add(session.toJson(false, false));
}
json.add("sessions", sessions);
}
+ if (withRecordings) {
+ JsonArray activeRecordingsJson = new JsonArray();
+ for (String recordingId : this.activeRecordings.keySet()) {
+ activeRecordingsJson.add(recordingId);
+ }
+ json.add("recordingIds", activeRecordingsJson);
+ }
+
if (withExtraInfo) {
if (json.get("connected").getAsBoolean()) {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsManager.java
index f93112c3..b1fcab94 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/KmsManager.java
@@ -23,8 +23,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -34,17 +34,23 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.RandomStringUtils;
-import org.kurento.client.KurentoConnectionListener;
+import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.JsonObject;
+import io.openvidu.java.client.RecordingProperties;
import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.IdentifierPrefixes;
+import io.openvidu.server.core.Session;
+import io.openvidu.server.core.SessionEventsHandler;
+import io.openvidu.server.core.SessionManager;
import io.openvidu.server.kurento.core.KurentoSession;
import io.openvidu.server.utils.MediaNodeStatusManager;
+import io.openvidu.server.utils.QuarantineKiller;
import io.openvidu.server.utils.UpdatableTimerTask;
public abstract class KmsManager {
@@ -54,7 +60,7 @@ public abstract class KmsManager {
public static final Lock selectAndRemoveKmsLock = new ReentrantLock(true);
public static final int MAX_SECONDS_LOCK_WAIT = 15;
- private Map kmsReconnectionLocks = new ConcurrentHashMap<>();
+ private UpdatableTimerTask kurentoReconnectTimer;
public class KmsLoad implements Comparable {
@@ -85,8 +91,8 @@ public abstract class KmsManager {
return json;
}
- public JsonObject toJsonExtended(boolean withSessions, boolean withExtraInfo) {
- JsonObject json = this.kms.toJsonExtended(withSessions, withExtraInfo);
+ public JsonObject toJsonExtended(boolean withSessions, boolean withRecordings, boolean withExtraInfo) {
+ JsonObject json = this.kms.toJsonExtended(withSessions, withRecordings, withExtraInfo);
json.addProperty("load", this.load);
return json;
}
@@ -98,11 +104,23 @@ public abstract class KmsManager {
@Autowired
protected LoadManager loadManager;
+ @Autowired
+ protected QuarantineKiller quarantineKiller;
+
@Autowired
protected MediaNodeStatusManager mediaNodeStatusManager;
+ @Autowired
+ protected SessionEventsHandler sessionEventsHandler;
+
final protected Map kmss = new ConcurrentHashMap<>();
+ private SessionManager sessionManager;
+
+ public KmsManager(SessionManager sessionManager) {
+ this.sessionManager = sessionManager;
+ }
+
public synchronized void addKms(Kms kms) {
this.kmss.put(kms.getId(), kms);
}
@@ -121,6 +139,13 @@ public abstract class KmsManager {
}
}
+ public synchronized boolean atLeastOneConnectedAndRunningKms() {
+ Optional optional = this.kmss.values().stream()
+ .filter(kms -> kms.isKurentoClientConnected() && mediaNodeStatusManager.isRunning(kms.getId()))
+ .findFirst();
+ return optional.isPresent();
+ }
+
public synchronized List getKmssSortedByLoad() {
List kmsLoads = getKmsLoads();
Collections.sort(kmsLoads);
@@ -131,11 +156,6 @@ public abstract class KmsManager {
return this.kmss.get(kmsId);
}
- public KmsLoad getKmsLoad(String kmsId) {
- Kms kms = this.kmss.get(kmsId);
- return new KmsLoad(kms, kms.getLoad());
- }
-
public Collection getKmss() {
return this.kmss.values();
}
@@ -154,55 +174,14 @@ public abstract class KmsManager {
return kmsLoads;
}
- protected KurentoConnectionListener generateKurentoConnectionListener(final String kmsId) {
- return new KurentoConnectionListener() {
+ protected JsonRpcWSConnectionListener generateKurentoConnectionListener(final String kmsId) {
+ return new JsonRpcWSConnectionListener() {
@Override
public void reconnected(boolean sameServer) {
-
final Kms kms = kmss.get(kmsId);
-
log.info("Kurento Client \"reconnected\" event for KMS {} (sameServer: {}) [{}]", kms.getUri(),
sameServer, kms.getKurentoClient().toString());
-
- kmsReconnectionLocks.putIfAbsent(kms.getId(), new ReentrantLock());
- boolean lockAcquired = false;
- try {
- if (kmsReconnectionLocks.get(kms.getId()).tryLock(5, TimeUnit.SECONDS)) {
- lockAcquired = true;
- if (kms.isKurentoClientConnected()) {
- // Timer task of disconnected event successfully executed
- log.warn(
- "Timer task already executed for reconnected Kurento Client [{}] to KMS with uri {}. Skipping event listener execution",
- kms.getKurentoClient().toString(), kms.getUri());
- return;
- }
- kms.setKurentoClientConnected(true);
- kms.setTimeOfKurentoClientConnection(System.currentTimeMillis());
- if (!sameServer) {
- // Different KMS. Reset sessions status (no Publisher or SUbscriber endpoints)
- log.warn("Kurento Client reconnected to a different KMS instance, with uri {}",
- kms.getUri());
- log.warn("Updating all webrtc endpoints for active sessions");
- final long timeOfKurentoDisconnection = kms.getTimeOfKurentoClientDisconnection();
- kms.getKurentoSessions().forEach(kSession -> {
- kSession.restartStatusInKurento(timeOfKurentoDisconnection);
- });
- } else {
- // Same KMS. We may infer that openvidu-server/KMS connection has been lost, but
- // not the clients/KMS connections
- log.warn("Kurento Client reconnected to same KMS {} with uri {}", kmsId, kms.getUri());
- }
- kms.setTimeOfKurentoClientDisconnection(0);
- }
- } catch (InterruptedException e) {
- log.error("InterruptedException when waiting for lock on reconnected event of KMS with uri {}",
- kms.getUri());
- } finally {
- if (lockAcquired) {
- kmsReconnectionLocks.get(kms.getId()).unlock();
- }
- }
}
@Override
@@ -221,74 +200,67 @@ public abstract class KmsManager {
kms.getUri(), kms.getKurentoClient().toString());
}
- // TODO: this is a fix for the lack of reconnected event
- kmsReconnectionLocks.putIfAbsent(kms.getId(), new ReentrantLock());
- final UpdatableTimerTask[] TIMER = new UpdatableTimerTask[1];
- final AtomicInteger ITERATION = new AtomicInteger(0);
+ final int loops = 6;
+ final AtomicInteger iteration = new AtomicInteger(loops);
+ final long intervalWaitMs = 500L;
- TIMER[0] = new UpdatableTimerTask(() -> {
- boolean lockAcquired = false;
- try {
- if (kmsReconnectionLocks.get(kms.getId()).tryLock(5, TimeUnit.SECONDS)) {
- lockAcquired = true;
+ kurentoReconnectTimer = new UpdatableTimerTask(() -> {
+ if (iteration.decrementAndGet() < 0) {
- if (kms.isKurentoClientConnected()) {
- // reconnected listener already executed
- log.info(
- "Timer of KMS with uri {} and KurentoClient [{}] cancelled (reconnected event received during interval wait)",
- kms.getUri(), kms.getKurentoClient().toString());
- TIMER[0].cancelTimer();
- return;
- }
+ log.error("KurentoClient [{}] could not reconnect to KMS with uri {} in {} seconds",
+ kms.getKurentoClient().toString(), kms.getUri(), (intervalWaitMs * 6 / 1000));
+ kurentoReconnectTimer.cancelTimer();
+ log.warn("Closing {} sessions hosted by KMS with uri {}: {}", kms.getKurentoSessions().size(),
+ kms.getUri(), kms.getKurentoSessions().stream().map(s -> s.getSessionId())
+ .collect(Collectors.joining(",", "[", "]")));
- if (kms.getKurentoClient().isClosed()) {
- log.info(
- "Timer of KMS with uri {} and KurentoClient [{}] has been closed. Cancelling Timer",
- kms.getUri(), kms.getKurentoClient().toString());
- TIMER[0].cancelTimer();
- return;
- }
+ final long timeOfKurentoDisconnection = kms.getTimeOfKurentoClientDisconnection();
+ sessionEventsHandler.onMediaServerCrashed(kms, timeOfKurentoDisconnection);
+ kms.getKurentoSessions().forEach(kSession -> {
+ sessionManager.closeSession(kSession.getSessionId(), EndReason.mediaServerCrashed);
+ });
+
+ } else {
+
+ try {
kms.getKurentoClient().getServerManager().getInfo();
- log.info("According to Timer KMS with uri {} and KurentoClient [{}] is now reconnected",
- kms.getUri(), kms.getKurentoClient().toString());
- TIMER[0].cancelTimer();
- kms.setKurentoClientConnected(true);
- kms.setTimeOfKurentoClientConnection(System.currentTimeMillis());
+ } catch (Exception e) {
+ log.error(
+ "According to Timer KMS with uri {} and KurentoClient [{}] is not reconnected yet. Exception {}",
+ kms.getUri(), kms.getKurentoClient().toString(), e.getClass().getName());
+ return;
+ }
- final long timeOfKurentoDisconnection = kms.getTimeOfKurentoClientDisconnection();
+ log.info("According to Timer KMS with uri {} and KurentoClient [{}] is now reconnected",
+ kms.getUri(), kms.getKurentoClient().toString());
+ kurentoReconnectTimer.cancelTimer();
+ kms.setKurentoClientConnected(true);
+ kms.setTimeOfKurentoClientConnection(System.currentTimeMillis());
- if (kms.getKurentoSessions().isEmpty()) {
- log.info("There were no sessions in the KMS with uri {}. Nothing must be done",
- kms.getUri());
+ final long timeOfKurentoDisconnection = kms.getTimeOfKurentoClientDisconnection();
+
+ if (kms.getKurentoSessions().isEmpty()) {
+ log.info("There were no sessions in the KMS with uri {}. Nothing must be done",
+ kms.getUri());
+ } else {
+ if (isNewKms(kms)) {
+ log.warn("KMS with URI {} is a new KMS process. Resetting {} sessions: {}",
+ kms.getUri(), kms.getKurentoSessions().size(), kms.getKurentoSessions().stream()
+ .map(s -> s.getSessionId()).collect(Collectors.joining(",", "[", "]")));
+ kms.getKurentoSessions().forEach(kSession -> {
+ kSession.restartStatusInKurento(timeOfKurentoDisconnection);
+ });
} else {
- if (isNewKms(kms)) {
- log.warn("KMS with URI {} is a new KMS process. Resetting {} sessions: {}",
- kms.getUri(), kms.getKurentoSessions().size(),
- kms.getKurentoSessions().stream().map(s -> s.getSessionId())
- .collect(Collectors.joining(",", "[", "]")));
- kms.getKurentoSessions().forEach(kSession -> {
- kSession.restartStatusInKurento(timeOfKurentoDisconnection);
- });
- } else {
- log.info("KMS with URI {} is the same process. Nothing must be done", kms.getUri());
- }
+ log.info("KMS with URI {} is the same process. Nothing must be done", kms.getUri());
}
+ }
- kms.setTimeOfKurentoClientDisconnection(0);
- }
- } catch (Exception e) {
- log.error(
- "According to Timer KMS with uri {} and KurentoClient [{}] is not reconnected yet. Exception {}",
- kms.getUri(), kms.getKurentoClient().toString(), e.getClass().getName());
- } finally {
- if (lockAcquired) {
- kmsReconnectionLocks.get(kms.getId()).unlock();
- }
+ kms.setTimeOfKurentoClientDisconnection(0);
}
- }, () -> new Long(dynamicReconnectLoopSeconds(ITERATION.getAndIncrement()) * 1000));
+ }, () -> intervalWaitMs); // Try 2 times per seconds
- TIMER[0].updateTimer();
+ kurentoReconnectTimer.updateTimer();
}
@Override
@@ -308,6 +280,11 @@ public abstract class KmsManager {
// kms.setKurentoClientConnected(true);
// kms.setTimeOfKurentoClientConnection(System.currentTimeMillis());
}
+
+ @Override
+ public void reconnecting() {
+ }
+
};
}
@@ -323,36 +300,23 @@ public abstract class KmsManager {
}
}
- private int dynamicReconnectLoopSeconds(int iteration) {
- // First 10 loops every second, next 20 loops ever 3s, the rest every 10s
- final int[][] intervals = { new int[] { 1, 10 }, new int[] { 3, 20 }, new int[] { 10, Integer.MAX_VALUE } };
-
- int accumulatedIntervals = 0;
- for (int i = 0; i < intervals.length - 1; i++) {
- if ((accumulatedIntervals + intervals[i][1]) > iteration) {
- // Interval found for current iteration
- return intervals[i][0];
- } else {
- // This iteration has already been surpassed
- accumulatedIntervals += intervals[i][1];
- }
- }
- // Return last interval
- return intervals[intervals.length - 1][0];
- }
-
public abstract List initializeKurentoClients(List kmsProperties, boolean disconnectUponFailure)
throws Exception;
- public LoadManager getLoadManager() {
- return this.loadManager;
- }
+ public abstract void incrementActiveRecordings(RecordingProperties recordingProperties, String recordingId,
+ Session session);
+
+ public abstract void decrementActiveRecordings(RecordingProperties recordingProperties, String recordingId,
+ Session session);
@PostConstruct
protected abstract void postConstructInitKurentoClients();
@PreDestroy
public void close() {
+ if (kurentoReconnectTimer != null) {
+ kurentoReconnectTimer.cancelTimer();
+ }
log.info("Closing all KurentoClients");
this.kmss.values().forEach(kms -> {
kms.getKurentoClient().destroy();
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/CompositeWrapper.java b/openvidu-server/src/main/java/io/openvidu/server/recording/CompositeWrapper.java
index d8946d86..1d66e9af 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/CompositeWrapper.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/CompositeWrapper.java
@@ -86,8 +86,8 @@ public class CompositeWrapper {
this.recorderEndpoint.record();
}
- public synchronized void stopCompositeRecording(CountDownLatch stopLatch, Long timeOfKmsDisconnection) {
- if (timeOfKmsDisconnection == 0) {
+ public synchronized void stopCompositeRecording(CountDownLatch stopLatch, Long kmsDisconnectionTime) {
+ if (kmsDisconnectionTime == null) {
this.recorderEndpoint.addStoppedListener(new EventListener() {
@Override
public void onEvent(StoppedEvent event) {
@@ -101,7 +101,7 @@ public class CompositeWrapper {
});
this.recorderEndpoint.stop();
} else {
- endTime = timeOfKmsDisconnection;
+ endTime = kmsDisconnectionTime;
stopLatch.countDown();
log.warn("Forcing composed audio-only recording stop after KMS restart in session {}",
this.session.getSessionId());
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingDownloader.java b/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingDownloader.java
index d62cb0ee..20ddbd0a 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingDownloader.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingDownloader.java
@@ -23,7 +23,7 @@ import java.util.Collection;
public class DummyRecordingDownloader implements RecordingDownloader {
@Override
- public void downloadRecording(Recording recording, Collection streamIds, Runnable callback)
+ public void downloadRecording(Recording recording, Collection wrappers, Runnable callback)
throws IOException {
// Just immediately run callback function
callback.run();
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingUploader.java b/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingUploader.java
new file mode 100644
index 00000000..828e10c1
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/DummyRecordingUploader.java
@@ -0,0 +1,20 @@
+package io.openvidu.server.recording;
+
+public class DummyRecordingUploader implements RecordingUploader {
+
+ @Override
+ public void uploadRecording(Recording recording, Runnable successCallback, Runnable errorCallback) {
+ // Just immediately run success callback function
+ successCallback.run();
+ }
+
+ @Override
+ public void storeAsUploadingRecording(String recording) {
+ }
+
+ @Override
+ public boolean isBeingUploaded(String recordingId) {
+ return false;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java
index 51577e2c..f6b7dc29 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java
@@ -17,13 +17,19 @@
package io.openvidu.server.recording;
+import org.apache.commons.lang3.StringUtils;
import org.kurento.client.RecorderEndpoint;
import com.google.gson.JsonObject;
+import io.openvidu.server.kurento.core.KurentoParticipant;
+import io.openvidu.server.recording.service.RecordingService;
+
public class RecorderEndpointWrapper {
private RecorderEndpoint recorder;
+ private KurentoParticipant kParticipant;
+ private String name;
private String connectionId;
private String recordingId;
private String streamId;
@@ -37,23 +43,57 @@ public class RecorderEndpointWrapper {
private long endTime;
private long size;
- public RecorderEndpointWrapper(RecorderEndpoint recorder, String connectionId, String recordingId, String streamId,
- String clientData, String serverData, boolean hasAudio, boolean hasVideo, String typeOfVideo) {
+ public RecorderEndpointWrapper(RecorderEndpoint recorder, KurentoParticipant kParticipant, String recordingId,
+ String name) {
+ this.name = name;
this.recorder = recorder;
- this.connectionId = connectionId;
+ this.kParticipant = kParticipant;
this.recordingId = recordingId;
- this.streamId = streamId;
- this.clientData = clientData;
- this.serverData = serverData;
- this.hasAudio = hasAudio;
- this.hasVideo = hasVideo;
- this.typeOfVideo = typeOfVideo;
+ this.connectionId = kParticipant.getParticipantPublicId();
+ this.streamId = kParticipant.getPublisherStreamId();
+ this.clientData = kParticipant.getClientMetadata();
+ this.serverData = kParticipant.getServerMetadata();
+ this.hasAudio = kParticipant.getPublisher().getMediaOptions().hasAudio();
+ this.hasVideo = kParticipant.getPublisher().getMediaOptions().hasVideo();
+ this.typeOfVideo = kParticipant.getPublisher().getMediaOptions().getTypeOfVideo();
+ }
+
+ public RecorderEndpointWrapper(JsonObject json) {
+ String nameAux = json.get("name").getAsString();
+ // If the name includes the extension, remove it
+ this.name = StringUtils.removeEnd(nameAux, RecordingService.INDIVIDUAL_RECORDING_EXTENSION);
+ this.connectionId = json.get("connectionId").getAsString();
+ this.streamId = json.get("streamId").getAsString();
+ this.clientData = (json.has("clientData") && !json.get("clientData").isJsonNull())
+ ? json.get("clientData").getAsString()
+ : null;
+ this.serverData = json.get("serverData").getAsString();
+ this.startTime = json.get("startTime").getAsLong();
+ this.endTime = json.get("endTime").getAsLong();
+ this.size = json.get("size").getAsLong();
+ this.hasAudio = json.get("hasAudio").getAsBoolean();
+ this.hasVideo = json.get("hasVideo").getAsBoolean();
+ if (this.hasVideo) {
+ this.typeOfVideo = json.get("typeOfVideo").getAsString();
+ }
}
public RecorderEndpoint getRecorder() {
return recorder;
}
+ public KurentoParticipant getParticipant() {
+ return this.kParticipant;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getNameWithExtension() {
+ return this.name + RecordingService.INDIVIDUAL_RECORDING_EXTENSION;
+ }
+
public String getConnectionId() {
return connectionId;
}
@@ -112,18 +152,19 @@ public class RecorderEndpointWrapper {
public JsonObject toJson() {
JsonObject json = new JsonObject();
- json.addProperty("connectionId", this.connectionId);
- json.addProperty("streamId", this.streamId);
- json.addProperty("clientData", this.clientData);
- json.addProperty("serverData", this.serverData);
- json.addProperty("startTime", this.startTime);
- json.addProperty("endTime", this.endTime);
- json.addProperty("duration", this.endTime - this.startTime);
- json.addProperty("size", this.size);
- json.addProperty("hasAudio", this.hasAudio);
- json.addProperty("hasVideo", this.hasVideo);
- if (this.hasVideo) {
- json.addProperty("typeOfVideo", this.typeOfVideo);
+ json.addProperty("name", this.getNameWithExtension());
+ json.addProperty("connectionId", this.getConnectionId());
+ json.addProperty("streamId", this.getStreamId());
+ json.addProperty("clientData", this.getClientData());
+ json.addProperty("serverData", this.getServerData());
+ json.addProperty("startTime", this.getStartTime());
+ json.addProperty("endTime", this.getEndTime());
+ json.addProperty("duration", this.getEndTime() - this.getStartTime());
+ json.addProperty("size", this.getSize());
+ json.addProperty("hasAudio", this.hasAudio());
+ json.addProperty("hasVideo", this.hasVideo());
+ if (this.hasVideo()) {
+ json.addProperty("typeOfVideo", this.getTypeOfVideo());
}
return json;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java
index bcaf216a..e35805ee 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java
@@ -61,7 +61,7 @@ public class Recording {
try {
this.duration = json.get("duration").getAsDouble();
} catch (Exception e) {
- this.duration = new Long((long) json.get("duration").getAsLong()).doubleValue();
+ this.duration = Long.valueOf((long) json.get("duration").getAsLong()).doubleValue();
}
if (json.get("url").isJsonNull()) {
this.url = null;
@@ -191,6 +191,7 @@ public class Recording {
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("id", this.id);
+ json.addProperty("object", "recording");
json.addProperty("name", this.recordingProperties.name());
json.addProperty("outputMode", this.getOutputMode().name());
if (RecordingUtils.IS_COMPOSED(this.recordingProperties.outputMode()) && this.hasVideo) {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingDownloader.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingDownloader.java
index 65c750b5..71d392c5 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingDownloader.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingDownloader.java
@@ -22,7 +22,7 @@ import java.util.Collection;
public interface RecordingDownloader {
- public void downloadRecording(Recording recording, Collection streamIds, Runnable callback)
+ public void downloadRecording(Recording recording, Collection wrappers, Runnable callback)
throws IOException;
public void cancelDownload(String recordingId);
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java
index f4e2cddd..ac303add 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java
@@ -50,7 +50,7 @@ public class RecordingInfoUtils {
throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is corrupted");
}
if (this.json.size() == 0) {
- // Recording metadata from ffprobe is an emtpy JSON
+ // Recording metadata from ffprobe is an empty JSON
throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is empty");
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingUploader.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingUploader.java
new file mode 100644
index 00000000..e88b8719
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingUploader.java
@@ -0,0 +1,11 @@
+package io.openvidu.server.recording;
+
+public interface RecordingUploader {
+
+ void uploadRecording(Recording recording, Runnable successCallback, Runnable errorCallback);
+
+ void storeAsUploadingRecording(String recording);
+
+ boolean isBeingUploaded(String recordingId);
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java
index 3f529772..c9dc6e6f 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java
@@ -1,216 +1,309 @@
package io.openvidu.server.recording.service;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
+
import io.openvidu.client.OpenViduException;
+import io.openvidu.client.OpenViduException.Code;
import io.openvidu.java.client.RecordingProperties;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.Session;
+import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.RecordingDownloader;
-import io.openvidu.server.utils.QuarantineKiller;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
+import io.openvidu.server.recording.RecordingUploader;
+import io.openvidu.server.utils.CustomFileManager;
+import io.openvidu.server.utils.DockerManager;
public class ComposedQuickStartRecordingService extends ComposedRecordingService {
- private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class);
+ private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class);
- public ComposedQuickStartRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader, OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
- super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
- }
+ public ComposedQuickStartRecordingService(RecordingManager recordingManager,
+ RecordingDownloader recordingDownloader, RecordingUploader recordingUploader, KmsManager kmsManager,
+ CustomFileManager fileManager, OpenviduConfig openviduConfig, CallDetailRecord cdr,
+ DockerManager dockerManager) {
+ super(recordingManager, recordingDownloader, recordingUploader, kmsManager, fileManager, openviduConfig, cdr,
+ dockerManager);
+ }
- public void stopRecordingContainer(Session session, EndReason reason) {
- log.info("Stopping COMPOSED_QUICK_START of session {}. Reason: {}",
- session.getSessionId(), RecordingManager.finalReason(reason));
+ public void stopRecordingContainer(Session session, EndReason reason) {
+ log.info("Stopping COMPOSED_QUICK_START of session {}. Reason: {}", session.getSessionId(),
+ RecordingManager.finalReason(reason));
- String containerId = this.sessionsContainers.get(session.getSessionId());
+ String containerId = this.sessionsContainers.get(session.getSessionId());
- if (containerId != null) {
- try {
- dockerManager.removeDockerContainer(containerId, true);
- } catch (Exception e) {
- log.error("Can't remove COMPOSED_QUICK_START recording container from session {}", session.getSessionId());
- }
+ if (containerId != null) {
+ try {
+ dockerManager.removeContainer(session.getMediaNodeId(), containerId, true);
+ } catch (Exception e) {
+ log.error("Can't remove COMPOSED_QUICK_START recording container from session {}",
+ session.getSessionId());
+ }
- containers.remove(containerId);
- sessionsContainers.remove(session.getSessionId());
- }
+ containers.remove(containerId);
+ sessionsContainers.remove(session.getSessionId());
+ }
- }
+ }
- @Override
- protected Recording startRecordingWithVideo(Session session, Recording recording, RecordingProperties properties)
- throws OpenViduException {
+ @Override
+ protected Recording startRecordingWithVideo(Session session, Recording recording, RecordingProperties properties)
+ throws OpenViduException {
- log.info("Starting COMPOSED_QUICK_START ({}) recording {} of session {}",
- properties.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId());
+ log.info("Starting COMPOSED_QUICK_START ({}) recording {} of session {}",
+ properties.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId());
- List envs = new ArrayList<>();
+ List envs = new ArrayList<>();
- envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug());
- envs.add("RESOLUTION=" + properties.resolution());
- envs.add("ONLY_VIDEO=" + !properties.hasAudio());
- envs.add("FRAMERATE=30");
- envs.add("VIDEO_ID=" + recording.getId());
- envs.add("VIDEO_NAME=" + properties.name());
- envs.add("VIDEO_FORMAT=mp4");
- envs.add("RECORDING_JSON='" + recording.toJson().toString() + "'");
+ envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug());
+ envs.add("RESOLUTION=" + properties.resolution());
+ envs.add("ONLY_VIDEO=" + !properties.hasAudio());
+ envs.add("FRAMERATE=30");
+ envs.add("VIDEO_ID=" + recording.getId());
+ envs.add("VIDEO_NAME=" + properties.name());
+ envs.add("VIDEO_FORMAT=mp4");
+ envs.add("RECORDING_JSON='" + recording.toJson().toString() + "'");
- String containerId = this.sessionsContainers.get(session.getSessionId());
- try {
- String recordExecCommand = "";
- for(int i = 0; i < envs.size(); i++) {
- if (i > 0) {
- recordExecCommand += "&& ";
- }
- recordExecCommand += "export " + envs.get(i) + " ";
- }
- recordExecCommand += "&& ./composed_quick_start.sh --start-recording > /var/log/ffmpeg.log 2>&1 &";
- dockerManager.runCommandInContainer(containerId, recordExecCommand, 0);
- } catch (Exception e) {
- this.cleanRecordingMaps(recording);
- throw this.failStartRecording(session, recording,
- "Couldn't initialize recording container. Error: " + e.getMessage());
- }
+ String containerId = this.sessionsContainers.get(session.getSessionId());
+ try {
+ String recordExecCommand = "";
+ for (int i = 0; i < envs.size(); i++) {
+ if (i > 0) {
+ recordExecCommand += "&& ";
+ }
+ recordExecCommand += "export " + envs.get(i) + " ";
+ }
+ recordExecCommand += "&& ./composed_quick_start.sh --start-recording > /var/log/ffmpeg.log 2>&1 &";
+ dockerManager.runCommandInContainerAsync(recording.getRecordingProperties().mediaNode(), containerId,
+ recordExecCommand);
+ } catch (Exception e) {
+ this.cleanRecordingMaps(recording);
+ throw this.failStartRecording(session, recording,
+ "Couldn't initialize recording container. Error: " + e.getMessage());
+ }
- this.sessionsContainers.put(session.getSessionId(), containerId);
+ this.sessionsContainers.put(session.getSessionId(), containerId);
- try {
- this.waitForVideoFileNotEmpty(recording);
- } catch (OpenViduException e) {
- this.cleanRecordingMaps(recording);
- throw this.failStartRecording(session, recording,
- "Couldn't initialize recording container. Error: " + e.getMessage());
- }
+ try {
+ this.waitForVideoFileNotEmpty(recording);
+ } catch (Exception e) {
+ this.cleanRecordingMaps(recording);
+ throw this.failStartRecording(session, recording,
+ "Couldn't initialize recording container. Error: " + e.getMessage());
+ }
- return recording;
- }
+ if (this.openviduConfig.isRecordingComposedExternal()) {
+ this.generateRecordingMetadataFile(recording);
+ }
- @Override
- protected Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason) {
- log.info("Stopping COMPOSED_QUICK_START ({}) recording {} of session {}. Reason: {}",
- recording.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId(),
- RecordingManager.finalReason(reason));
- log.info("Container for session {} still being ready for new recordings", session.getSessionId());
+ return recording;
+ }
- String containerId = this.sessionsContainers.get(recording.getSessionId());
+ @Override
+ protected Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason) {
+ log.info("Stopping COMPOSED_QUICK_START ({}) recording {} of session {}. Reason: {}",
+ recording.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId(),
+ RecordingManager.finalReason(reason));
+ log.info("Container for session {} still ready for new recordings", recording.getSessionId());
- if (session == null) {
- log.warn(
- "Existing recording {} does not have an active session associated. This usually means a custom recording"
- + " layout did not join a recorded participant or the recording has been automatically"
- + " stopped after last user left and timeout passed",
- recording.getId());
- }
+ String containerId = this.sessionsContainers.get(recording.getSessionId());
- try {
- dockerManager.runCommandInContainer(containerId, "./composed_quick_start.sh --stop-recording", 10);
- } catch (InterruptedException e1) {
- cleanRecordingMaps(recording);
- log.error("Error stopping recording for session id: {}", session.getSessionId());
- e1.printStackTrace();
- }
+ if (session == null) {
+ log.warn(
+ "Existing recording {} does not have an active session associated. This usually means a custom recording"
+ + " layout did not join a recorded participant or the recording has been automatically"
+ + " stopped after last user left and timeout passed",
+ recording.getId());
+ }
- recording = updateRecordingAttributes(recording);
+ try {
+ dockerManager.runCommandInContainerSync(recording.getRecordingProperties().mediaNode(), containerId,
+ "./composed_quick_start.sh --stop-recording", 10);
+ } catch (IOException e1) {
+ log.error("Error stopping COMPOSED_QUICK_START recording {}: {}", recording.getId(), e1.getMessage());
+ failRecordingCompletion(recording, containerId, true,
+ new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE, e1.getMessage()));
+ }
- final String folderPath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/";
- final String metadataFilePath = folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
- this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
- metadataFilePath);
- cleanRecordingMaps(recording);
+ if (this.openviduConfig.isRecordingComposedExternal()) {
+ try {
+ waitForComposedQuickStartFiles(recording);
+ } catch (Exception e) {
+ failRecordingCompletion(recording, containerId, false,
+ new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE, e.getMessage()));
+ }
+ }
- final long timestamp = System.currentTimeMillis();
- this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
+ if (session != null && reason != null) {
+ this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
+ }
- if (session != null && reason != null) {
- this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
- }
+ downloadComposedRecording(session, recording, reason);
- // Decrement active recordings
- // ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
+ return recording;
+ }
- return recording;
- }
+ public void runComposedQuickStartContainer(Session session) {
+ // Start recording container if output mode=COMPOSED_QUICK_START
+ Session recorderSession = session;
+ io.openvidu.java.client.Recording.OutputMode defaultOutputMode = recorderSession.getSessionProperties()
+ .defaultOutputMode();
+ if (io.openvidu.java.client.Recording.OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode)
+ && sessionsContainers.get(recorderSession.getSessionId()) == null) {
+ // Retry to run if container is launched for the same session quickly after
+ // close it
+ int secondsToRetry = 10;
+ int secondsBetweenRetries = 1;
+ int seconds = 0;
+ boolean launched = false;
+ while (!launched && seconds < secondsToRetry) {
+ try {
+ log.info("Launching COMPOSED_QUICK_START recording container for session: {}",
+ recorderSession.getSessionId());
+ runContainer(recorderSession, new RecordingProperties.Builder().name("")
+ .outputMode(recorderSession.getSessionProperties().defaultOutputMode())
+ .recordingLayout(recorderSession.getSessionProperties().defaultRecordingLayout())
+ .customLayout(recorderSession.getSessionProperties().defaultCustomLayout())
+ .resolution(
+ /* recorderSession.getSessionProperties().defaultRecordingResolution() */"1920x1080")
+ .mediaNode(recorderSession.getMediaNodeId()).build());
+ log.info("COMPOSED_QUICK_START recording container launched for session: {}",
+ recorderSession.getSessionId());
+ launched = true;
+ } catch (Exception e) {
+ log.warn(
+ "Failed to launch COMPOSED_QUICK_START recording container for session {}. Trying again in {} seconds",
+ recorderSession.getSessionId(), secondsBetweenRetries);
+ try {
+ Thread.sleep(secondsBetweenRetries * 1000);
+ } catch (InterruptedException e2) {
+ }
+ seconds++;
+ } finally {
+ if (seconds == secondsToRetry && !launched) {
+ log.error("Error launchaing COMPOSED_QUICK_ªSTART recording container for session {}",
+ recorderSession.getSessionId());
+ }
+ }
+ }
+ }
+ }
- public void runComposedQuickStartContainer(Session session) {
- // Start recording container if output mode=COMPOSED_QUICK_START
- Session recorderSession = session;
- io.openvidu.java.client.Recording.OutputMode defaultOutputMode = recorderSession.getSessionProperties().defaultOutputMode();
- if (io.openvidu.java.client.Recording.OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode)
- && sessionsContainers.get(recorderSession.getSessionId()) == null) {
- // Retry to run if container is launched for the same session quickly after close it
- int secondsToRetry = 10;
- int secondsBetweenRetries = 1;
- int seconds = 0;
- boolean launched = false;
- while (!launched && seconds < secondsToRetry) {
- try {
- log.info("Launching COMPOSED_QUICK_START recording container for session: {}", recorderSession.getSessionId());
- runContainer(recorderSession, new RecordingProperties.Builder().name("")
- .outputMode(recorderSession.getSessionProperties().defaultOutputMode())
- .recordingLayout(recorderSession.getSessionProperties().defaultRecordingLayout())
- .customLayout(recorderSession.getSessionProperties().defaultCustomLayout()).build());
- log.info("COMPOSED_QUICK_START recording container launched for session: {}", recorderSession.getSessionId());
- launched = true;
- } catch (Exception e) {
- log.warn("Failed to launch COMPOSED_QUICK_START recording container for session {}. Trying again in {} seconds", recorderSession.getSessionId(), secondsBetweenRetries);
- try {
- Thread.sleep(secondsBetweenRetries * 1000);
- } catch (InterruptedException e2) {}
- seconds++;
- } finally {
- if (seconds == secondsToRetry && !launched) {
- log.error("Error launchaing COMPOSED_QUICK_ªSTART recording container for session {}", recorderSession.getSessionId());
- }
- }
- }
- }
- }
+ private String runContainer(Session session, RecordingProperties properties) throws Exception {
+ log.info("Starting COMPOSED_QUICK_START container for session id: {}", session.getSessionId());
- private String runContainer(Session session, RecordingProperties properties) throws Exception {
- log.info("Starting COMPOSED_QUICK_START container for session id: {}", session.getSessionId());
+ Recording recording = new Recording(session.getSessionId(), null, properties);
+ String layoutUrl = this.getLayoutUrl(recording);
- Recording recording = new Recording(session.getSessionId(), null, properties);
- String layoutUrl = this.getLayoutUrl(recording);
+ List envs = new ArrayList<>();
+ envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug());
+ envs.add("RECORDING_TYPE=COMPOSED_QUICK_START");
+ envs.add("RESOLUTION=" + properties.resolution());
+ envs.add("URL=" + layoutUrl);
- List envs = new ArrayList<>();
- envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug());
- envs.add("RECORDING_TYPE=COMPOSED_QUICK_START");
- envs.add("RESOLUTION=" + properties.resolution());
- envs.add("URL=" + layoutUrl);
+ log.info("Recorder connecting to url {}", layoutUrl);
- log.info("Recorder connecting to url {}", layoutUrl);
+ String containerId = null;
+ try {
+ final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion();
+ final String containerName = "recording_" + session.getSessionId();
+ Volume volume1 = new Volume("/recordings");
+ List volumes = new ArrayList<>();
+ volumes.add(volume1);
+ Bind bind1 = new Bind(openviduConfig.getOpenViduRecordingPath(), volume1);
+ List binds = new ArrayList<>();
+ binds.add(bind1);
+ containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
+ binds, "host", envs, null, properties.shmSize(), false, null);
+ containers.put(containerId, containerName);
+ this.sessionsContainers.put(session.getSessionId(), containerId);
+ } catch (Exception e) {
+ if (containerId != null) {
+ dockerManager.removeContainer(properties.mediaNode(), containerId, true);
+ containers.remove(containerId);
+ sessionsContainers.remove(session.getSessionId());
+ }
+ log.error("Error while launching container for COMPOSED_QUICK_START: ({})", e.getMessage());
+ throw e;
+ }
+ return containerId;
+ }
- String containerId = null;
- try {
- final String container = RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG;
- final String containerName = "recording_" + session.getSessionId();
- Volume volume1 = new Volume("/recordings");
- List volumes = new ArrayList<>();
- volumes.add(volume1);
- Bind bind1 = new Bind(openviduConfig.getOpenViduRecordingPath(), volume1);
- List binds = new ArrayList<>();
- binds.add(bind1);
- containerId = dockerManager.runContainer(container, containerName, null, volumes, binds, "host", envs, null,
- properties.shmSize(), false, null);
- containers.put(containerId, containerName);
- this.sessionsContainers.put(session.getSessionId(), containerId);
- } catch (Exception e) {
- if (containerId != null) {
- dockerManager.removeDockerContainer(containerId, true);
- containers.remove(containerId);
- sessionsContainers.remove(session.getSessionId());
- }
- log.error("Error while launchig container for COMPOSED_QUICK_START: ({})", e.getMessage());
- throw e;
- }
- return containerId;
- }
+ private void waitForComposedQuickStartFiles(Recording recording) throws Exception {
+
+ final int SECONDS_MAX_WAIT = fileManager.maxSecondsWaitForFile();
+ final String PATH = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/";
+
+ // Waiting for the files generated at the end of the stopping process: the
+ // ffprobe info and the thumbnail
+ final String INFO_FILE = PATH + recording.getId() + RecordingService.COMPOSED_INFO_FILE_EXTENSION;
+ final String THUMBNAIL_FILE = PATH + recording.getId() + RecordingService.COMPOSED_THUMBNAIL_EXTENSION;
+
+ Set filesToWaitFor = Stream.of(INFO_FILE, THUMBNAIL_FILE).collect(Collectors.toSet());
+
+ Collection waitForFileThreads = new HashSet<>();
+ CountDownLatch latch = new CountDownLatch(filesToWaitFor.size());
+
+ for (final String file : filesToWaitFor) {
+ Thread downloadThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ fileManager.waitForFileToExistAndNotEmpty(recording.getRecordingProperties().mediaNode(), file);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
+ } finally {
+ latch.countDown();
+ }
+ }
+ };
+ waitForFileThreads.add(downloadThread);
+ }
+ waitForFileThreads.forEach(t -> t.start());
+
+ try {
+ if (!latch.await(SECONDS_MAX_WAIT, TimeUnit.SECONDS)) {
+ recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
+ String msg = "The wait for files of COMPOSED_QUICK_START recording " + recording.getId()
+ + " didn't complete in " + fileManager.maxSecondsWaitForFile() + " seconds";
+ log.error(msg);
+ throw new Exception(msg);
+ } else {
+ if (io.openvidu.java.client.Recording.Status.failed.equals(recording.getStatus())) {
+ String msg = "Error waiting for some COMPOSED_QUICK_START recording file in recording "
+ + recording.getId();
+ log.error(msg);
+ throw new Exception(msg);
+ } else {
+ log.info("All files of COMPOSED_QUICK_START recording {} are available to download",
+ recording.getId());
+ }
+ }
+ } catch (InterruptedException e) {
+ recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
+ String msg = "Error waiting for COMPOSED_QUICK_START recording files of recording " + recording.getId()
+ + ": " + e.getMessage();
+ log.error(msg);
+ throw new Exception(msg);
+ }
+ }
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java
index a645e6d2..27922ea4 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java
@@ -52,12 +52,15 @@ import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.kurento.core.KurentoSession;
+import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.recording.CompositeWrapper;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.RecordingDownloader;
import io.openvidu.server.recording.RecordingInfoUtils;
+import io.openvidu.server.recording.RecordingUploader;
+import io.openvidu.server.rest.RequestMappings;
+import io.openvidu.server.utils.CustomFileManager;
import io.openvidu.server.utils.DockerManager;
-import io.openvidu.server.utils.QuarantineKiller;
public class ComposedRecordingService extends RecordingService {
@@ -70,18 +73,15 @@ public class ComposedRecordingService extends RecordingService {
protected DockerManager dockerManager;
public ComposedRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
- OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
- super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
- this.dockerManager = new DockerManager();
+ RecordingUploader recordingUploader, KmsManager kmsManager, CustomFileManager fileManager,
+ OpenviduConfig openviduConfig, CallDetailRecord cdr, DockerManager dockerManager) {
+ super(recordingManager, recordingDownloader, recordingUploader, kmsManager, fileManager, openviduConfig, cdr);
+ this.dockerManager = dockerManager;
}
@Override
- public Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException {
-
- PropertiesRecordingId updatePropertiesAndRecordingId = this.setFinalRecordingNameAndGetFreeRecordingId(session,
- properties);
- properties = updatePropertiesAndRecordingId.properties;
- String recordingId = updatePropertiesAndRecordingId.recordingId;
+ public Recording startRecording(Session session, String recordingId, RecordingProperties properties)
+ throws OpenViduException {
// Instantiate and store recording object
Recording recording = new Recording(session.getSessionId(), recordingId, properties);
@@ -95,23 +95,19 @@ public class ComposedRecordingService extends RecordingService {
recording = this.startRecordingAudioOnly(session, recording, properties);
}
- // Increment active recordings
- // ((KurentoSession) session).getKms().getActiveRecordings().incrementAndGet();
-
return recording;
}
@Override
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
- recording = this.sealRecordingMetadataFileAsStopped(recording);
if (recording.hasVideo()) {
return this.stopRecordingWithVideo(session, recording, reason);
} else {
- return this.stopRecordingAudioOnly(session, recording, reason, 0);
+ return this.stopRecordingAudioOnly(session, recording, reason, null);
}
}
- public Recording stopRecording(Session session, Recording recording, EndReason reason, long kmsDisconnectionTime) {
+ public Recording stopRecording(Session session, Recording recording, EndReason reason, Long kmsDisconnectionTime) {
if (recording.hasVideo()) {
return this.stopRecordingWithVideo(session, recording, reason);
} else {
@@ -146,7 +142,7 @@ public class ComposedRecordingService extends RecordingService {
throws OpenViduException {
log.info("Starting composed ({}) recording {} of session {}",
- properties.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId());
+ properties.hasAudio() ? "video + audio" : "video-only", recording.getId(), recording.getSessionId());
List envs = new ArrayList<>();
@@ -167,7 +163,7 @@ public class ComposedRecordingService extends RecordingService {
String containerId;
try {
- final String container = RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG;
+ final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion();
final String containerName = "recording_" + recording.getId();
Volume volume1 = new Volume("/recordings");
List volumes = new ArrayList<>();
@@ -175,8 +171,8 @@ public class ComposedRecordingService extends RecordingService {
Bind bind1 = new Bind(openviduConfig.getOpenViduRecordingPath(), volume1);
List binds = new ArrayList<>();
binds.add(bind1);
- containerId = dockerManager.runContainer(container, containerName, null, volumes, binds, "host", envs, null,
- properties.shmSize(), false, null);
+ containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
+ binds, "host", envs, null, properties.shmSize(), false, null);
containers.put(containerId, containerName);
} catch (Exception e) {
this.cleanRecordingMaps(recording);
@@ -188,12 +184,16 @@ public class ComposedRecordingService extends RecordingService {
try {
this.waitForVideoFileNotEmpty(recording);
- } catch (OpenViduException e) {
+ } catch (Exception e) {
this.cleanRecordingMaps(recording);
throw this.failStartRecording(session, recording,
"Couldn't initialize recording container. Error: " + e.getMessage());
}
+ if (this.openviduConfig.isRecordingComposedExternal()) {
+ this.generateRecordingMetadataFile(recording);
+ }
+
return recording;
}
@@ -222,16 +222,13 @@ public class ComposedRecordingService extends RecordingService {
this.generateRecordingMetadataFile(recording);
- // Increment active recordings
- ((KurentoSession) session).getKms().getActiveRecordings().incrementAndGet();
-
return recording;
}
protected Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason) {
log.info("Stopping composed ({}) recording {} of session {}. Reason: {}",
- recording.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId(),
+ recording.hasAudio() ? "video + audio" : "video-only", recording.getId(), recording.getSessionId(),
RecordingManager.finalReason(reason));
String containerId = this.sessionsContainers.remove(recording.getSessionId());
@@ -271,7 +268,8 @@ public class ComposedRecordingService extends RecordingService {
} else {
log.warn("Removing container {} for closed session {}...", containerIdAux,
session.getSessionId());
- dockerManager.removeDockerContainer(containerIdAux, true);
+ dockerManager.removeContainer(recordingAux.getRecordingProperties().mediaNode(),
+ containerIdAux, true);
containers.remove(containerId);
containerClosed = true;
log.warn("Container {} for closed session {} succesfully stopped and removed",
@@ -279,46 +277,39 @@ public class ComposedRecordingService extends RecordingService {
log.warn("Deleting unusable files for recording {}", recordingId);
if (HttpStatus.NO_CONTENT
.equals(this.recordingManager.deleteRecordingFromHost(recordingId, true))) {
- log.warn("Files properly deleted");
+ log.warn("Files properly deleted for recording {}", recordingId);
+ } else {
+ log.warn("No files found for recording {}", recordingId);
}
}
}
cleanRecordingMaps(recordingAux);
+
+ // Decrement active recordings
+ kmsManager.decrementActiveRecordings(recordingAux.getRecordingProperties(), recordingId, session);
+
if (i == timeout) {
log.error("Container did not launched in {} seconds", timeout / 2);
return;
}
- // Decrement active recordings
- // ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
}).start();
}
} else {
stopAndRemoveRecordingContainer(recording, containerId, 30);
- recording = updateRecordingAttributes(recording);
-
- final String folderPath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/";
- final String metadataFilePath = folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
- this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
- metadataFilePath);
- cleanRecordingMaps(recording);
-
- final long timestamp = System.currentTimeMillis();
- this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
if (session != null && reason != null) {
this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
}
- // Decrement active recordings
- // ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
+ downloadComposedRecording(session, recording, reason);
}
return recording;
}
private Recording stopRecordingAudioOnly(Session session, Recording recording, EndReason reason,
- long kmsDisconnectionTime) {
+ Long kmsDisconnectionTime) {
log.info("Stopping composed (audio-only) recording {} of session {}. Reason: {}", recording.getId(),
recording.getSessionId(), reason);
@@ -357,6 +348,7 @@ public class ComposedRecordingService extends RecordingService {
finalRecordingArray[0] = recording;
try {
this.recordingDownloader.downloadRecording(finalRecordingArray[0], null, () -> {
+
String filesPath = this.openviduConfig.getOpenViduRecordingPath() + finalRecordingArray[0].getId()
+ "/";
File videoFile = new File(filesPath + finalRecordingArray[0].getName() + ".webm");
@@ -365,17 +357,16 @@ public class ComposedRecordingService extends RecordingService {
this.updateFilePermissions(filesPath);
finalRecordingArray[0] = this.sealRecordingMetadataFileAsReady(finalRecordingArray[0], finalSize,
finalDuration,
- filesPath + RecordingManager.RECORDING_ENTITY_FILE + finalRecordingArray[0].getId());
+ filesPath + RecordingService.RECORDING_ENTITY_FILE + finalRecordingArray[0].getId());
- final long timestamp = System.currentTimeMillis();
- cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
- finalRecordingArray[0].getStatus());
+ // Decrement active recordings once it is downloaded. This method will also drop
+ // the Media Node if no more sessions or recordings and status is
+ // waiting-idle-to-terminate
+ kmsManager.decrementActiveRecordings(finalRecordingArray[0].getRecordingProperties(),
+ finalRecordingArray[0].getId(), session);
- // Decrement active recordings once it is downloaded
- ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
-
- // Now we can drop Media Node if waiting-idle-to-terminate
- this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
+ // Upload if necessary
+ this.uploadRecording(finalRecordingArray[0], reason);
});
} catch (IOException e) {
@@ -390,32 +381,35 @@ public class ComposedRecordingService extends RecordingService {
return finalRecordingArray[0];
}
- protected void stopAndRemoveRecordingContainer(Recording recording, String containerId, int secondsOfWait) {
+ private void stopAndRemoveRecordingContainer(Recording recording, String containerId, int secondsOfWait) {
// Gracefully stop ffmpeg process
try {
- dockerManager.runCommandInContainer(containerId, "echo 'q' > stop", 0);
- } catch (InterruptedException e1) {
+ dockerManager.runCommandInContainerAsync(recording.getRecordingProperties().mediaNode(), containerId,
+ "echo 'q' > stop");
+ } catch (IOException e1) {
e1.printStackTrace();
}
// Wait for the container to be gracefully self-stopped
final int timeOfWait = 30;
try {
- dockerManager.waitForContainerStopped(containerId, timeOfWait);
+ dockerManager.waitForContainerStopped(recording.getRecordingProperties().mediaNode(), containerId,
+ timeOfWait);
} catch (Exception e) {
- failRecordingCompletion(recording, containerId, new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE,
- "The recording completion process couldn't finish in " + timeOfWait + " seconds"));
+ failRecordingCompletion(recording, containerId, true,
+ new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE,
+ "The recording completion process couldn't finish in " + timeOfWait + " seconds"));
}
// Remove container
- dockerManager.removeDockerContainer(containerId, false);
+ dockerManager.removeContainer(recording.getRecordingProperties().mediaNode(), containerId, false);
containers.remove(containerId);
}
- protected Recording updateRecordingAttributes(Recording recording) {
+ protected void updateRecordingAttributes(Recording recording) {
try {
RecordingInfoUtils infoUtils = new RecordingInfoUtils(this.openviduConfig.getOpenViduRecordingPath()
- + recording.getId() + "/" + recording.getId() + ".info");
+ + recording.getId() + "/" + recording.getId() + RecordingService.COMPOSED_INFO_FILE_EXTENSION);
if (!infoUtils.hasVideo()) {
log.error("COMPOSED recording {} with hasVideo=true has not video track", recording.getId());
@@ -429,7 +423,6 @@ public class ComposedRecordingService extends RecordingService {
recording.setHasVideo(infoUtils.hasVideo());
}
infoUtils.deleteFilePath();
- return recording;
} catch (IOException e) {
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
@@ -437,34 +430,22 @@ public class ComposedRecordingService extends RecordingService {
}
}
- protected void waitForVideoFileNotEmpty(Recording recording) throws OpenViduException {
- boolean isPresent = false;
- int i = 1;
- int timeout = 150; // Wait for 150*150 = 22500 = 22.5 seconds
- while (!isPresent && timeout <= 150) {
- try {
- Thread.sleep(150);
- timeout++;
- File f = new File(this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
- + recording.getName() + ".mp4");
- isPresent = ((f.isFile()) && (f.length() > 0));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- if (i == timeout) {
- log.error("Recorder container failed generating video file (is empty) for session {}",
- recording.getSessionId());
- throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
- "Recorder container failed generating video file (is empty)");
- }
+ protected void waitForVideoFileNotEmpty(Recording recording) throws Exception {
+ final String VIDEO_FILE = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
+ + recording.getName() + RecordingService.COMPOSED_RECORDING_EXTENSION;
+ this.fileManager.waitForFileToExistAndNotEmpty(recording.getRecordingProperties().mediaNode(), VIDEO_FILE);
}
- protected void failRecordingCompletion(Recording recording, String containerId, OpenViduException e)
- throws OpenViduException {
+ protected void failRecordingCompletion(Recording recording, String containerId, boolean removeContainer,
+ OpenViduException e) throws OpenViduException {
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
- dockerManager.removeDockerContainer(containerId, true);
- containers.remove(containerId);
+ if (removeContainer) {
+ dockerManager.removeContainer(recording.getRecordingProperties().mediaNode(), containerId, true);
+ containers.remove(containerId);
+ }
+ sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
+ getMetadataFilePath(recording));
+ cleanRecordingMaps(recording);
throw e;
}
@@ -519,6 +500,8 @@ public class ComposedRecordingService extends RecordingService {
}
String layout, finalUrl;
+ final String basicauth = openviduConfig.isOpenviduRecordingComposedBasicauth() ? ("OPENVIDUAPP:" + secret + "@")
+ : "";
if (RecordingLayout.CUSTOM.equals(recording.getRecordingLayout())) {
layout = recording.getCustomLayout();
if (!layout.isEmpty()) {
@@ -526,8 +509,9 @@ public class ComposedRecordingService extends RecordingService {
layout = layout.endsWith("/") ? layout.substring(0, layout.length() - 1) : layout;
}
layout += "/index.html";
- finalUrl = (startsWithHttp ? "http" : "https") + "://OPENVIDUAPP:" + secret + "@" + recordingUrl
- + "/layouts/custom" + layout + "?sessionId=" + recording.getSessionId() + "&secret=" + secret;
+ finalUrl = (startsWithHttp ? "http" : "https") + "://" + basicauth + recordingUrl
+ + RequestMappings.CUSTOM_LAYOUTS + layout + "?sessionId=" + recording.getSessionId() + "&secret="
+ + secret;
} else {
layout = recording.getRecordingLayout().name().toLowerCase().replaceAll("_", "-");
int port = startsWithHttp ? 80 : 443;
@@ -537,8 +521,8 @@ public class ComposedRecordingService extends RecordingService {
log.error(e.getMessage());
}
String defaultPathForDefaultLayout = recordingComposedUrlDefined ? ""
- : ("/" + openviduConfig.getOpenViduFrontendDefaultPath());
- finalUrl = (startsWithHttp ? "http" : "https") + "://OPENVIDUAPP:" + secret + "@" + recordingUrl
+ : (openviduConfig.getOpenViduFrontendDefaultPath());
+ finalUrl = (startsWithHttp ? "http" : "https") + "://" + basicauth + recordingUrl
+ defaultPathForDefaultLayout + "/#/layout-" + layout + "/" + recording.getSessionId() + "/"
+ secret + "/" + port + "/" + !recording.hasAudio();
}
@@ -595,4 +579,27 @@ public class ComposedRecordingService extends RecordingService {
return finalUrl;
}
+ protected void downloadComposedRecording(final Session session, final Recording recording, final EndReason reason) {
+ try {
+ this.recordingDownloader.downloadRecording(recording, null, () -> {
+
+ updateRecordingAttributes(recording);
+
+ this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
+ getMetadataFilePath(recording));
+ cleanRecordingMaps(recording);
+
+ // Decrement active recordings once it is downloaded. This method will also drop
+ // the Media Node if no more sessions or recordings and status is
+ // waiting-idle-to-terminate
+ kmsManager.decrementActiveRecordings(recording.getRecordingProperties(), recording.getId(), session);
+
+ // Upload if necessary
+ this.uploadRecording(recording, reason);
+ });
+ } catch (IOException e) {
+ log.error("Error while downloading recording {}: {}", recording.getName(), e.getMessage());
+ }
+ }
+
}
\ No newline at end of file
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java
index 41b901f7..20faad49 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java
@@ -67,15 +67,17 @@ import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionEventsHandler;
import io.openvidu.server.core.SessionManager;
-import io.openvidu.server.kurento.core.KurentoSession;
import io.openvidu.server.kurento.kms.Kms;
import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.RecordingDownloader;
+import io.openvidu.server.recording.RecordingUploader;
+import io.openvidu.server.recording.service.RecordingService.PropertiesRecordingId;
import io.openvidu.server.utils.CustomFileManager;
import io.openvidu.server.utils.DockerManager;
import io.openvidu.server.utils.JsonUtils;
-import io.openvidu.server.utils.QuarantineKiller;
+import io.openvidu.server.utils.LocalCustomFileManager;
+import io.openvidu.server.utils.LocalDockerManager;
import io.openvidu.server.utils.RecordingUtils;
public class RecordingManager {
@@ -86,6 +88,7 @@ public class RecordingManager {
private ComposedQuickStartRecordingService composedQuickStartRecordingService;
private SingleStreamRecordingService singleStreamRecordingService;
private DockerManager dockerManager;
+ private CustomFileManager fileManager;
@Autowired
protected SessionEventsHandler sessionHandler;
@@ -93,18 +96,21 @@ public class RecordingManager {
@Autowired
private SessionManager sessionManager;
+ @Autowired
+ protected RecordingManagerUtils recordingManagerUtils;
+
@Autowired
private RecordingDownloader recordingDownloader;
+ @Autowired
+ private RecordingUploader recordingUploader;
+
@Autowired
protected OpenviduConfig openviduConfig;
@Autowired
private KmsManager kmsManager;
- @Autowired
- protected QuarantineKiller quarantineKiller;
-
@Autowired
private CallDetailRecord cdr;
@@ -119,14 +125,17 @@ public class RecordingManager {
private ScheduledThreadPoolExecutor automaticRecordingStopExecutor = new ScheduledThreadPoolExecutor(
Runtime.getRuntime().availableProcessors());
- static final String RECORDING_ENTITY_FILE = ".recording.";
public static final String IMAGE_NAME = "openvidu/openvidu-recording";
- static String IMAGE_TAG;
private static final List LAST_PARTICIPANT_LEFT_REASONS = Arrays
.asList(new EndReason[] { EndReason.disconnect, EndReason.forceDisconnectByUser,
EndReason.forceDisconnectByServer, EndReason.networkDisconnect });
+ public RecordingManager(DockerManager dockerManager, CustomFileManager fileManager) {
+ this.dockerManager = dockerManager;
+ this.fileManager = fileManager;
+ }
+
@PostConstruct
public void init() {
if (this.openviduConfig.isRecordingModuleEnabled()) {
@@ -156,63 +165,33 @@ public class RecordingManager {
public void initializeRecordingManager() throws OpenViduException {
- RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
+ this.dockerManager.init();
- this.dockerManager = new DockerManager();
- this.composedRecordingService = new ComposedRecordingService(this, recordingDownloader, openviduConfig, cdr,
- quarantineKiller);
+ this.composedRecordingService = new ComposedRecordingService(this, recordingDownloader, recordingUploader,
+ kmsManager, fileManager, openviduConfig, cdr, this.dockerManager);
this.composedQuickStartRecordingService = new ComposedQuickStartRecordingService(this, recordingDownloader,
- openviduConfig, cdr, quarantineKiller);
- this.singleStreamRecordingService = new SingleStreamRecordingService(this, recordingDownloader, openviduConfig,
- cdr, quarantineKiller);
-
- log.info("Recording module required: Downloading openvidu/openvidu-recording:"
- + openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
+ recordingUploader, kmsManager, fileManager, openviduConfig, cdr, this.dockerManager);
+ this.singleStreamRecordingService = new SingleStreamRecordingService(this, recordingDownloader,
+ recordingUploader, kmsManager, fileManager, openviduConfig, cdr);
this.checkRecordingRequirements(this.openviduConfig.getOpenViduRecordingPath(),
this.openviduConfig.getOpenviduRecordingCustomLayout());
- if (dockerManager.dockerImageExistsLocally(IMAGE_NAME + ":" + IMAGE_TAG)) {
- log.info("Docker image already exists locally");
- } else {
- Thread t = new Thread(() -> {
- boolean keep = true;
- log.info("Downloading ");
- while (keep) {
- System.out.print(".");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- keep = false;
- log.info("\nDownload complete");
- }
- }
- });
- t.start();
- try {
- dockerManager.downloadDockerImage(IMAGE_NAME + ":" + IMAGE_TAG, 600);
- } catch (Exception e) {
- log.error("Error downloading docker image {}:{}", IMAGE_NAME, IMAGE_TAG);
- }
- t.interrupt();
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("Docker image available");
+ LocalDockerManager dockMng = new LocalDockerManager(true);
+
+ if (!openviduConfig.isRecordingComposedExternal()) {
+ downloadRecordingImageToLocal(dockMng);
}
// Clean any stranded openvidu/openvidu-recording container on startup
- dockerManager.cleanStrandedContainers(RecordingManager.IMAGE_NAME);
+ dockMng.cleanStrandedContainers(RecordingManager.IMAGE_NAME);
}
public void checkRecordingRequirements(String openviduRecordingPath, String openviduRecordingCustomLayout)
throws OpenViduException {
- if (dockerManager == null) {
- this.dockerManager = new DockerManager();
- }
+ LocalDockerManager dockerManager = null;
try {
+ dockerManager = new LocalDockerManager(true);
dockerManager.checkDockerEnabled();
} catch (OpenViduException e) {
String message = e.getMessage();
@@ -231,10 +210,49 @@ public class RecordingManager {
}
log.error(message);
throw e;
+ } finally {
+ dockerManager.close();
}
this.checkRecordingPaths(openviduRecordingPath, openviduRecordingCustomLayout);
}
+ private void downloadRecordingImageToLocal(LocalDockerManager dockMng) {
+ log.info("Recording module required: Downloading openvidu/openvidu-recording:"
+ + openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
+
+ if (dockMng.dockerImageExistsLocally(IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion())) {
+ log.info("Docker image already exists locally");
+ } else {
+ Thread t = new Thread(() -> {
+ boolean keep = true;
+ log.info("Downloading ");
+ while (keep) {
+ System.out.print(".");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ keep = false;
+ log.info("\nDownload complete");
+ }
+ }
+ });
+ t.start();
+ try {
+ dockMng.downloadDockerImage(IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion(), 600);
+ } catch (Exception e) {
+ log.error("Error downloading docker image {}:{}", IMAGE_NAME,
+ openviduConfig.getOpenViduRecordingVersion());
+ }
+ t.interrupt();
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ log.info("Docker image available");
+ }
+ }
+
public void startComposedQuickStartContainer(Session session) {
this.composedQuickStartRecordingService.runComposedQuickStartContainer(session);
}
@@ -244,61 +262,80 @@ public class RecordingManager {
}
public Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException {
+
+ String recordingId = null;
+
try {
- if (session.recordingLock.tryLock(15, TimeUnit.SECONDS)) {
- try {
- if (sessionIsBeingRecorded(session.getSessionId())) {
- throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
- "Concurrent start of recording for session " + session.getSessionId());
- } else {
- Recording recording = null;
- try {
+ PropertiesRecordingId updatePropertiesAndRecordingId = ((RecordingService) this.composedRecordingService)
+ .setFinalRecordingNameAndGetFreeRecordingId(session, properties);
+ properties = updatePropertiesAndRecordingId.properties;
+ recordingId = updatePropertiesAndRecordingId.recordingId;
+
+ // INCREMENT ACTIVE RECORDINGS OF MEDIA NODE HERE. IF MEDIA NODE IS NOT
+ // AVAILABLE FOR STARTING NEW RECORDINGS THIS METHOD THROWS AN EXCEPTION
+ kmsManager.incrementActiveRecordings(properties, recordingId, session);
+
+ try {
+ if (session.recordingLock.tryLock(15, TimeUnit.SECONDS)) {
+ try {
+ if (sessionIsBeingRecorded(session.getSessionId())) {
+ throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
+ "Concurrent start of recording for session " + session.getSessionId());
+ } else {
+ Recording recording = null;
switch (properties.outputMode()) {
case COMPOSED:
- recording = this.composedRecordingService.startRecording(session, properties);
+ recording = this.composedRecordingService.startRecording(session, recordingId,
+ properties);
break;
case COMPOSED_QUICK_START:
- recording = this.composedQuickStartRecordingService.startRecording(session, properties);
+ recording = this.composedQuickStartRecordingService.startRecording(session, recordingId,
+ properties);
break;
case INDIVIDUAL:
- recording = this.singleStreamRecordingService.startRecording(session, properties);
+ recording = this.singleStreamRecordingService.startRecording(session, recordingId,
+ properties);
break;
}
- } catch (Exception e) {
- throw e;
- }
- this.recordingFromStartingToStarted(recording);
+ this.recordingFromStartingToStarted(recording);
- this.cdr.recordRecordingStarted(recording);
- this.cdr.recordRecordingStatusChanged(recording, null, recording.getCreatedAt(),
- Status.started);
+ this.cdr.recordRecordingStarted(recording);
+ this.cdr.recordRecordingStatusChanged(recording, null, recording.getCreatedAt(),
+ Status.started);
- if (!(OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo())) {
- // Directly send recording started notification for all cases except for
- // COMPOSED recordings with video (will be sent on first RECORDER subscriber)
- this.sessionHandler.sendRecordingStartedNotification(session, recording);
+ if (!(OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo())) {
+ // Directly send recording started notification for all cases except for
+ // COMPOSED recordings with video (will be sent on first RECORDER subscriber)
+ // Both INDIVIDUAL and COMPOSED_QUICK_START should notify immediately
+ this.sessionHandler.sendRecordingStartedNotification(session, recording);
+ }
+ if (session.getActivePublishers() == 0) {
+ // Init automatic recording stop if no publishers when starting the recording
+ log.info(
+ "No publisher in session {}. Starting {} seconds countdown for stopping recording",
+ session.getSessionId(),
+ this.openviduConfig.getOpenviduRecordingAutostopTimeout());
+ this.initAutomaticRecordingStopThread(session);
+ }
+ return recording;
}
- if (session.getActivePublishers() == 0) {
- // Init automatic recording stop if there are now publishers when starting
- // recording
- log.info("No publisher in session {}. Starting {} seconds countdown for stopping recording",
- session.getSessionId(), this.openviduConfig.getOpenviduRecordingAutostopTimeout());
- this.initAutomaticRecordingStopThread(session);
- }
- return recording;
+ } finally {
+ session.recordingLock.unlock();
}
- } finally {
- session.recordingLock.unlock();
+ } else {
+ throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
+ "Timeout waiting for recording Session lock to be available for session "
+ + session.getSessionId());
}
- } else {
+ } catch (InterruptedException e) {
throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
- "Timeout waiting for recording Session lock to be available for session "
+ "InterruptedException waiting for recording Session lock to be available for session "
+ session.getSessionId());
}
- } catch (InterruptedException e) {
- throw new OpenViduException(Code.RECORDING_START_ERROR_CODE,
- "InterruptedException waiting for recording Session lock to be available for session "
- + session.getSessionId());
+ } catch (Exception e) {
+ // DECREMENT ACTIVE RECORDINGS OF MEDIA NODE AND TRY REMOVE MEDIA NODE HERE
+ kmsManager.decrementActiveRecordings(properties, recordingId, session);
+ throw e;
}
}
@@ -310,6 +347,20 @@ public class RecordingManager {
recording = this.sessionsRecordings.get(session.getSessionId());
}
+ if (recording == null) {
+ recording = this.sessionsRecordingsStarting.get(session.getSessionId());
+ if (recording == null) {
+ log.error("Cannot stop recording. Session {} is not being recorded", recordingId,
+ session.getSessionId());
+ return null;
+ } else {
+ // Recording is still starting
+ log.warn("Recording {} is still in \"starting\" status", recording.getId());
+ }
+ }
+
+ ((RecordingService) singleStreamRecordingService).sealRecordingMetadataFileAsStopped(recording);
+
final long timestamp = System.currentTimeMillis();
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, Status.stopped);
cdr.recordRecordingStopped(recording, reason, timestamp);
@@ -329,7 +380,7 @@ public class RecordingManager {
return recording;
}
- public Recording forceStopRecording(Session session, EndReason reason, long kmsDisconnectionTime) {
+ public Recording forceStopRecording(Session session, EndReason reason, Long kmsDisconnectionTime) {
Recording recording;
recording = this.sessionsRecordings.get(session.getSessionId());
switch (recording.getOutputMode()) {
@@ -361,8 +412,7 @@ public class RecordingManager {
return recording;
}
- public void startOneIndividualStreamRecording(Session session, String recordingId, MediaProfileSpecType profile,
- Participant participant) {
+ public void startOneIndividualStreamRecording(Session session, Participant participant) {
Recording recording = this.sessionsRecordings.get(session.getSessionId());
if (recording == null) {
recording = this.sessionsRecordingsStarting.get(session.getSessionId());
@@ -377,28 +427,46 @@ public class RecordingManager {
log.info("Starting new RecorderEndpoint in session {} for new stream of participant {}",
session.getSessionId(), participant.getParticipantPublicId());
final CountDownLatch startedCountDown = new CountDownLatch(1);
- this.singleStreamRecordingService.startRecorderEndpointForPublisherEndpoint(session, recordingId, profile,
+
+ MediaProfileSpecType profile = null;
+ try {
+ profile = this.singleStreamRecordingService.generateMediaProfile(recording.getRecordingProperties(),
+ participant);
+ } catch (OpenViduException e) {
+ log.error("Cannot start single stream recorder for stream {} in session {}: {}",
+ participant.getPublisherStreamId(), session.getSessionId(), e.getMessage());
+ return;
+ }
+
+ this.singleStreamRecordingService.startRecorderEndpointForPublisherEndpoint(recording.getId(), profile,
participant, startedCountDown);
} else if (RecordingUtils.IS_COMPOSED(recording.getOutputMode()) && !recording.hasVideo()) {
// Connect this stream to existing Composite recorder
log.info("Joining PublisherEndpoint to existing Composite in session {} for new stream of participant {}",
session.getSessionId(), participant.getParticipantPublicId());
- this.composedRecordingService.joinPublisherEndpointToComposite(session, recordingId, participant);
+ this.composedRecordingService.joinPublisherEndpointToComposite(session, recording.getId(), participant);
}
}
- public void stopOneIndividualStreamRecording(KurentoSession session, String streamId, long kmsDisconnectionTime) {
+ public void stopOneIndividualStreamRecording(Session session, String streamId, Long kmsDisconnectionTime) {
Recording recording = this.sessionsRecordings.get(session.getSessionId());
if (recording == null) {
- log.error("Cannot stop recording of existing stream {}. Session {} is not being recorded", streamId,
- session.getSessionId());
+ recording = this.sessionsRecordingsStarting.get(session.getSessionId());
+ if (recording == null) {
+ log.error("Cannot stop recording of existing stream {}. Session {} is not being recorded", streamId,
+ session.getSessionId());
+ return;
+ } else {
+ // Recording is still starting
+ log.warn("Recording {} is still in \"starting\" status", recording.getId());
+ }
}
if (OutputMode.INDIVIDUAL.equals(recording.getOutputMode())) {
// Stop specific RecorderEndpoint for this stream
log.info("Stopping RecorderEndpoint in session {} for stream of participant {}", session.getSessionId(),
streamId);
final CountDownLatch stoppedCountDown = new CountDownLatch(1);
- this.singleStreamRecordingService.stopRecorderEndpointOfPublisherEndpoint(session.getSessionId(), streamId,
+ this.singleStreamRecordingService.stopRecorderEndpointOfPublisherEndpoint(recording.getId(), streamId,
stoppedCountDown, kmsDisconnectionTime);
try {
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
@@ -430,68 +498,110 @@ public class RecordingManager {
}
public Collection getFinishedRecordings() {
- return this.getAllRecordingsFromHost().stream().filter(recording -> recording.getStatus().equals(Status.ready))
- .collect(Collectors.toSet());
+ return recordingManagerUtils.getAllRecordingsFromStorage().stream()
+ .filter(recording -> recording.getStatus().equals(Status.ready)).collect(Collectors.toSet());
}
public Recording getRecording(String recordingId) {
- return this.getRecordingFromHost(recordingId);
+ return recordingManagerUtils.getRecordingFromStorage(recordingId);
}
public Collection getAllRecordings() {
- return this.getAllRecordingsFromHost();
+ return recordingManagerUtils.getAllRecordingsFromStorage();
}
public String getFreeRecordingId(String sessionId) {
- Set recordingIds = this.getRecordingIdsFromHost();
- String recordingId = sessionId;
- boolean isPresent = recordingIds.contains(recordingId);
- int i = 1;
- while (isPresent) {
- recordingId = sessionId + "-" + i;
- i++;
- isPresent = recordingIds.contains(recordingId);
- }
- return recordingId;
+ return recordingManagerUtils.getFreeRecordingId(sessionId);
}
public HttpStatus deleteRecordingFromHost(String recordingId, boolean force) {
- if (!force && (this.startedRecordings.containsKey(recordingId)
- || this.startingRecordings.containsKey(recordingId))) {
- // Cannot delete an active recording
- return HttpStatus.CONFLICT;
+ if (this.startedRecordings.containsKey(recordingId) || this.startingRecordings.containsKey(recordingId)) {
+ if (!force) {
+ // Cannot delete an active recording
+ return HttpStatus.CONFLICT;
+ }
}
- Recording recording = getRecordingFromHost(recordingId);
+ Recording recording = recordingManagerUtils.getRecordingFromStorage(recordingId);
if (recording == null) {
return HttpStatus.NOT_FOUND;
}
if (Status.stopped.equals(recording.getStatus())) {
- // Recording is being downloaded from remote host
- log.warn("Cancelling ongoing download process of recording {}", recording.getId());
+ // Recording is being downloaded from remote host or being uploaded
+ log.warn("Recording {} status is \"stopped\". Cancelling possible ongoing download process",
+ recording.getId());
this.recordingDownloader.cancelDownload(recording.getId());
}
- File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
+ return recordingManagerUtils.deleteRecordingFromStorage(recordingId);
+ }
+
+ public Set getAllRecordingIdsFromLocalStorage() {
+ File folder = new File(openviduConfig.getOpenViduRecordingPath());
+ File[] files = folder.listFiles();
+
+ Set fileNamesNoExtension = new HashSet<>();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ File[] innerFiles = files[i].listFiles();
+ for (int j = 0; j < innerFiles.length; j++) {
+ if (innerFiles[j].isFile()
+ && innerFiles[j].getName().startsWith(RecordingService.RECORDING_ENTITY_FILE)) {
+ fileNamesNoExtension
+ .add(innerFiles[j].getName().replaceFirst(RecordingService.RECORDING_ENTITY_FILE, ""));
+ break;
+ }
+ }
+ }
+ }
+ return fileNamesNoExtension;
+ }
+
+ public HttpStatus deleteRecordingFromLocalStorage(String recordingId) {
+ File folder = new File(openviduConfig.getOpenViduRecordingPath());
File[] files = folder.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory() && files[i].getName().equals(recordingId)) {
// Correct folder. Delete it
try {
FileUtils.deleteDirectory(files[i]);
+ return HttpStatus.NO_CONTENT;
} catch (IOException e) {
log.error("Couldn't delete folder {}", files[i].getAbsolutePath());
+ return HttpStatus.INTERNAL_SERVER_ERROR;
}
- break;
}
}
+ return HttpStatus.NOT_FOUND;
+ }
- return HttpStatus.NO_CONTENT;
+ public File getRecordingEntityFileFromLocalStorage(String recordingId) {
+ String metadataFilePath = openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
+ + RecordingService.RECORDING_ENTITY_FILE + recordingId;
+ return new File(metadataFilePath);
+ }
+
+ public Set getAllRecordingsFromLocalStorage() {
+ File folder = new File(openviduConfig.getOpenViduRecordingPath());
+ File[] files = folder.listFiles();
+ Set recordingEntities = new HashSet<>();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ File[] innerFiles = files[i].listFiles();
+ for (int j = 0; j < innerFiles.length; j++) {
+ Recording recording = getRecordingFromEntityFile(innerFiles[j]);
+ if (recording != null) {
+ recordingEntities.add(recording);
+ }
+ }
+ }
+ }
+ return recordingEntities;
}
public Recording getRecordingFromEntityFile(File file) {
- if (file.isFile() && file.getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
+ if (file.isFile() && file.getName().startsWith(RecordingService.RECORDING_ENTITY_FILE)) {
JsonObject json;
try {
json = jsonUtils.fromFileToJsonObject(file.getAbsolutePath());
@@ -499,28 +609,26 @@ public class RecordingManager {
log.error("Error reading recording entity file {}: {}", file.getAbsolutePath(), (e.getMessage()));
return null;
}
- Recording recording = new Recording(json);
- if (Status.ready.equals(recording.getStatus()) || Status.failed.equals(recording.getStatus())) {
- recording.setUrl(getRecordingUrl(recording));
- }
- return recording;
+ return getRecordingFromJson(json);
}
return null;
}
- public String getRecordingUrl(Recording recording) {
- return openviduConfig.getFinalUrl() + "recordings/" + recording.getId() + "/" + recording.getName() + "."
- + this.getExtensionFromRecording(recording);
+ public Recording getRecordingFromJson(JsonObject json) {
+ Recording recording = new Recording(json);
+ if (Status.ready.equals(recording.getStatus())
+ && composedQuickStartRecordingService.isBeingUploaded(recording)) {
+ // Recording has finished but is being uploaded
+ recording.setStatus(Status.stopped);
+ } else if (Status.ready.equals(recording.getStatus()) || Status.failed.equals(recording.getStatus())) {
+ // Recording has been completely processed and must include URL
+ recording.setUrl(recordingManagerUtils.getRecordingUrl(recording));
+ }
+ return recording;
}
- private String getExtensionFromRecording(Recording recording) {
- if (OutputMode.INDIVIDUAL.equals(recording.getOutputMode())) {
- return "zip";
- } else if (recording.hasVideo()) {
- return "mp4";
- } else {
- return "webm";
- }
+ public String getRecordingUrl(Recording recording) {
+ return recordingManagerUtils.getRecordingUrl(recording);
}
public void initAutomaticRecordingStopThread(final Session session) {
@@ -621,56 +729,6 @@ public class RecordingManager {
}
}
- private Recording getRecordingFromHost(String recordingId) {
- log.info(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
- + RecordingManager.RECORDING_ENTITY_FILE + recordingId);
- File file = new File(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
- + RecordingManager.RECORDING_ENTITY_FILE + recordingId);
- log.info("File exists: " + file.exists());
- Recording recording = this.getRecordingFromEntityFile(file);
- return recording;
- }
-
- private Set getAllRecordingsFromHost() {
- File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
- File[] files = folder.listFiles();
-
- Set recordingEntities = new HashSet<>();
- for (int i = 0; i < files.length; i++) {
- if (files[i].isDirectory()) {
- File[] innerFiles = files[i].listFiles();
- for (int j = 0; j < innerFiles.length; j++) {
- Recording recording = this.getRecordingFromEntityFile(innerFiles[j]);
- if (recording != null) {
- recordingEntities.add(recording);
- }
- }
- }
- }
- return recordingEntities;
- }
-
- private Set getRecordingIdsFromHost() {
- File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
- File[] files = folder.listFiles();
-
- Set fileNamesNoExtension = new HashSet<>();
- for (int i = 0; i < files.length; i++) {
- if (files[i].isDirectory()) {
- File[] innerFiles = files[i].listFiles();
- for (int j = 0; j < innerFiles.length; j++) {
- if (innerFiles[j].isFile()
- && innerFiles[j].getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
- fileNamesNoExtension
- .add(innerFiles[j].getName().replaceFirst(RecordingManager.RECORDING_ENTITY_FILE, ""));
- break;
- }
- }
- }
- }
- return fileNamesNoExtension;
- }
-
private void checkRecordingPaths(String openviduRecordingPath, String openviduRecordingCustomLayout)
throws OpenViduException {
log.info("Initializing recording paths");
@@ -698,7 +756,8 @@ public class RecordingManager {
}
final String testFolderPath = openviduRecordingPath + "/TEST_RECORDING_PATH_" + System.currentTimeMillis();
- final String testFilePath = testFolderPath + "/TEST_RECORDING_PATH.webm";
+ final String testFilePath = testFolderPath + "/TEST_RECORDING_PATH"
+ + RecordingService.INDIVIDUAL_RECORDING_EXTENSION;
// Check Kurento Media Server write permissions in recording path
if (this.kmsManager.getKmss().isEmpty()) {
@@ -752,7 +811,7 @@ public class RecordingManager {
log.info("Kurento Media Server has write permissions on recording path: {}", openviduRecordingPath);
try {
- new CustomFileManager().deleteFolder(testFolderPath);
+ new LocalCustomFileManager().deleteFolder(testFolderPath);
log.info("OpenVidu Server has write permissions over files created by Kurento Media Server");
} catch (IOException e) {
String errorMessage = "The recording path \"" + openviduRecordingPath
@@ -826,6 +885,8 @@ public class RecordingManager {
|| (sessionsRecordingsStarting.putIfAbsent(recording.getSessionId(), recording) != null)) {
log.error("Concurrent session recording initialization. Aborting this thread");
throw new RuntimeException("Concurrent initialization of recording " + recording.getId());
+ } else {
+ this.sessionHandler.storeRecordingToSendClientEvent(recording);
}
}
@@ -834,7 +895,6 @@ public class RecordingManager {
* collection
*/
private void recordingFromStartingToStarted(Recording recording) {
- this.sessionHandler.setRecordingStarted(recording.getSessionId(), recording);
this.sessionsRecordings.put(recording.getSessionId(), recording);
this.startingRecordings.remove(recording.getId());
this.sessionsRecordingsStarting.remove(recording.getSessionId());
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtils.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtils.java
new file mode 100644
index 00000000..b2eaa7cb
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtils.java
@@ -0,0 +1,61 @@
+package io.openvidu.server.recording.service;
+
+import java.util.Set;
+
+import org.springframework.http.HttpStatus;
+
+import io.openvidu.java.client.Recording.OutputMode;
+import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.recording.Recording;
+import io.openvidu.server.utils.JsonUtils;
+
+public abstract class RecordingManagerUtils {
+
+ protected OpenviduConfig openviduConfig;
+ protected RecordingManager recordingManager;
+
+ protected JsonUtils jsonUtils = new JsonUtils();
+
+ public RecordingManagerUtils(OpenviduConfig openviduConfig, RecordingManager recordingManager) {
+ this.openviduConfig = openviduConfig;
+ this.recordingManager = recordingManager;
+ }
+
+ public abstract Recording getRecordingFromStorage(String recordingId);
+
+ public abstract Set getAllRecordingsFromStorage();
+
+ public abstract HttpStatus deleteRecordingFromStorage(String recordingId);
+
+ protected abstract String getRecordingUrl(Recording recording);
+
+ protected abstract Set getAllRecordingIdsFromStorage();
+
+ protected String getExtensionFromRecording(Recording recording) {
+ if (OutputMode.INDIVIDUAL.equals(recording.getOutputMode())) {
+ return "zip";
+ } else if (recording.hasVideo()) {
+ return "mp4";
+ } else {
+ return "webm";
+ }
+ }
+
+ public String getFreeRecordingId(String sessionId) {
+ Set recordingIds = getAllRecordingIdsFromStorage();
+ return getNextAvailableRecordingId(sessionId, recordingIds);
+ }
+
+ private String getNextAvailableRecordingId(String baseRecordingId, Set existingRecordingIds) {
+ String recordingId = baseRecordingId;
+ boolean isPresent = existingRecordingIds.contains(recordingId);
+ int i = 1;
+ while (isPresent) {
+ recordingId = baseRecordingId + "-" + i;
+ i++;
+ isPresent = existingRecordingIds.contains(recordingId);
+ }
+ return recordingId;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtilsLocalStorage.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtilsLocalStorage.java
new file mode 100644
index 00000000..66130a87
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManagerUtilsLocalStorage.java
@@ -0,0 +1,64 @@
+package io.openvidu.server.recording.service;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.http.HttpStatus;
+
+import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.recording.Recording;
+import io.openvidu.server.rest.RequestMappings;
+
+public class RecordingManagerUtilsLocalStorage extends RecordingManagerUtils {
+
+ public RecordingManagerUtilsLocalStorage(OpenviduConfig openviduConfig, RecordingManager recordingManager) {
+ super(openviduConfig, recordingManager);
+ }
+
+ @Override
+ public Recording getRecordingFromStorage(String recordingId) {
+ File file = recordingManager.getRecordingEntityFileFromLocalStorage(recordingId);
+ return recordingManager.getRecordingFromEntityFile(file);
+ }
+
+ @Override
+ public Set getAllRecordingsFromStorage() {
+ return recordingManager.getAllRecordingsFromLocalStorage();
+ }
+
+ @Override
+ public HttpStatus deleteRecordingFromStorage(String recordingId) {
+ return recordingManager.deleteRecordingFromLocalStorage(recordingId);
+ }
+
+ @Override
+ public String getRecordingUrl(Recording recording) {
+ String basePath = RequestMappings.RECORDINGS.replaceFirst("^/", "") + "/";
+ return openviduConfig.getFinalUrl() + basePath + recording.getId() + "/" + recording.getName() + "."
+ + this.getExtensionFromRecording(recording);
+ }
+
+ @Override
+ protected Set getAllRecordingIdsFromStorage() {
+ File folder = new File(openviduConfig.getOpenViduRecordingPath());
+ File[] files = folder.listFiles();
+
+ Set fileNamesNoExtension = new HashSet<>();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ File[] innerFiles = files[i].listFiles();
+ for (int j = 0; j < innerFiles.length; j++) {
+ if (innerFiles[j].isFile()
+ && innerFiles[j].getName().startsWith(RecordingService.RECORDING_ENTITY_FILE)) {
+ fileNamesNoExtension
+ .add(innerFiles[j].getName().replaceFirst(RecordingService.RECORDING_ENTITY_FILE, ""));
+ break;
+ }
+ }
+ }
+ }
+ return fileNamesNoExtension;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java
index 15317a07..4076532c 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java
@@ -24,17 +24,19 @@ import org.slf4j.LoggerFactory;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
+import io.openvidu.java.client.Recording.Status;
import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.RecordingProperties;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.Session;
+import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.RecordingDownloader;
+import io.openvidu.server.recording.RecordingUploader;
import io.openvidu.server.utils.CommandExecutor;
import io.openvidu.server.utils.CustomFileManager;
-import io.openvidu.server.utils.QuarantineKiller;
import io.openvidu.server.utils.RecordingUtils;
public abstract class RecordingService {
@@ -44,20 +46,33 @@ public abstract class RecordingService {
protected OpenviduConfig openviduConfig;
protected RecordingManager recordingManager;
protected RecordingDownloader recordingDownloader;
+ protected RecordingUploader recordingUploader;
+ protected KmsManager kmsManager;
+ protected CustomFileManager fileManager;
protected CallDetailRecord cdr;
- protected QuarantineKiller quarantineKiller;
- protected CustomFileManager fileWriter = new CustomFileManager();
- RecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
- OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
+ public final static String RECORDING_ENTITY_FILE = ".recording.";
+ public final static String COMPOSED_RECORDING_EXTENSION = ".mp4";
+ public final static String COMPOSED_THUMBNAIL_EXTENSION = ".jpg";
+ public final static String COMPOSED_INFO_FILE_EXTENSION = ".info";
+ public final static String INDIVIDUAL_RECORDING_EXTENSION = ".webm";
+ public final static String INDIVIDUAL_STREAM_METADATA_FILE = ".stream.";
+ public final static String INDIVIDUAL_RECORDING_COMPRESSED_EXTENSION = ".zip";
+
+ public RecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
+ RecordingUploader recordingUploader, KmsManager kmsManager, CustomFileManager fileManager,
+ OpenviduConfig openviduConfig, CallDetailRecord cdr) {
this.recordingManager = recordingManager;
this.recordingDownloader = recordingDownloader;
+ this.recordingUploader = recordingUploader;
+ this.kmsManager = kmsManager;
+ this.fileManager = fileManager;
this.openviduConfig = openviduConfig;
this.cdr = cdr;
- this.quarantineKiller = quarantineKiller;
}
- public abstract Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException;
+ public abstract Recording startRecording(Session session, String recordingId, RecordingProperties properties)
+ throws OpenViduException;
public abstract Recording stopRecording(Session session, Recording recording, EndReason reason);
@@ -67,20 +82,16 @@ public abstract class RecordingService {
*/
protected void generateRecordingMetadataFile(Recording recording) {
String folder = this.openviduConfig.getOpenViduRecordingPath() + recording.getId();
- boolean newFolderCreated = this.fileWriter.createFolderIfNotExists(folder);
+ boolean newFolderCreated = this.fileManager.createFolderIfNotExists(folder);
if (newFolderCreated) {
- log.warn(
- "New folder {} created. This means A) Cluster mode is enabled B) The recording started for a session with no publishers or C) No media type compatible publishers",
- folder);
- } else {
- log.info("Folder {} already existed. Some publisher is already being recorded", folder);
+ log.info("New folder {} created for recording {}", folder, recording.getId());
}
String filePath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
- + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
+ + RecordingService.RECORDING_ENTITY_FILE + recording.getId();
String text = recording.toJson().toString();
- this.fileWriter.createAndWriteFile(filePath, text);
+ this.fileManager.createAndWriteFile(filePath, text);
log.info("Generated recording metadata file at {}", filePath);
}
@@ -92,7 +103,7 @@ public abstract class RecordingService {
*/
protected Recording sealRecordingMetadataFileAsStopped(Recording recording) {
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
- + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
+ + RecordingService.RECORDING_ENTITY_FILE + recording.getId();
return this.sealRecordingMetadataFile(recording, 0, 0, io.openvidu.java.client.Recording.Status.stopped,
entityFile);
}
@@ -105,15 +116,20 @@ public abstract class RecordingService {
*/
protected Recording sealRecordingMetadataFileAsReady(Recording recording, long size, double duration,
String metadataFilePath) {
- io.openvidu.java.client.Recording.Status status = io.openvidu.java.client.Recording.Status.failed
- .equals(recording.getStatus()) ? io.openvidu.java.client.Recording.Status.failed
- : io.openvidu.java.client.Recording.Status.ready;
+ Status status = Status.failed.equals(recording.getStatus()) ? Status.failed : Status.ready;
+
+ if (Status.ready.equals(status)) {
+ // Prevent uploading recordings from being retrieved from REST API with "ready"
+ // status. This will force their status back to "stopped" on GET until upload
+ // process has finished
+ storeAsUploadingRecording(recording);
+ }
// Status is now failed or ready. Url property must be defined
recording.setUrl(recordingManager.getRecordingUrl(recording));
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
- + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
+ + RecordingService.RECORDING_ENTITY_FILE + recording.getId();
return this.sealRecordingMetadataFile(recording, size, duration, status, entityFile);
}
@@ -123,7 +139,7 @@ public abstract class RecordingService {
recording.setSize(size); // Size in bytes
recording.setDuration(duration > 0 ? duration : 0); // Duration in seconds
- if (this.fileWriter.overwriteFile(metadataFilePath, recording.toJson().toString())) {
+ if (this.fileManager.overwriteFile(metadataFilePath, recording.toJson().toString())) {
log.info("Sealed recording metadata file at {} with status [{}]", metadataFilePath, status.name());
}
@@ -141,8 +157,8 @@ public abstract class RecordingService {
if (properties.name() == null || properties.name().isEmpty()) {
// No name provided for the recording file. Use recordingId
RecordingProperties.Builder builder = new RecordingProperties.Builder().name(recordingId)
- .outputMode(properties.outputMode()).hasAudio(properties.hasAudio())
- .hasVideo(properties.hasVideo());
+ .outputMode(properties.outputMode()).hasAudio(properties.hasAudio()).hasVideo(properties.hasVideo())
+ .mediaNode(properties.mediaNode());
if (RecordingUtils.IS_COMPOSED(properties.outputMode()) && properties.hasVideo()) {
builder.resolution(properties.resolution());
builder.recordingLayout(properties.recordingLayout());
@@ -175,6 +191,10 @@ public abstract class RecordingService {
protected OpenViduException failStartRecording(Session session, Recording recording, String errorMessage) {
log.error("Recording start failed for session {}: {}", session.getSessionId(), errorMessage);
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
+
+ sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
+ getMetadataFilePath(recording));
+
this.recordingManager.startingRecordings.remove(recording.getId());
this.recordingManager.sessionsRecordingsStarting.remove(session.getSessionId());
this.stopRecording(session, recording, null);
@@ -186,6 +206,30 @@ public abstract class RecordingService {
this.recordingManager.startedRecordings.remove(recording.getId());
}
+ protected String getMetadataFilePath(Recording recording) {
+ final String folderPath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/";
+ return folderPath + RecordingService.RECORDING_ENTITY_FILE + recording.getId();
+ }
+
+ protected void uploadRecording(final Recording recording, EndReason reason) {
+ recordingUploader.uploadRecording(recording, () -> {
+ final long timestamp = System.currentTimeMillis();
+ cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
+ }, () -> {
+ final long timestamp = System.currentTimeMillis();
+ cdr.recordRecordingStatusChanged(recording, reason, timestamp,
+ io.openvidu.java.client.Recording.Status.failed);
+ });
+ }
+
+ protected void storeAsUploadingRecording(Recording recording) {
+ recordingUploader.storeAsUploadingRecording(recording.getId());
+ }
+
+ protected boolean isBeingUploaded(Recording recording) {
+ return recordingUploader.isBeingUploaded(recording.getId());
+ }
+
/**
* Simple wrapper for returning update RecordingProperties and a free
* recordingId when starting a new recording
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java
index 1691d833..9bea2201 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java
@@ -24,7 +24,9 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
@@ -45,10 +47,10 @@ import org.kurento.client.StoppedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
@@ -59,34 +61,32 @@ import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
import io.openvidu.server.kurento.core.KurentoParticipant;
-import io.openvidu.server.kurento.core.KurentoSession;
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
+import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.recording.RecorderEndpointWrapper;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.RecordingDownloader;
-import io.openvidu.server.utils.QuarantineKiller;
+import io.openvidu.server.recording.RecordingUploader;
+import io.openvidu.server.utils.CustomFileManager;
public class SingleStreamRecordingService extends RecordingService {
private static final Logger log = LoggerFactory.getLogger(SingleStreamRecordingService.class);
+ // One recorder endpoint active at a time per stream
private Map> activeRecorders = new ConcurrentHashMap<>();
- private Map> storedRecorders = new ConcurrentHashMap<>();
-
- private final String INDIVIDUAL_STREAM_METADATA_FILE = ".stream.";
+ // Multiple recorder endpoints per stream during a recording
+ private Map>> storedRecorders = new ConcurrentHashMap<>();
public SingleStreamRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
- OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
- super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
+ RecordingUploader recordingUploader, KmsManager kmsManager, CustomFileManager fileManager,
+ OpenviduConfig openviduConfig, CallDetailRecord cdr) {
+ super(recordingManager, recordingDownloader, recordingUploader, kmsManager, fileManager, openviduConfig, cdr);
}
@Override
- public Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException {
-
- PropertiesRecordingId updatePropertiesAndRecordingId = this.setFinalRecordingNameAndGetFreeRecordingId(session,
- properties);
- properties = updatePropertiesAndRecordingId.properties;
- String recordingId = updatePropertiesAndRecordingId.recordingId;
+ public Recording startRecording(Session session, String recordingId, RecordingProperties properties)
+ throws OpenViduException {
log.info("Starting individual ({}) recording {} of session {}",
properties.hasVideo() ? (properties.hasAudio() ? "video+audio" : "video-only") : "audioOnly",
@@ -95,14 +95,14 @@ public class SingleStreamRecordingService extends RecordingService {
Recording recording = new Recording(session.getSessionId(), recordingId, properties);
this.recordingManager.recordingToStarting(recording);
- activeRecorders.put(session.getSessionId(), new ConcurrentHashMap());
- storedRecorders.put(session.getSessionId(), new ConcurrentHashMap());
+ activeRecorders.put(recording.getId(), new ConcurrentHashMap<>());
+ storedRecorders.put(recording.getId(), new ConcurrentHashMap<>());
- final int activePublishers = session.getActivePublishers();
- final CountDownLatch recordingStartedCountdown = new CountDownLatch(activePublishers);
+ int activePublishersToRecord = session.getActiveIndividualRecordedPublishers();
+ final CountDownLatch recordingStartedCountdown = new CountDownLatch(activePublishersToRecord);
for (Participant p : session.getParticipants()) {
- if (p.isStreaming()) {
+ if (p.isStreaming() && p.getToken().record()) {
MediaProfileSpecType profile = null;
try {
@@ -114,7 +114,7 @@ public class SingleStreamRecordingService extends RecordingService {
recordingStartedCountdown.countDown();
continue;
}
- this.startRecorderEndpointForPublisherEndpoint(session, recordingId, profile, p,
+ this.startRecorderEndpointForPublisherEndpoint(recording.getId(), profile, p,
recordingStartedCountdown);
}
}
@@ -131,30 +131,28 @@ public class SingleStreamRecordingService extends RecordingService {
this.generateRecordingMetadataFile(recording);
- // Increment active recordings
- ((KurentoSession) session).getKms().getActiveRecordings().incrementAndGet();
-
return recording;
}
@Override
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
- recording = this.sealRecordingMetadataFileAsStopped(recording);
- return this.stopRecording(session, recording, reason, 0);
+ return this.stopRecording(session, recording, reason, null);
}
- public Recording stopRecording(Session session, Recording recording, EndReason reason, long kmsDisconnectionTime) {
+ public Recording stopRecording(Session session, Recording recording, EndReason reason, Long kmsDisconnectionTime) {
log.info("Stopping individual ({}) recording {} of session {}. Reason: {}",
recording.hasVideo() ? (recording.hasAudio() ? "video+audio" : "video-only") : "audioOnly",
recording.getId(), recording.getSessionId(), reason);
- final HashMap wrappers = new HashMap<>(
- storedRecorders.get(recording.getSessionId()));
+ final List wrappers = new ArrayList<>();
+ storedRecorders.get(recording.getId()).values().forEach(list -> {
+ wrappers.addAll(list);
+ });
final CountDownLatch stoppedCountDown = new CountDownLatch(wrappers.size());
- for (RecorderEndpointWrapper wrapper : wrappers.values()) {
- this.stopRecorderEndpointOfPublisherEndpoint(recording.getSessionId(), wrapper.getStreamId(),
- stoppedCountDown, kmsDisconnectionTime);
+ for (RecorderEndpointWrapper wrapper : wrappers) {
+ this.stopRecorderEndpointOfPublisherEndpoint(recording.getId(), wrapper.getStreamId(), stoppedCountDown,
+ kmsDisconnectionTime);
}
try {
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
@@ -171,31 +169,30 @@ public class SingleStreamRecordingService extends RecordingService {
final Recording[] finalRecordingArray = new Recording[1];
finalRecordingArray[0] = recording;
try {
- this.recordingDownloader.downloadRecording(finalRecordingArray[0], wrappers.keySet(), () -> {
+ this.recordingDownloader.downloadRecording(finalRecordingArray[0], wrappers, () -> {
// Update recording entity files with final file size
- for (RecorderEndpointWrapper wrapper : wrappers.values()) {
+ for (RecorderEndpointWrapper wrapper : wrappers) {
if (wrapper.getSize() == 0) {
updateIndividualMetadataFile(wrapper);
}
}
finalRecordingArray[0] = this.sealMetadataFiles(finalRecordingArray[0]);
- final long timestamp = System.currentTimeMillis();
- cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
- finalRecordingArray[0].getStatus());
+ cleanRecordingWrappers(finalRecordingArray[0]);
- cleanRecordingWrappers(finalRecordingArray[0].getSessionId());
+ // Decrement active recordings once it is downloaded. This method will also drop
+ // the Media Node if no more sessions or recordings and status is
+ // waiting-idle-to-terminate
+ kmsManager.decrementActiveRecordings(finalRecordingArray[0].getRecordingProperties(),
+ finalRecordingArray[0].getId(), session);
- // Decrement active recordings once it is downloaded
- ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
-
- // Now we can drop Media Node if waiting-idle-to-terminate
- this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
+ // Upload if necessary
+ this.uploadRecording(finalRecordingArray[0], reason);
});
} catch (IOException e) {
log.error("Error while downloading recording {}", finalRecordingArray[0].getName());
- cleanRecordingWrappers(finalRecordingArray[0].getSessionId());
+ cleanRecordingWrappers(finalRecordingArray[0]);
}
if (reason != null && session != null) {
@@ -206,60 +203,42 @@ public class SingleStreamRecordingService extends RecordingService {
return finalRecordingArray[0];
}
- public void startRecorderEndpointForPublisherEndpoint(final Session session, String recordingId,
- MediaProfileSpecType profile, final Participant participant, CountDownLatch globalStartLatch) {
+ public void startRecorderEndpointForPublisherEndpoint(final String recordingId, MediaProfileSpecType profile,
+ final Participant participant, CountDownLatch globalStartLatch) {
log.info("Starting single stream recorder for stream {} in session {}", participant.getPublisherStreamId(),
- session.getSessionId());
+ participant.getSessionId());
+
+ final String streamId = participant.getPublisherStreamId();
try {
if (participant.singleRecordingLock.tryLock(15, TimeUnit.SECONDS)) {
try {
- if (this.activeRecorders.get(session.getSessionId())
- .containsKey(participant.getPublisherStreamId())) {
+ if (this.activeRecorders.get(recordingId).containsKey(streamId)) {
log.warn("Concurrent initialization of RecorderEndpoint for stream {} of session {}. Returning",
- participant.getPublisherStreamId(), session.getSessionId());
+ streamId, participant.getSessionId());
return;
}
- if (recordingId == null) {
- // Stream is being recorded because is a new publisher in an ongoing recorded
- // session. If recordingId is defined is because Stream is being recorded from
- // "startRecording" method
- Recording recording = this.recordingManager.sessionsRecordings.get(session.getSessionId());
- if (recording == null) {
- recording = this.recordingManager.sessionsRecordingsStarting.get(session.getSessionId());
- if (recording == null) {
- log.error(
- "Cannot start single stream recorder for stream {} in session {}. The recording {} cannot be found",
- participant.getPublisherStreamId(), session.getSessionId(), recordingId);
- return;
- }
- }
- recordingId = recording.getId();
-
- try {
- profile = generateMediaProfile(recording.getRecordingProperties(), participant);
- } catch (OpenViduException e) {
- log.error("Cannot start single stream recorder for stream {} in session {}: {}",
- participant.getPublisherStreamId(), session.getSessionId(), e.getMessage());
- return;
- }
- }
+ // Update stream recording counter
+ final List wrapperList = storedRecorders.get(recordingId).get(streamId);
+ final int streamCounter = wrapperList != null ? wrapperList.size() : 0;
+ String fileName = streamCounter == 0 ? streamId : (streamId + "-" + streamCounter);
KurentoParticipant kurentoParticipant = (KurentoParticipant) participant;
MediaPipeline pipeline = kurentoParticipant.getPublisher().getPipeline();
RecorderEndpoint recorder = new RecorderEndpoint.Builder(pipeline,
- "file://" + openviduConfig.getOpenViduRemoteRecordingPath() + recordingId + "/"
- + participant.getPublisherStreamId() + ".webm").withMediaProfile(profile).build();
+ "file://" + openviduConfig.getOpenViduRemoteRecordingPath() + recordingId + "/" + fileName
+ + RecordingService.INDIVIDUAL_RECORDING_EXTENSION).withMediaProfile(profile)
+ .build();
recorder.addRecordingListener(new EventListener() {
@Override
public void onEvent(RecordingEvent event) {
- activeRecorders.get(session.getSessionId()).get(participant.getPublisherStreamId())
+ activeRecorders.get(recordingId).get(streamId)
.setStartTime(Long.parseLong(event.getTimestampMillis()));
- log.info("Recording started event for stream {}", participant.getPublisherStreamId());
+ log.info("Recording started event for stream {}", streamId);
globalStartLatch.countDown();
}
});
@@ -271,15 +250,14 @@ public class SingleStreamRecordingService extends RecordingService {
}
});
- RecorderEndpointWrapper wrapper = new RecorderEndpointWrapper(recorder,
- participant.getParticipantPublicId(), recordingId, participant.getPublisherStreamId(),
- participant.getClientMetadata(), participant.getServerMetadata(),
- kurentoParticipant.getPublisher().getMediaOptions().hasAudio(),
- kurentoParticipant.getPublisher().getMediaOptions().hasVideo(),
- kurentoParticipant.getPublisher().getMediaOptions().getTypeOfVideo());
-
- activeRecorders.get(session.getSessionId()).put(participant.getPublisherStreamId(), wrapper);
- storedRecorders.get(session.getSessionId()).put(participant.getPublisherStreamId(), wrapper);
+ RecorderEndpointWrapper wrapper = new RecorderEndpointWrapper(recorder, kurentoParticipant,
+ recordingId, fileName);
+ activeRecorders.get(recordingId).put(streamId, wrapper);
+ if (wrapperList != null) {
+ wrapperList.add(wrapper);
+ } else {
+ storedRecorders.get(recordingId).put(streamId, new ArrayList<>(Arrays.asList(wrapper)));
+ }
connectAccordingToProfile(kurentoParticipant.getPublisher(), recorder, profile);
wrapper.getRecorder().record();
@@ -289,51 +267,73 @@ public class SingleStreamRecordingService extends RecordingService {
}
} else {
log.error(
- "Timeout waiting for individual recording lock to be available for participant {} of session {}",
- participant.getParticipantPublicId(), session.getSessionId());
+ "Timeout waiting for individual recording lock to be available to start stream recording for participant {} of session {}",
+ participant.getParticipantPublicId(), participant.getSessionId());
}
} catch (InterruptedException e) {
log.error(
- "InterruptedException waiting for individual recording lock to be available for participant {} of session {}",
- participant.getParticipantPublicId(), session.getSessionId());
+ "InterruptedException waiting for individual recording lock to be available to start stream recording for participant {} of session {}",
+ participant.getParticipantPublicId(), participant.getSessionId());
}
}
- public void stopRecorderEndpointOfPublisherEndpoint(String sessionId, String streamId,
+ public void stopRecorderEndpointOfPublisherEndpoint(String recordingId, String streamId,
CountDownLatch globalStopLatch, Long kmsDisconnectionTime) {
- log.info("Stopping single stream recorder for stream {} in session {}", streamId, sessionId);
- final RecorderEndpointWrapper finalWrapper = activeRecorders.get(sessionId).remove(streamId);
- if (finalWrapper != null && kmsDisconnectionTime == 0) {
- finalWrapper.getRecorder().addStoppedListener(new EventListener() {
- @Override
- public void onEvent(StoppedEvent event) {
- finalWrapper.setEndTime(Long.parseLong(event.getTimestampMillis()));
- generateIndividualMetadataFile(finalWrapper);
- log.info("Recording stopped event for stream {}", streamId);
- finalWrapper.getRecorder().release();
- globalStopLatch.countDown();
- }
- });
- finalWrapper.getRecorder().stop();
- } else {
- if (kmsDisconnectionTime != 0) {
- // Stopping recorder endpoint because of a KMS disconnection
- finalWrapper.setEndTime(kmsDisconnectionTime);
- generateIndividualMetadataFile(finalWrapper);
- log.warn("Forcing individual recording stop after KMS restart for stream {} in session {}", streamId,
- sessionId);
- } else {
- if (storedRecorders.get(sessionId).containsKey(streamId)) {
- log.info("Stream {} recording of session {} was already stopped", streamId, sessionId);
+
+ log.info("Stopping single stream recorder for stream {} in recording {}", streamId, recordingId);
+
+ final RecorderEndpointWrapper finalWrapper = activeRecorders.get(recordingId).remove(streamId);
+ if (finalWrapper != null) {
+ KurentoParticipant kParticipant = finalWrapper.getParticipant();
+ try {
+ if (kParticipant.singleRecordingLock.tryLock(15, TimeUnit.SECONDS)) {
+ try {
+ if (kmsDisconnectionTime == null) {
+ finalWrapper.getRecorder().addStoppedListener(new EventListener() {
+ @Override
+ public void onEvent(StoppedEvent event) {
+ finalWrapper.setEndTime(Long.parseLong(event.getTimestampMillis()));
+ generateIndividualMetadataFile(finalWrapper);
+ log.info("Recording stopped event for stream {}", streamId);
+ finalWrapper.getRecorder().release();
+ globalStopLatch.countDown();
+ }
+ });
+ finalWrapper.getRecorder().stop();
+ } else {
+ // Stopping recorder endpoint because of a KMS disconnection
+ finalWrapper.setEndTime(kmsDisconnectionTime);
+ generateIndividualMetadataFile(finalWrapper);
+ globalStopLatch.countDown();
+ log.warn(
+ "Forcing individual recording stop after KMS restart for stream {} in recording {}",
+ streamId, recordingId);
+ }
+ } finally {
+ kParticipant.singleRecordingLock.unlock();
+ }
} else {
- log.error("Stream {} wasn't being recorded in session {}", streamId, sessionId);
+ log.error(
+ "Timeout waiting for individual recording lock to be available to stop stream recording for participant {} of session {}",
+ kParticipant.getParticipantPublicId(), kParticipant.getSessionId());
}
+ } catch (InterruptedException e) {
+ log.error(
+ "InterruptedException waiting for individual recording lock to be available to stop stream recording for participant {} of session {}",
+ kParticipant.getParticipantPublicId(), kParticipant.getSessionId());
+ }
+ } else {
+ // The streamId has no associated RecorderEndpoint
+ if (storedRecorders.get(recordingId).containsKey(streamId)) {
+ log.info("Stream {} recording of recording {} was already stopped", streamId, recordingId);
+ } else {
+ log.info("Stream {} wasn't being recorded in recording {}", streamId, recordingId);
}
globalStopLatch.countDown();
}
}
- private MediaProfileSpecType generateMediaProfile(RecordingProperties properties, Participant participant)
+ MediaProfileSpecType generateMediaProfile(RecordingProperties properties, Participant participant)
throws OpenViduException {
KurentoParticipant kParticipant = (KurentoParticipant) participant;
@@ -406,19 +406,19 @@ public class SingleStreamRecordingService extends RecordingService {
}
private void generateIndividualMetadataFile(RecorderEndpointWrapper wrapper) {
- this.commonWriteIndividualMetadataFile(wrapper, this.fileWriter::createAndWriteFile);
+ this.commonWriteIndividualMetadataFile(wrapper, this.fileManager::createAndWriteFile);
}
private void updateIndividualMetadataFile(RecorderEndpointWrapper wrapper) {
- this.commonWriteIndividualMetadataFile(wrapper, this.fileWriter::overwriteFile);
+ this.commonWriteIndividualMetadataFile(wrapper, this.fileManager::overwriteFile);
}
private void commonWriteIndividualMetadataFile(RecorderEndpointWrapper wrapper,
BiFunction writeFunction) {
String filesPath = this.openviduConfig.getOpenViduRecordingPath() + wrapper.getRecordingId() + "/";
- File videoFile = new File(filesPath + wrapper.getStreamId() + ".webm");
+ File videoFile = new File(filesPath + wrapper.getNameWithExtension());
wrapper.setSize(videoFile.length());
- String metadataFilePath = filesPath + INDIVIDUAL_STREAM_METADATA_FILE + wrapper.getStreamId();
+ String metadataFilePath = filesPath + INDIVIDUAL_STREAM_METADATA_FILE + wrapper.getName();
String metadataFileContent = wrapper.toJson().toString();
writeFunction.apply(metadataFilePath, metadataFileContent);
}
@@ -428,7 +428,7 @@ public class SingleStreamRecordingService extends RecordingService {
// individual recordings) and "size" (sum of all individual recordings size)
String folderPath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/";
- String metadataFilePath = folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
+ String metadataFilePath = folderPath + RecordingService.RECORDING_ENTITY_FILE + recording.getId();
String syncFilePath = folderPath + recording.getName() + ".json";
recording = this.recordingManager.getRecordingFromEntityFile(new File(metadataFilePath));
@@ -441,7 +441,6 @@ public class SingleStreamRecordingService extends RecordingService {
File[] files = folder.listFiles();
Reader reader = null;
- Gson gson = new Gson();
// Sync metadata json object to store in "RECORDING_NAME.json"
JsonObject json = new JsonObject();
@@ -458,12 +457,14 @@ public class SingleStreamRecordingService extends RecordingService {
} catch (FileNotFoundException e) {
log.error("Error reading file {}. Error: {}", files[i].getAbsolutePath(), e.getMessage());
}
- RecorderEndpointWrapper wr = gson.fromJson(reader, RecorderEndpointWrapper.class);
+ RecorderEndpointWrapper wr = new RecorderEndpointWrapper(
+ JsonParser.parseReader(reader).getAsJsonObject());
minStartTime = Math.min(minStartTime, wr.getStartTime());
maxEndTime = Math.max(maxEndTime, wr.getEndTime());
accumulatedSize += wr.getSize();
JsonObject jsonFile = new JsonObject();
+ jsonFile.addProperty("name", wr.getNameWithExtension());
jsonFile.addProperty("connectionId", wr.getConnectionId());
jsonFile.addProperty("streamId", wr.getStreamId());
jsonFile.addProperty("size", wr.getSize());
@@ -482,8 +483,9 @@ public class SingleStreamRecordingService extends RecordingService {
}
json.add("files", jsonArrayFiles);
- this.fileWriter.createAndWriteFile(syncFilePath, new GsonBuilder().setPrettyPrinting().create().toJson(json));
- this.generateZipFileAndCleanFolder(folderPath, recording.getName() + ".zip");
+ this.fileManager.createAndWriteFile(syncFilePath, new GsonBuilder().setPrettyPrinting().create().toJson(json));
+ this.generateZipFileAndCleanFolder(folderPath,
+ recording.getName() + RecordingService.INDIVIDUAL_RECORDING_COMPRESSED_EXTENSION);
double duration = (double) (maxEndTime - minStartTime) / 1000;
duration = duration > 0 ? duration : 0;
@@ -506,7 +508,8 @@ public class SingleStreamRecordingService extends RecordingService {
for (int i = 0; i < files.length; i++) {
String fileExtension = FilenameUtils.getExtension(files[i].getName());
- if (files[i].isFile() && (fileExtension.equals("json") || fileExtension.equals("webm"))) {
+ if (files[i].isFile() && (fileExtension.equals("json")
+ || RecordingService.INDIVIDUAL_RECORDING_EXTENSION.equals("." + fileExtension))) {
// Zip video files and json sync metadata file
FileInputStream fis = new FileInputStream(files[i]);
@@ -520,7 +523,7 @@ public class SingleStreamRecordingService extends RecordingService {
fis.close();
}
- if (!files[i].getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
+ if (!files[i].getName().startsWith(RecordingService.RECORDING_ENTITY_FILE)) {
// Clean inspected file if it is not
files[i].delete();
}
@@ -539,9 +542,9 @@ public class SingleStreamRecordingService extends RecordingService {
}
}
- private void cleanRecordingWrappers(String sessionId) {
- this.storedRecorders.remove(sessionId);
- this.activeRecorders.remove(sessionId);
+ private void cleanRecordingWrappers(Recording recording) {
+ this.storedRecorders.remove(recording.getId());
+ this.activeRecorders.remove(recording.getId());
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/resources/CDRHttpHandler.java b/openvidu-server/src/main/java/io/openvidu/server/resources/CDRHttpHandler.java
index 151e24ee..a2f94978 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/resources/CDRHttpHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/resources/CDRHttpHandler.java
@@ -23,6 +23,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.rest.RequestMappings;
@Configuration
public class CDRHttpHandler implements WebMvcConfigurer {
@@ -33,7 +34,7 @@ public class CDRHttpHandler implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String cdrPath = openviduConfig.getOpenviduCdrPath();
- registry.addResourceHandler("/cdr/**.log").addResourceLocations("file:" + cdrPath);
+ registry.addResourceHandler(RequestMappings.CDR + "/**.log").addResourceLocations("file:" + cdrPath);
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/resources/FrontendResourceHandler.java b/openvidu-server/src/main/java/io/openvidu/server/resources/FrontendResourceHandler.java
index 076b4c86..4add4b05 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/resources/FrontendResourceHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/resources/FrontendResourceHandler.java
@@ -29,7 +29,7 @@ import io.openvidu.server.config.OpenviduConfig;
* /NEW_FRONTEND_PATH. Entrypoint file index.html must have tag
*
*
- * By default in OpenVidu CE this path is /dashbaord and in OpenVidu PRO is
+ * By default in OpenVidu CE this path is /dashboard and in OpenVidu PRO is
* /inspector
*
* @author Pablo Fuente (pablofuenteperez@gmail.com)
@@ -42,10 +42,10 @@ public class FrontendResourceHandler extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
- registry.addViewController("/" + openviduConfig.getOpenViduFrontendDefaultPath())
- .setViewName("redirect:/" + openviduConfig.getOpenViduFrontendDefaultPath() + "/");
- registry.addViewController("/" + openviduConfig.getOpenViduFrontendDefaultPath() + "/")
- .setViewName("forward:/" + openviduConfig.getOpenViduFrontendDefaultPath() + "/index.html");
+ registry.addViewController(openviduConfig.getOpenViduFrontendDefaultPath())
+ .setViewName("redirect:" + openviduConfig.getOpenViduFrontendDefaultPath() + "/");
+ registry.addViewController(openviduConfig.getOpenViduFrontendDefaultPath() + "/")
+ .setViewName("forward:" + openviduConfig.getOpenViduFrontendDefaultPath() + "/index.html");
super.addViewControllers(registry);
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingCustomLayoutsResourceHandler.java b/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingCustomLayoutsResourceHandler.java
index dab3e05f..29311660 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingCustomLayoutsResourceHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingCustomLayoutsResourceHandler.java
@@ -24,6 +24,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.rest.RequestMappings;
/**
* This class serves custom recording layouts from host folder indicated in
@@ -41,7 +42,8 @@ public class RecordingCustomLayoutsResourceHandler implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String customLayoutsPath = openviduConfig.getOpenviduRecordingCustomLayout();
- registry.addResourceHandler("/layouts/custom/**").addResourceLocations("file:" + customLayoutsPath);
+ registry.addResourceHandler(RequestMappings.CUSTOM_LAYOUTS + "/**")
+ .addResourceLocations("file:" + customLayoutsPath);
}
}
\ No newline at end of file
diff --git a/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingsResourceHandler.java b/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingsResourceHandler.java
index f1433fa6..9bb6474f 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingsResourceHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/resources/RecordingsResourceHandler.java
@@ -23,6 +23,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.rest.RequestMappings;
/**
* This class serves recording files from host folder indicated in configuration
@@ -39,7 +40,7 @@ public class RecordingsResourceHandler implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String recordingsPath = openviduConfig.getOpenViduRecordingPath();
- registry.addResourceHandler("/recordings/**").addResourceLocations("file:" + recordingsPath);
+ registry.addResourceHandler(RequestMappings.RECORDINGS + "/**").addResourceLocations("file:" + recordingsPath);
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/ApiRestPathRewriteFilter.java b/openvidu-server/src/main/java/io/openvidu/server/rest/ApiRestPathRewriteFilter.java
new file mode 100644
index 00000000..2abe443b
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/ApiRestPathRewriteFilter.java
@@ -0,0 +1,145 @@
+package io.openvidu.server.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+import org.springframework.web.util.WebUtils;
+
+import io.openvidu.server.config.OpenviduConfig;
+
+public class ApiRestPathRewriteFilter implements Filter {
+
+ protected static final Logger log = LoggerFactory.getLogger(ApiRestPathRewriteFilter.class);
+
+ protected Map PATH_REDIRECTIONS_MAP = new HashMap() {
+ {
+ // WS
+ put("/info", RequestMappings.WS_INFO);
+ // APIs
+ put("/api/", RequestMappings.API + "/");
+ put("/config", RequestMappings.API + "/config");
+ put("/config/", RequestMappings.API + "/config/");
+ put("/cdr", RequestMappings.CDR);
+ // Static resources
+ put("/cdr/", RequestMappings.CDR + "/");
+ put("/recordings/", RequestMappings.RECORDINGS + "/");
+ put("/layouts/custom/", RequestMappings.CUSTOM_LAYOUTS + "/");
+
+ put("/accept-certificate", RequestMappings.ACCEPT_CERTIFICATE); // ??
+ }
+ };
+ protected String[] PATH_REDIRECTIONS_ARRAY;
+
+ public ApiRestPathRewriteFilter() {
+ PATH_REDIRECTIONS_ARRAY = PATH_REDIRECTIONS_MAP.keySet().toArray(new String[PATH_REDIRECTIONS_MAP.size()]);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ log.warn("Support for deprecated REST API paths enabled. Update your REST API clients to use the new paths");
+ log.warn(
+ "Deprecated path support will be removed in a future release. You can disable old path support to test compatibility with property SUPPORT_DEPRECATED_API=false");
+ }
+
+ @Override
+ public void destroy() {
+ // Nothing to free up...
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse res = (HttpServletResponse) response;
+ String requestPath = req.getRequestURI();
+
+ String oldBasePath = null;
+ String newBasePath = null;
+
+ for (final String path : PATH_REDIRECTIONS_ARRAY) {
+ if (requestPath.startsWith(path)) {
+ oldBasePath = path;
+ break;
+ }
+ }
+
+ if (oldBasePath != null) {
+
+ newBasePath = PATH_REDIRECTIONS_MAP.get(oldBasePath);
+
+ String redirectURI = newBasePath + requestPath.substring(oldBasePath.length());
+ StringBuffer redirectURL = new StringBuffer(
+ ((HttpServletRequest) request).getRequestURL().toString().replaceFirst(oldBasePath, newBasePath));
+
+ String logPathEnding = oldBasePath.endsWith("/") ? "**" : "/**";
+ log.warn("Path {} is deprecated. Use path {} instead. Deprecated path will be removed in a future release",
+ oldBasePath + logPathEnding, newBasePath + logPathEnding);
+
+ chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
+ @Override
+ public String getRequestURI() {
+ return redirectURI;
+ }
+
+ @Override
+ public StringBuffer getRequestURL() {
+ return redirectURL;
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ if (WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE.equals(name))
+ return redirectURI;
+ return super.getAttribute(name);
+ }
+ }, response);
+
+ } else {
+ chain.doFilter(req, res);
+ }
+ }
+
+ public static void protectOldPathsCe(
+ ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry conf,
+ OpenviduConfig openviduConf) throws Exception {
+
+ conf.antMatchers("/api/**").authenticated()
+ // /config
+ .antMatchers(HttpMethod.GET, "/config/openvidu-publicurl").permitAll()
+ .antMatchers(HttpMethod.GET, "/config/**").authenticated()
+ // /cdr
+ .antMatchers(HttpMethod.GET, "/cdr/**").authenticated()
+ // /accept-certificate
+ .antMatchers(HttpMethod.GET, "/accept-certificate").permitAll()
+ // Dashboard
+ .antMatchers(HttpMethod.GET, "/dashboard/**").authenticated();
+
+ // Security for recording layouts
+ conf.antMatchers("/layouts/**").authenticated();
+
+ // Security for recorded video files
+ if (openviduConf.getOpenViduRecordingPublicAccess()) {
+ conf = conf.antMatchers("/recordings/**").permitAll();
+ } else {
+ conf = conf.antMatchers("/recordings/**").authenticated();
+ }
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/CDRRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/CDRRestController.java
index 0ef92fa3..47d207d2 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/rest/CDRRestController.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/CDRRestController.java
@@ -47,7 +47,7 @@ import io.openvidu.server.config.OpenviduConfig;
*/
@RestController
@CrossOrigin
-@RequestMapping("/cdr")
+@RequestMapping(RequestMappings.CDR)
public class CDRRestController {
private static final Logger log = LoggerFactory.getLogger(CDRRestController.class);
@@ -58,7 +58,7 @@ public class CDRRestController {
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity listCdrFiles() {
- log.info("REST API: GET /cdr");
+ log.info("REST API: GET {}", RequestMappings.CDR);
String cdrPath = openviduConfig.getOpenviduCdrPath();
JsonArray cdrFiles = new JsonArray();
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/CertificateRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/CertificateRestController.java
index c5058d9d..542249fa 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/rest/CertificateRestController.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/CertificateRestController.java
@@ -24,7 +24,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
-@RequestMapping("/accept-certificate")
+@RequestMapping(RequestMappings.ACCEPT_CERTIFICATE)
public class CertificateRestController {
@RequestMapping(method = RequestMethod.GET)
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java
index c8f4d847..9c1d1e2f 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java
@@ -21,9 +21,8 @@ import org.apache.http.Header;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpHeaders;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -36,6 +35,7 @@ import com.google.gson.JsonObject;
import io.openvidu.server.cdr.CDREventName;
import io.openvidu.server.config.OpenviduBuildInfo;
import io.openvidu.server.config.OpenviduConfig;
+import io.openvidu.server.utils.RestUtils;
/**
*
@@ -43,7 +43,8 @@ import io.openvidu.server.config.OpenviduConfig;
*/
@RestController
@CrossOrigin
-@RequestMapping("/config")
+@ConditionalOnMissingBean(name = "configRestControllerPro")
+@RequestMapping(RequestMappings.API + "/config")
public class ConfigRestController {
private static final Logger log = LoggerFactory.getLogger(ConfigRestController.class);
@@ -54,10 +55,18 @@ public class ConfigRestController {
@Autowired
private OpenviduBuildInfo openviduBuildInfo;
+ @RequestMapping(method = RequestMethod.GET)
+ public ResponseEntity getOpenViduConfiguration() {
+
+ log.info("REST API: GET {}", RequestMappings.API + "/config");
+
+ return this.getConfig();
+ }
+
@RequestMapping(value = "/openvidu-version", method = RequestMethod.GET)
public String getOpenViduServerVersion() {
- log.info("REST API: GET /config/openvidu-version");
+ log.info("REST API: GET {}/openvidu-version", RequestMappings.API + "/config");
return openviduBuildInfo.getOpenViduServerVersion();
}
@@ -65,7 +74,7 @@ public class ConfigRestController {
@RequestMapping(value = "/openvidu-publicurl", method = RequestMethod.GET)
public String getOpenViduPublicUrl() {
- log.info("REST API: GET /config/openvidu-publicurl");
+ log.info("REST API: GET {}/openvidu-publicurl", RequestMappings.API + "/config");
return openviduConfig.getFinalUrl();
}
@@ -73,7 +82,7 @@ public class ConfigRestController {
@RequestMapping(value = "/openvidu-recording", method = RequestMethod.GET)
public Boolean getOpenViduRecordingEnabled() {
- log.info("REST API: GET /config/openvidu-recording");
+ log.info("REST API: GET {}/openvidu-recording", RequestMappings.API + "/config");
return openviduConfig.isRecordingModuleEnabled();
}
@@ -81,7 +90,7 @@ public class ConfigRestController {
@RequestMapping(value = "/openvidu-recording-path", method = RequestMethod.GET)
public String getOpenViduRecordingPath() {
- log.info("REST API: GET /config/openvidu-recording-path");
+ log.info("REST API: GET {}/openvidu-recording-path", RequestMappings.API + "/config");
return openviduConfig.getOpenViduRecordingPath();
}
@@ -89,16 +98,12 @@ public class ConfigRestController {
@RequestMapping(value = "/openvidu-cdr", method = RequestMethod.GET)
public Boolean getOpenViduCdrEnabled() {
- log.info("REST API: GET /config/openvidu-cdr");
+ log.info("REST API: GET {}/openvidu-cdr", RequestMappings.API + "/config");
return openviduConfig.isCdrEnabled();
}
- @RequestMapping(method = RequestMethod.GET)
- public ResponseEntity getOpenViduConfiguration() {
-
- log.info("REST API: GET /config");
-
+ protected ResponseEntity getConfig() {
JsonObject json = new JsonObject();
json.addProperty("VERSION", openviduBuildInfo.getVersion());
json.addProperty("DOMAIN_OR_PUBLIC_IP", openviduConfig.getDomainOrPublicIp());
@@ -109,6 +114,8 @@ public class ConfigRestController {
json.addProperty("OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH", openviduConfig.getVideoMinRecvBandwidth());
json.addProperty("OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH", openviduConfig.getVideoMaxSendBandwidth());
json.addProperty("OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH", openviduConfig.getVideoMinSendBandwidth());
+ json.addProperty("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", openviduConfig.getOpenviduForcedCodec().name());
+ json.addProperty("OPENVIDU_STREAMS_ALLOW_TRANSCODING", openviduConfig.isOpenviduAllowingTranscoding());
json.addProperty("OPENVIDU_SESSIONS_GARBAGE_INTERVAL", openviduConfig.getSessionGarbageInterval());
json.addProperty("OPENVIDU_SESSIONS_GARBAGE_THRESHOLD", openviduConfig.getSessionGarbageThreshold());
json.addProperty("OPENVIDU_RECORDING", openviduConfig.isRecordingModuleEnabled());
@@ -141,13 +148,7 @@ public class ConfigRestController {
json.add("OPENVIDU_WEBHOOK_EVENTS", webhookEvents);
}
- return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK);
- }
-
- protected HttpHeaders getResponseHeaders() {
- HttpHeaders responseHeaders = new HttpHeaders();
- responseHeaders.setContentType(MediaType.APPLICATION_JSON);
- return responseHeaders;
+ return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/RequestMappings.java b/openvidu-server/src/main/java/io/openvidu/server/rest/RequestMappings.java
new file mode 100644
index 00000000..f842ee85
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/RequestMappings.java
@@ -0,0 +1,21 @@
+package io.openvidu.server.rest;
+
+public class RequestMappings {
+
+ // WebSocket
+ final public static String WS_RPC = "/openvidu";
+ final public static String WS_INFO = "/openvidu/info";
+ // REST API
+ final public static String API = "/openvidu/api";
+ final public static String CDR = "/openvidu/cdr";
+ final public static String API_ELK = "/openvidu/elk";
+ final public static String API_INSPECTOR = "/openvidu/inspector-api";
+ // Static resources
+ final public static String RECORDINGS = "/openvidu/recordings";
+ final public static String CUSTOM_LAYOUTS = "/openvidu/layouts";
+ final public static String FRONTEND_CE = "/dashboard";
+ final public static String FRONTEND_PRO = "/inspector";
+
+ final public static String ACCEPT_CERTIFICATE = "/openvidu/accept-certificate"; // ????
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java
index 606efcb1..4a5b56ea 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java
@@ -20,7 +20,9 @@ package io.openvidu.server.rest;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -34,9 +36,8 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpHeaders;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
@@ -46,8 +47,19 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
import io.openvidu.client.OpenViduException;
+import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.ConnectionType;
+import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.Recording.OutputMode;
@@ -62,11 +74,12 @@ import io.openvidu.server.core.IdentifierPrefixes;
import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionManager;
+import io.openvidu.server.core.Token;
import io.openvidu.server.kurento.core.KurentoMediaOptions;
-import io.openvidu.server.kurento.core.KurentoTokenOptions;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.recording.service.RecordingManager;
import io.openvidu.server.utils.RecordingUtils;
+import io.openvidu.server.utils.RestUtils;
/**
*
@@ -74,114 +87,39 @@ import io.openvidu.server.utils.RecordingUtils;
*/
@RestController
@CrossOrigin
-@RequestMapping("/api")
+@ConditionalOnMissingBean(name = "sessionRestControllerPro")
+@RequestMapping(RequestMappings.API)
public class SessionRestController {
private static final Logger log = LoggerFactory.getLogger(SessionRestController.class);
@Autowired
- private SessionManager sessionManager;
+ protected SessionManager sessionManager;
@Autowired
- private RecordingManager recordingManager;
+ protected RecordingManager recordingManager;
@Autowired
- private OpenviduConfig openviduConfig;
+ protected OpenviduConfig openviduConfig;
@RequestMapping(value = "/sessions", method = RequestMethod.POST)
- public ResponseEntity> getSessionId(@RequestBody(required = false) Map, ?> params) {
+ public ResponseEntity> initializeSession(@RequestBody(required = false) Map, ?> params) {
- log.info("REST API: POST /api/sessions {}", params != null ? params.toString() : "{}");
+ log.info("REST API: POST {}/sessions {}", RequestMappings.API, params != null ? params.toString() : "{}");
- SessionProperties.Builder builder = new SessionProperties.Builder();
- String customSessionId = null;
-
- if (params != null) {
-
- String mediaModeString;
- String recordingModeString;
- String defaultOutputModeString;
- String defaultRecordingLayoutString;
- String defaultCustomLayout;
- String forcedVideoCodec;
- Boolean allowTranscoding;
- try {
- mediaModeString = (String) params.get("mediaMode");
- recordingModeString = (String) params.get("recordingMode");
- defaultOutputModeString = (String) params.get("defaultOutputMode");
- defaultRecordingLayoutString = (String) params.get("defaultRecordingLayout");
- defaultCustomLayout = (String) params.get("defaultCustomLayout");
- customSessionId = (String) params.get("customSessionId");
- forcedVideoCodec = (String) params.get("forcedVideoCodec");
- allowTranscoding = (Boolean) params.get("allowTranscoding");
- } catch (ClassCastException e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/sessions",
- HttpStatus.BAD_REQUEST);
- }
-
- try {
-
- // Safe parameter retrieval. Default values if not defined
- if (recordingModeString != null) {
- RecordingMode recordingMode = RecordingMode.valueOf(recordingModeString);
- builder = builder.recordingMode(recordingMode);
- } else {
- builder = builder.recordingMode(RecordingMode.MANUAL);
- }
- if (defaultOutputModeString != null) {
- OutputMode defaultOutputMode = OutputMode.valueOf(defaultOutputModeString);
- builder = builder.defaultOutputMode(defaultOutputMode);
- } else {
- builder.defaultOutputMode(OutputMode.COMPOSED);
- }
- if (defaultRecordingLayoutString != null) {
- RecordingLayout defaultRecordingLayout = RecordingLayout.valueOf(defaultRecordingLayoutString);
- builder = builder.defaultRecordingLayout(defaultRecordingLayout);
- } else {
- builder.defaultRecordingLayout(RecordingLayout.BEST_FIT);
- }
- if (mediaModeString != null) {
- MediaMode mediaMode = MediaMode.valueOf(mediaModeString);
- builder = builder.mediaMode(mediaMode);
- } else {
- builder = builder.mediaMode(MediaMode.ROUTED);
- }
- if (customSessionId != null && !customSessionId.isEmpty()) {
- if (!sessionManager.formatChecker.isValidCustomSessionId(customSessionId)) {
- return this.generateErrorResponse(
- "Parameter \"customSessionId\" is wrong. Must be an alphanumeric string",
- "/api/sessions", HttpStatus.BAD_REQUEST);
- }
- builder = builder.customSessionId(customSessionId);
- }
- builder = builder.defaultCustomLayout((defaultCustomLayout != null) ? defaultCustomLayout : "");
- if (forcedVideoCodec != null) {
- builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec));
- } else {
- builder = builder.forcedVideoCodec(openviduConfig.getOpenviduForcedCodec());
- }
- if (allowTranscoding != null) {
- builder = builder.allowTranscoding(allowTranscoding);
- } else {
- builder = builder.allowTranscoding(openviduConfig.isOpenviduAllowingTranscoding());
- }
-
- } catch (IllegalArgumentException e) {
- return this.generateErrorResponse("RecordingMode " + params.get("recordingMode") + " | "
- + "Default OutputMode " + params.get("defaultOutputMode") + " | " + "Default RecordingLayout "
- + params.get("defaultRecordingLayout") + " | " + "MediaMode " + params.get("mediaMode")
- + ". Some parameter is not defined", "/api/sessions", HttpStatus.BAD_REQUEST);
- }
+ SessionProperties sessionProperties;
+ try {
+ sessionProperties = getSessionPropertiesFromParams(params).build();
+ } catch (Exception e) {
+ return this.generateErrorResponse(e.getMessage(), "/sessions", HttpStatus.BAD_REQUEST);
}
- SessionProperties sessionProperties = builder.build();
-
String sessionId;
- if (customSessionId != null && !customSessionId.isEmpty()) {
- if (sessionManager.getSessionWithNotActive(customSessionId) != null) {
+ if (sessionProperties.customSessionId() != null && !sessionProperties.customSessionId().isEmpty()) {
+ if (sessionManager.getSessionWithNotActive(sessionProperties.customSessionId()) != null) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
- sessionId = customSessionId;
+ sessionId = sessionProperties.customSessionId();
} else {
sessionId = IdentifierPrefixes.SESSION_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ RandomStringUtils.randomAlphanumeric(9);
@@ -190,28 +128,27 @@ public class SessionRestController {
Session sessionNotActive = sessionManager.storeSessionNotActive(sessionId, sessionProperties);
log.info("New session {} initialized {}", sessionId, this.sessionManager.getSessionsWithNotActive().stream()
.map(Session::getSessionId).collect(Collectors.toList()).toString());
- JsonObject responseJson = sessionNotActive.toJson();
- responseJson.remove("sessionId");
- responseJson.addProperty("id", sessionNotActive.getSessionId());
- return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK);
+
+ return new ResponseEntity<>(sessionNotActive.toJson(false, false).toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
}
@RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.GET)
public ResponseEntity> getSession(@PathVariable("sessionId") String sessionId,
+ @RequestParam(value = "pendingConnections", defaultValue = "false", required = false) boolean pendingConnections,
@RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
- log.info("REST API: GET /api/sessions/{}", sessionId);
+ log.info("REST API: GET {}/sessions/{}", RequestMappings.API, sessionId);
Session session = this.sessionManager.getSession(sessionId);
if (session != null) {
- JsonObject response = (webRtcStats == true) ? session.withStatsToJson() : session.toJson();
- return new ResponseEntity<>(response.toString(), getResponseHeaders(), HttpStatus.OK);
+ JsonObject response = session.toJson(pendingConnections, webRtcStats);
+ return new ResponseEntity<>(response.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
} else {
Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId);
if (sessionNotActive != null) {
- JsonObject response = (webRtcStats == true) ? sessionNotActive.withStatsToJson()
- : sessionNotActive.toJson();
- return new ResponseEntity<>(response.toString(), getResponseHeaders(), HttpStatus.OK);
+ JsonObject response = sessionNotActive.toJson(pendingConnections, webRtcStats);
+ return new ResponseEntity<>(response.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@@ -220,26 +157,27 @@ public class SessionRestController {
@RequestMapping(value = "/sessions", method = RequestMethod.GET)
public ResponseEntity> listSessions(
+ @RequestParam(value = "pendingConnections", defaultValue = "false", required = false) boolean pendingConnections,
@RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
- log.info("REST API: GET /api/sessions?webRtcStats={}", webRtcStats);
+ log.info("REST API: GET {}/sessions", RequestMappings.API);
Collection sessions = this.sessionManager.getSessionsWithNotActive();
JsonObject json = new JsonObject();
JsonArray jsonArray = new JsonArray();
- sessions.forEach(s -> {
- JsonObject sessionJson = (webRtcStats == true) ? s.withStatsToJson() : s.toJson();
+ sessions.forEach(session -> {
+ JsonObject sessionJson = session.toJson(pendingConnections, webRtcStats);
jsonArray.add(sessionJson);
});
json.addProperty("numberOfElements", sessions.size());
json.add("content", jsonArray);
- return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK);
+ return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
}
@RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.DELETE)
public ResponseEntity> closeSession(@PathVariable("sessionId") String sessionId) {
- log.info("REST API: DELETE /api/sessions/{}", sessionId);
+ log.info("REST API: DELETE {}/sessions/{}", RequestMappings.API, sessionId);
Session session = this.sessionManager.getSession(sessionId);
if (session != null) {
@@ -263,26 +201,102 @@ public class SessionRestController {
}
} else {
String errorMsg = "Timeout waiting for Session " + sessionId
- + " closing lock to be available for closing from DELETE /api/sessions";
+ + " closing lock to be available for closing from DELETE " + RequestMappings.API
+ + "/sessions";
log.error(errorMsg);
- return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
+ return this.generateErrorResponse(errorMsg, "/sessions", HttpStatus.BAD_REQUEST);
}
} catch (InterruptedException e) {
String errorMsg = "InterruptedException while waiting for Session " + sessionId
- + " closing lock to be available for closing from DELETE /api/sessions";
+ + " closing lock to be available for closing from DELETE " + RequestMappings.API + "/sessions";
log.error(errorMsg);
- return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
+ return this.generateErrorResponse(errorMsg, "/sessions", HttpStatus.BAD_REQUEST);
}
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
+ @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.POST)
+ public ResponseEntity> initializeConnection(@PathVariable("sessionId") String sessionId,
+ @RequestBody Map, ?> params) {
+
+ log.info("REST API: POST {} {}", RequestMappings.API + "/sessions/" + sessionId + "/connection",
+ params.toString());
+
+ Session session = this.sessionManager.getSessionWithNotActive(sessionId);
+ if (session == null) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ ConnectionProperties connectionProperties;
+ try {
+ connectionProperties = getConnectionPropertiesFromParams(params).build();
+ } catch (Exception e) {
+ return this.generateErrorResponse(e.getMessage(), "/sessions/" + sessionId + "/connection",
+ HttpStatus.BAD_REQUEST);
+ }
+ switch (connectionProperties.getType()) {
+ case WEBRTC:
+ return this.newWebrtcConnection(session, connectionProperties);
+ case IPCAM:
+ return this.newIpcamConnection(session, connectionProperties);
+ default:
+ return this.generateErrorResponse("Wrong type parameter", "/sessions/" + sessionId + "/connection",
+ HttpStatus.BAD_REQUEST);
+ }
+ }
+
+ @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.GET)
+ public ResponseEntity> getConnection(@PathVariable("sessionId") String sessionId,
+ @PathVariable("connectionId") String connectionId) {
+
+ log.info("REST API: GET {}/sessions/{}/connection/{}", RequestMappings.API, sessionId, connectionId);
+
+ Session session = this.sessionManager.getSessionWithNotActive(sessionId);
+ if (session != null) {
+ Participant p = session.getParticipantByPublicId(connectionId);
+ if (p != null) {
+ return new ResponseEntity<>(p.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
+ } else {
+ Token t = getTokenFromConnectionId(connectionId, session.getTokenIterator());
+ if (t != null) {
+ return new ResponseEntity<>(t.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+ } else {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+ }
+
+ @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.GET)
+ public ResponseEntity> listConnections(@PathVariable("sessionId") String sessionId,
+ @RequestParam(value = "pendingConnections", defaultValue = "true", required = false) boolean pendingConnections,
+ @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
+
+ log.info("REST API: GET {}/sessions/{}/connection", RequestMappings.API, sessionId);
+
+ Session session = this.sessionManager.getSessionWithNotActive(sessionId);
+
+ if (session != null) {
+ JsonObject json = new JsonObject();
+ JsonArray jsonArray = session.getSnapshotOfConnectionsAsJsonArray(pendingConnections, webRtcStats);
+ json.addProperty("numberOfElements", jsonArray.size());
+ json.add("content", jsonArray);
+ return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+
@RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE)
- public ResponseEntity> disconnectParticipant(@PathVariable("sessionId") String sessionId,
+ public ResponseEntity> closeConnection(@PathVariable("sessionId") String sessionId,
@PathVariable("connectionId") String participantPublicId) {
- log.info("REST API: DELETE /api/sessions/{}/connection/{}", sessionId, participantPublicId);
+ log.info("REST API: DELETE {}/sessions/{}/connection/{}", RequestMappings.API, sessionId, participantPublicId);
Session session = this.sessionManager.getSessionWithNotActive(sessionId);
if (session == null) {
@@ -294,173 +308,24 @@ public class SessionRestController {
this.sessionManager.evictParticipant(participant, null, null, EndReason.forceDisconnectByServer);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
- }
-
- @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE)
- public ResponseEntity> unpublishStream(@PathVariable("sessionId") String sessionId,
- @PathVariable("streamId") String streamId) {
-
- log.info("REST API: DELETE /api/sessions/{}/stream/{}", sessionId, streamId);
-
- Session session = this.sessionManager.getSessionWithNotActive(sessionId);
- if (session == null) {
- return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
- }
-
- session = this.sessionManager.getSession(sessionId);
- if (session != null) {
-
- final String participantPrivateId = this.sessionManager.getParticipantPrivateIdFromStreamId(sessionId,
- streamId);
-
- if (participantPrivateId == null) {
+ // Try to delete unused token
+ if (session.deleteTokenFromConnectionId(participantPublicId)) {
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ } else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
-
- Participant participant = this.sessionManager.getParticipant(participantPrivateId);
- if (participant.isIpcam()) {
- return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED);
- }
-
- this.sessionManager.unpublishStream(session, streamId, null, null, EndReason.forceUnpublishByServer);
- return new ResponseEntity<>(HttpStatus.NO_CONTENT);
- } else {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
- }
-
- @RequestMapping(value = "/tokens", method = RequestMethod.POST)
- public ResponseEntity newToken(@RequestBody Map, ?> params) {
-
- if (params == null) {
- return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/api/tokens",
- HttpStatus.BAD_REQUEST);
- }
-
- log.info("REST API: POST /api/tokens {}", params.toString());
-
- String sessionId;
- String roleString;
- String metadata;
- try {
- sessionId = (String) params.get("session");
- roleString = (String) params.get("role");
- metadata = (String) params.get("data");
- } catch (ClassCastException e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/tokens", HttpStatus.BAD_REQUEST);
- }
-
- if (sessionId == null) {
- return this.generateErrorResponse("\"session\" parameter is mandatory", "/api/tokens",
- HttpStatus.BAD_REQUEST);
- }
-
- final Session session = this.sessionManager.getSessionWithNotActive(sessionId);
- if (session == null) {
- return this.generateErrorResponse("Session " + sessionId + " not found", "/api/tokens",
- HttpStatus.NOT_FOUND);
- }
-
- JsonObject kurentoOptions = null;
-
- if (params.get("kurentoOptions") != null) {
- try {
- kurentoOptions = JsonParser.parseString(params.get("kurentoOptions").toString()).getAsJsonObject();
- } catch (Exception e) {
- return this.generateErrorResponse("Error in parameter 'kurentoOptions'. It is not a valid JSON object",
- "/api/tokens", HttpStatus.BAD_REQUEST);
- }
- }
-
- OpenViduRole role;
- try {
- if (roleString != null) {
- role = OpenViduRole.valueOf(roleString);
- } else {
- role = OpenViduRole.PUBLISHER;
- }
- } catch (IllegalArgumentException e) {
- return this.generateErrorResponse("Parameter role " + params.get("role") + " is not defined", "/api/tokens",
- HttpStatus.BAD_REQUEST);
- }
-
- KurentoTokenOptions kurentoTokenOptions = null;
- if (kurentoOptions != null) {
- try {
- kurentoTokenOptions = new KurentoTokenOptions(kurentoOptions);
- } catch (Exception e) {
- return this.generateErrorResponse("Type error in some parameter of 'kurentoOptions'", "/api/tokens",
- HttpStatus.BAD_REQUEST);
- }
- }
-
- metadata = (metadata != null) ? metadata : "";
-
- // While closing a session tokens can't be generated
- if (session.closingLock.readLock().tryLock()) {
- try {
- String token = sessionManager.newToken(session, role, metadata, kurentoTokenOptions);
-
- JsonObject responseJson = new JsonObject();
- responseJson.addProperty("id", token);
- responseJson.addProperty("session", sessionId);
- responseJson.addProperty("role", role.toString());
- responseJson.addProperty("data", metadata);
- responseJson.addProperty("token", token);
-
- if (kurentoOptions != null) {
- JsonObject kurentoOptsResponse = new JsonObject();
- if (kurentoTokenOptions.getVideoMaxRecvBandwidth() != null) {
- kurentoOptsResponse.addProperty("videoMaxRecvBandwidth",
- kurentoTokenOptions.getVideoMaxRecvBandwidth());
- }
- if (kurentoTokenOptions.getVideoMinRecvBandwidth() != null) {
- kurentoOptsResponse.addProperty("videoMinRecvBandwidth",
- kurentoTokenOptions.getVideoMinRecvBandwidth());
- }
- if (kurentoTokenOptions.getVideoMaxSendBandwidth() != null) {
- kurentoOptsResponse.addProperty("videoMaxSendBandwidth",
- kurentoTokenOptions.getVideoMaxSendBandwidth());
- }
- if (kurentoTokenOptions.getVideoMinSendBandwidth() != null) {
- kurentoOptsResponse.addProperty("videoMinSendBandwidth",
- kurentoTokenOptions.getVideoMinSendBandwidth());
- }
- if (kurentoTokenOptions.getAllowedFilters().length > 0) {
- JsonArray filters = new JsonArray();
- for (String filter : kurentoTokenOptions.getAllowedFilters()) {
- filters.add(filter);
- }
- kurentoOptsResponse.add("allowedFilters", filters);
- }
- responseJson.add("kurentoOptions", kurentoOptsResponse);
- }
- return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK);
- } catch (Exception e) {
- return this.generateErrorResponse(
- "Error generating token for session " + sessionId + ": " + e.getMessage(), "/api/tokens",
- HttpStatus.INTERNAL_SERVER_ERROR);
- } finally {
- session.closingLock.readLock().unlock();
- }
- } else {
- log.error("Session {} is in the process of closing. Token couldn't be generated", sessionId);
- return this.generateErrorResponse("Session " + sessionId + " not found", "/api/tokens",
- HttpStatus.NOT_FOUND);
}
}
@RequestMapping(value = "/recordings/start", method = RequestMethod.POST)
- public ResponseEntity> startRecordingSession(@RequestBody Map, ?> params) {
+ public ResponseEntity> startRecording(@RequestBody Map, ?> params) {
if (params == null) {
- return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/api/recordings/start",
+ return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/recordings/start",
HttpStatus.BAD_REQUEST);
}
- log.info("REST API: POST /api/recordings/start {}", params.toString());
+ log.info("REST API: POST {}/recordings/start {}", RequestMappings.API, params.toString());
if (!this.openviduConfig.isRecordingModuleEnabled()) {
// OpenVidu Server configuration property "OPENVIDU_RECORDING" is set to false
@@ -468,69 +333,19 @@ public class SessionRestController {
}
String sessionId;
- String name;
- String outputModeString;
- String resolution;
- Boolean hasAudio;
- Boolean hasVideo;
- String recordingLayoutString;
- String customLayout;
- Long shmSize = null;
try {
sessionId = (String) params.get("session");
- name = (String) params.get("name");
- outputModeString = (String) params.get("outputMode");
- resolution = (String) params.get("resolution");
- hasAudio = (Boolean) params.get("hasAudio");
- hasVideo = (Boolean) params.get("hasVideo");
- recordingLayoutString = (String) params.get("recordingLayout");
- customLayout = (String) params.get("customLayout");
- if (params.get("shmSize") != null) {
- shmSize = new Long(params.get("shmSize").toString());
- }
- } catch (ClassCastException | NumberFormatException e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/recordings/start",
+ } catch (Exception e) {
+ return this.generateErrorResponse("Type error in parameter \"session\"", "/recordings/start",
HttpStatus.BAD_REQUEST);
}
if (sessionId == null) {
// "session" parameter not found
- return this.generateErrorResponse("\"session\" parameter is mandatory", "/api/recordings/start",
+ return this.generateErrorResponse("\"session\" parameter is mandatory", "/recordings/start",
HttpStatus.BAD_REQUEST);
}
- OutputMode finalOutputMode = OutputMode.COMPOSED;
- RecordingLayout recordingLayout = null;
- if (outputModeString != null && !outputModeString.isEmpty()) {
- try {
- finalOutputMode = OutputMode.valueOf(outputModeString);
- } catch (Exception e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/recordings/start",
- HttpStatus.BAD_REQUEST);
- }
- }
- if (RecordingUtils.IS_COMPOSED(finalOutputMode)) {
- if (resolution != null && !sessionManager.formatChecker.isAcceptableRecordingResolution(resolution)) {
- return this.generateErrorResponse(
- "Wrong \"resolution\" parameter. Acceptable values from 100 to 1999 for both width and height",
- "/api/recordings/start", HttpStatus.UNPROCESSABLE_ENTITY);
- }
- if (recordingLayoutString != null && !recordingLayoutString.isEmpty()) {
- try {
- recordingLayout = RecordingLayout.valueOf(recordingLayoutString);
- } catch (Exception e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/recordings/start",
- HttpStatus.BAD_REQUEST);
- }
- }
- }
- if ((hasAudio != null && hasVideo != null) && !hasAudio && !hasVideo) {
- // Cannot start a recording with both "hasAudio" and "hasVideo" to false
- return this.generateErrorResponse(
- "Cannot start a recording with both \"hasAudio\" and \"hasVideo\" set to false",
- "/api/recordings/start", HttpStatus.UNPROCESSABLE_ENTITY);
- }
-
Session session = sessionManager.getSession(sessionId);
if (session == null) {
session = sessionManager.getSessionNotActive(sessionId);
@@ -557,52 +372,37 @@ public class SessionRestController {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
- // If outputMode is COMPOSED when defaultOutputMode is COMPOSED_QUICK_START,
- // change outputMode to COMPOSED_QUICK_START (and vice versa)
- OutputMode defaultOutputMode = session.getSessionProperties().defaultOutputMode();
- if (OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode) && OutputMode.COMPOSED.equals(finalOutputMode)) {
- finalOutputMode = OutputMode.COMPOSED_QUICK_START;
- } else if (OutputMode.COMPOSED.equals(defaultOutputMode)
- && OutputMode.COMPOSED_QUICK_START.equals(finalOutputMode)) {
- finalOutputMode = OutputMode.COMPOSED;
+ RecordingProperties recordingProperties;
+ try {
+ recordingProperties = getRecordingPropertiesFromParams(params, session).build();
+ } catch (RuntimeException e) {
+ return this.generateErrorResponse(e.getMessage(), "/sessions", HttpStatus.UNPROCESSABLE_ENTITY);
+ } catch (Exception e) {
+ return this.generateErrorResponse(e.getMessage(), "/sessions", HttpStatus.BAD_REQUEST);
}
- RecordingProperties.Builder builder = new RecordingProperties.Builder();
- builder.outputMode(
- finalOutputMode == null ? session.getSessionProperties().defaultOutputMode() : finalOutputMode);
- if (RecordingUtils.IS_COMPOSED(finalOutputMode)) {
- if (resolution != null) {
- builder.resolution(resolution);
- }
- builder.recordingLayout(recordingLayout == null ? session.getSessionProperties().defaultRecordingLayout()
- : recordingLayout);
- if (RecordingLayout.CUSTOM.equals(recordingLayout)) {
- builder.customLayout(
- customLayout == null ? session.getSessionProperties().defaultCustomLayout() : customLayout);
- }
- if (shmSize != null) {
- if (shmSize < 134217728L) {
- return this.generateErrorResponse("Wrong \"shmSize\" parameter. Must be 134217728 (128 MB) minimum",
- "/api/recordings/start", HttpStatus.UNPROCESSABLE_ENTITY);
- }
- builder.shmSize(shmSize);
- }
- }
- builder.name(name).hasAudio(hasAudio != null ? hasAudio : true).hasVideo(hasVideo != null ? hasVideo : true);
-
try {
- Recording startedRecording = this.recordingManager.startRecording(session, builder.build());
- return new ResponseEntity<>(startedRecording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
+ Recording startedRecording = this.recordingManager.startRecording(session, recordingProperties);
+ return new ResponseEntity<>(startedRecording.toJson().toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
} catch (OpenViduException e) {
- return new ResponseEntity<>("Error starting recording: " + e.getMessage(), getResponseHeaders(),
- HttpStatus.INTERNAL_SERVER_ERROR);
+ HttpStatus status = e.getCodeValue() == Code.MEDIA_NODE_STATUS_WRONG.getValue()
+ ? HttpStatus.SERVICE_UNAVAILABLE
+ : HttpStatus.INTERNAL_SERVER_ERROR;
+ return new ResponseEntity<>("Error starting recording: " + e.getMessage(), RestUtils.getResponseHeaders(),
+ status);
}
}
@RequestMapping(value = "/recordings/stop/{recordingId}", method = RequestMethod.POST)
- public ResponseEntity> stopRecordingSession(@PathVariable("recordingId") String recordingId) {
+ public ResponseEntity> stopRecording(@PathVariable("recordingId") String recordingId) {
- log.info("REST API: POST /api/recordings/stop/{}", recordingId);
+ log.info("REST API: POST {}/recordings/stop/{}", RequestMappings.API, recordingId);
+
+ if (!this.openviduConfig.isRecordingModuleEnabled()) {
+ // OpenVidu Server configuration property "OPENVIDU_RECORDING" is set to false
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
Recording recording = recordingManager.getStartedRecording(recordingId);
@@ -621,8 +421,13 @@ public class SessionRestController {
Session session = sessionManager.getSession(recording.getSessionId());
- Recording stoppedRecording = this.recordingManager.stopRecording(session, recording.getId(),
- EndReason.recordingStoppedByServer);
+ Recording stoppedRecording;
+ try {
+ stoppedRecording = this.recordingManager.stopRecording(session, recording.getId(),
+ EndReason.recordingStoppedByServer);
+ } catch (Exception e) {
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+ }
session.recordingManuallyStopped.set(true);
@@ -632,13 +437,19 @@ public class SessionRestController {
session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null, null);
}
- return new ResponseEntity<>(stoppedRecording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
+ return new ResponseEntity<>(stoppedRecording.toJson().toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
}
@RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.GET)
public ResponseEntity> getRecording(@PathVariable("recordingId") String recordingId) {
- log.info("REST API: GET /api/recordings/{}", recordingId);
+ log.info("REST API: GET {}/recordings/{}", RequestMappings.API, recordingId);
+
+ if (!this.openviduConfig.isRecordingModuleEnabled()) {
+ // OpenVidu Server configuration property "OPENVIDU_RECORDING" is set to false
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
try {
Recording recording = this.recordingManager.getRecording(recordingId);
@@ -646,16 +457,21 @@ public class SessionRestController {
&& recordingManager.getStartingRecording(recording.getId()) != null) {
recording.setStatus(io.openvidu.java.client.Recording.Status.starting);
}
- return new ResponseEntity<>(recording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
+ return new ResponseEntity<>(recording.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@RequestMapping(value = "/recordings", method = RequestMethod.GET)
- public ResponseEntity> getAllRecordings() {
+ public ResponseEntity> listRecordings() {
- log.info("REST API: GET /api/recordings");
+ log.info("REST API: GET {}/recordings", RequestMappings.API);
+
+ if (!this.openviduConfig.isRecordingModuleEnabled()) {
+ // OpenVidu Server configuration property "OPENVIDU_RECORDING" is set to false
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
Collection recordings = this.recordingManager.getAllRecordings();
JsonObject json = new JsonObject();
@@ -669,26 +485,114 @@ public class SessionRestController {
});
json.addProperty("count", recordings.size());
json.add("items", jsonArray);
- return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK);
+ return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
}
@RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.DELETE)
public ResponseEntity> deleteRecording(@PathVariable("recordingId") String recordingId) {
- log.info("REST API: DELETE /api/recordings/{}", recordingId);
+ log.info("REST API: DELETE {}/recordings/{}", RequestMappings.API, recordingId);
+
+ if (!this.openviduConfig.isRecordingModuleEnabled()) {
+ // OpenVidu Server configuration property "OPENVIDU_RECORDING" is set to false
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
return new ResponseEntity<>(this.recordingManager.deleteRecordingFromHost(recordingId, false));
}
+ @RequestMapping(value = "/tokens", method = RequestMethod.POST)
+ public ResponseEntity newToken(@RequestBody Map, ?> params) {
+
+ if (params == null) {
+ return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/tokens",
+ HttpStatus.BAD_REQUEST);
+ }
+
+ log.info("REST API: POST {}/tokens {}", RequestMappings.API, params.toString());
+
+ String sessionId;
+ try {
+ sessionId = (String) params.get("session");
+ } catch (ClassCastException e) {
+ return this.generateErrorResponse("Type error in some parameter", "/tokens", HttpStatus.BAD_REQUEST);
+ }
+
+ if (sessionId == null) {
+ return this.generateErrorResponse("\"session\" parameter is mandatory", "/tokens", HttpStatus.BAD_REQUEST);
+ }
+
+ log.warn("Token API is deprecated. Use Connection API instead (POST {}/sessions/{}/connection)",
+ RequestMappings.API, sessionId);
+
+ final Session session = this.sessionManager.getSessionWithNotActive(sessionId);
+ if (session == null) {
+ return this.generateErrorResponse("Session " + sessionId + " not found", "/tokens", HttpStatus.NOT_FOUND);
+ }
+
+ ConnectionProperties connectionProperties;
+ params.remove("record");
+ try {
+ connectionProperties = getConnectionPropertiesFromParams(params).build();
+ } catch (Exception e) {
+ return this.generateErrorResponse(e.getMessage(), "/sessions/" + sessionId + "/connection",
+ HttpStatus.BAD_REQUEST);
+ }
+ ResponseEntity> entity = this.newWebrtcConnection(session, connectionProperties);
+ JsonObject jsonResponse = JsonParser.parseString(entity.getBody().toString()).getAsJsonObject();
+
+ if (jsonResponse.has("error")) {
+ return this.generateErrorResponse(jsonResponse.get("message").getAsString(), "/tokens",
+ HttpStatus.valueOf(jsonResponse.get("status").getAsInt()));
+ } else {
+ String connectionId = jsonResponse.get("id").getAsString();
+ Token token = getTokenFromConnectionId(connectionId, session.getTokenIterator());
+ return new ResponseEntity<>(token.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK);
+ }
+ }
+
+ @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE)
+ public ResponseEntity> unpublishStream(@PathVariable("sessionId") String sessionId,
+ @PathVariable("streamId") String streamId) {
+
+ log.info("REST API: DELETE {}/sessions/{}/stream/{}", RequestMappings.API, sessionId, streamId);
+
+ Session session = this.sessionManager.getSessionWithNotActive(sessionId);
+ if (session == null) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ session = this.sessionManager.getSession(sessionId);
+ if (session != null) {
+
+ final String participantPrivateId = this.sessionManager.getParticipantPrivateIdFromStreamId(sessionId,
+ streamId);
+
+ if (participantPrivateId == null) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ Participant participant = this.sessionManager.getParticipant(participantPrivateId);
+ if (participant.isIpcam()) {
+ return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED);
+ }
+
+ this.sessionManager.unpublishStream(session, streamId, null, null, EndReason.forceUnpublishByServer);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+
@RequestMapping(value = "/signal", method = RequestMethod.POST)
public ResponseEntity> signal(@RequestBody Map, ?> params) {
if (params == null) {
- return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/api/signal",
+ return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/signal",
HttpStatus.BAD_REQUEST);
}
- log.info("REST API: POST /api/signal {}", params.toString());
+ log.info("REST API: POST {}/signal {}", RequestMappings.API, params.toString());
String sessionId;
String type;
@@ -700,15 +604,14 @@ public class SessionRestController {
type = (String) params.get("type");
data = (String) params.get("data");
} catch (ClassCastException e) {
- return this.generateErrorResponse("Type error in some parameter", "/api/signal", HttpStatus.BAD_REQUEST);
+ return this.generateErrorResponse("Type error in some parameter", "/signal", HttpStatus.BAD_REQUEST);
}
JsonObject completeMessage = new JsonObject();
if (sessionId == null) {
// "session" parameter not found
- return this.generateErrorResponse("\"session\" parameter is mandatory", "/api/signal",
- HttpStatus.BAD_REQUEST);
+ return this.generateErrorResponse("\"session\" parameter is mandatory", "/signal", HttpStatus.BAD_REQUEST);
}
Session session = sessionManager.getSession(sessionId);
if (session == null) {
@@ -735,7 +638,7 @@ public class SessionRestController {
JsonArray toArray = gson.toJsonTree(to).getAsJsonArray();
completeMessage.add("to", toArray);
} catch (IllegalStateException exception) {
- return this.generateErrorResponse("\"to\" parameter is not a valid JSON array", "/api/signal",
+ return this.generateErrorResponse("\"to\" parameter is not a valid JSON array", "/signal",
HttpStatus.BAD_REQUEST);
}
}
@@ -743,63 +646,55 @@ public class SessionRestController {
try {
sessionManager.sendMessage(completeMessage.toString(), sessionId);
} catch (OpenViduException e) {
- return this.generateErrorResponse("\"to\" array has no valid connection identifiers", "/api/signal",
+ return this.generateErrorResponse("\"to\" array has no valid connection identifiers", "/signal",
HttpStatus.NOT_ACCEPTABLE);
}
return new ResponseEntity<>(HttpStatus.OK);
}
- @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.POST)
- public ResponseEntity> publishIpcam(@PathVariable("sessionId") String sessionId, @RequestBody Map, ?> params) {
+ protected ResponseEntity> newWebrtcConnection(Session session, ConnectionProperties connectionProperties) {
- if (params == null) {
- return this.generateErrorResponse("Error in body parameters. Cannot be empty",
- "/api/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST);
+ final String REQUEST_PATH = "/sessions/" + session.getSessionId() + "/connection";
+
+ // While closing a session tokens can't be generated
+ if (session.closingLock.readLock().tryLock()) {
+ try {
+ Token token = sessionManager.newToken(session, connectionProperties.getRole(),
+ connectionProperties.getData(), connectionProperties.record(),
+ connectionProperties.getKurentoOptions());
+ return new ResponseEntity<>(token.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
+ } catch (Exception e) {
+ return this.generateErrorResponse(
+ "Error creating Connection for session " + session.getSessionId() + ": " + e.getMessage(),
+ REQUEST_PATH, HttpStatus.INTERNAL_SERVER_ERROR);
+ } finally {
+ session.closingLock.readLock().unlock();
+ }
+ } else {
+ log.error("Session {} is in the process of closing. Connection couldn't be created",
+ session.getSessionId());
+ return this.generateErrorResponse("Session " + session.getSessionId() + " not found", REQUEST_PATH,
+ HttpStatus.NOT_FOUND);
}
+ }
- log.info("REST API: POST /api/sessions/{}/connection {}", sessionId, params.toString());
+ protected ResponseEntity> newIpcamConnection(Session session, ConnectionProperties connectionProperties) {
- Session session = this.sessionManager.getSessionWithNotActive(sessionId);
- if (session == null) {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- String type;
- String rtspUri;
- Boolean adaptativeBitrate;
- Boolean onlyPlayWithSubscribers;
- String data;
- try {
- type = (String) params.get("type");
- rtspUri = (String) params.get("rtspUri");
- adaptativeBitrate = (Boolean) params.get("adaptativeBitrate");
- onlyPlayWithSubscribers = (Boolean) params.get("onlyPlayWithSubscribers");
- data = (String) params.get("data");
- } catch (ClassCastException e) {
- return this.generateErrorResponse("Type error in some parameter",
- "/api/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST);
- }
- if (rtspUri == null) {
- return this.generateErrorResponse("\"rtspUri\" parameter is mandatory",
- "/api/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST);
- }
-
- type = "IPCAM"; // Other possible values in the future
- adaptativeBitrate = adaptativeBitrate != null ? adaptativeBitrate : true;
- onlyPlayWithSubscribers = onlyPlayWithSubscribers != null ? onlyPlayWithSubscribers : true;
- data = data != null ? data : "";
+ final String REQUEST_PATH = "/sessions/" + session.getSessionId() + "/connection";
boolean hasAudio = true;
boolean hasVideo = true;
boolean audioActive = true;
boolean videoActive = true;
- String typeOfVideo = type;
+ String typeOfVideo = ConnectionType.IPCAM.name();
Integer frameRate = null;
String videoDimensions = null;
KurentoMediaOptions mediaOptions = new KurentoMediaOptions(true, null, hasAudio, hasVideo, audioActive,
- videoActive, typeOfVideo, frameRate, videoDimensions, null, false, rtspUri, adaptativeBitrate,
- onlyPlayWithSubscribers);
+ videoActive, typeOfVideo, frameRate, videoDimensions, null, false, connectionProperties.getRtspUri(),
+ connectionProperties.adaptativeBitrate(), connectionProperties.onlyPlayWithSubscribers(),
+ connectionProperties.getNetworkCache());
// While closing a session IP cameras can't be published
if (session.closingLock.readLock().tryLock()) {
@@ -807,14 +702,15 @@ public class SessionRestController {
if (session.isClosed()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
- Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions, data);
- return new ResponseEntity<>(ipcamParticipant.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
+ Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions,
+ connectionProperties);
+ return new ResponseEntity<>(ipcamParticipant.toJson().toString(), RestUtils.getResponseHeaders(),
+ HttpStatus.OK);
} catch (MalformedURLException e) {
- return this.generateErrorResponse("\"rtspUri\" parameter is not a valid rtsp uri",
- "/api/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST);
+ return this.generateErrorResponse("\"rtspUri\" parameter is not a valid rtsp uri", REQUEST_PATH,
+ HttpStatus.BAD_REQUEST);
} catch (Exception e) {
- return this.generateErrorResponse(e.getMessage(), "/api/sessions/" + sessionId + "/connection",
- HttpStatus.INTERNAL_SERVER_ERROR);
+ return this.generateErrorResponse(e.getMessage(), REQUEST_PATH, HttpStatus.INTERNAL_SERVER_ERROR);
} finally {
session.closingLock.readLock().unlock();
}
@@ -823,19 +719,330 @@ public class SessionRestController {
}
}
- private ResponseEntity generateErrorResponse(String errorMessage, String path, HttpStatus status) {
+ protected SessionProperties.Builder getSessionPropertiesFromParams(Map, ?> params) throws Exception {
+
+ SessionProperties.Builder builder = new SessionProperties.Builder();
+ String customSessionId = null;
+
+ if (params != null) {
+
+ String mediaModeString;
+ String recordingModeString;
+ String defaultOutputModeString;
+ String defaultRecordingLayoutString;
+ String defaultCustomLayout;
+ String forcedVideoCodec;
+ Boolean allowTranscoding;
+ try {
+ mediaModeString = (String) params.get("mediaMode");
+ recordingModeString = (String) params.get("recordingMode");
+ defaultOutputModeString = (String) params.get("defaultOutputMode");
+ defaultRecordingLayoutString = (String) params.get("defaultRecordingLayout");
+ defaultCustomLayout = (String) params.get("defaultCustomLayout");
+ customSessionId = (String) params.get("customSessionId");
+ forcedVideoCodec = (String) params.get("forcedVideoCodec");
+ allowTranscoding = (Boolean) params.get("allowTranscoding");
+ } catch (ClassCastException e) {
+ throw new Exception("Type error in some parameter: " + e.getMessage());
+ }
+
+ try {
+ // Safe parameter retrieval. Default values if not defined
+ if (recordingModeString != null) {
+ RecordingMode recordingMode = RecordingMode.valueOf(recordingModeString);
+ builder = builder.recordingMode(recordingMode);
+ } else {
+ builder = builder.recordingMode(RecordingMode.MANUAL);
+ }
+ if (defaultOutputModeString != null) {
+ OutputMode defaultOutputMode = OutputMode.valueOf(defaultOutputModeString);
+ builder = builder.defaultOutputMode(defaultOutputMode);
+ } else {
+ builder.defaultOutputMode(OutputMode.COMPOSED);
+ }
+ if (defaultRecordingLayoutString != null) {
+ RecordingLayout defaultRecordingLayout = RecordingLayout.valueOf(defaultRecordingLayoutString);
+ builder = builder.defaultRecordingLayout(defaultRecordingLayout);
+ } else {
+ builder.defaultRecordingLayout(RecordingLayout.BEST_FIT);
+ }
+ if (defaultCustomLayout != null) {
+ builder.defaultCustomLayout(defaultCustomLayout);
+ } else {
+ builder.defaultCustomLayout("");
+ }
+ if (mediaModeString != null) {
+ MediaMode mediaMode = MediaMode.valueOf(mediaModeString);
+ builder = builder.mediaMode(mediaMode);
+ } else {
+ builder = builder.mediaMode(MediaMode.ROUTED);
+ }
+ if (customSessionId != null && !customSessionId.isEmpty()) {
+ if (!sessionManager.formatChecker.isValidCustomSessionId(customSessionId)) {
+ throw new Exception(
+ "Parameter 'customSessionId' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-]");
+ }
+ builder = builder.customSessionId(customSessionId);
+ }
+ if (forcedVideoCodec != null) {
+ builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec));
+ } else {
+ builder = builder.forcedVideoCodec(openviduConfig.getOpenviduForcedCodec());
+ }
+ if (allowTranscoding != null) {
+ builder = builder.allowTranscoding(allowTranscoding);
+ } else {
+ builder = builder.allowTranscoding(openviduConfig.isOpenviduAllowingTranscoding());
+ }
+
+ } catch (IllegalArgumentException e) {
+ throw new Exception("RecordingMode " + params.get("recordingMode") + " | " + "Default OutputMode "
+ + params.get("defaultOutputMode") + " | " + "Default RecordingLayout "
+ + params.get("defaultRecordingLayout") + " | " + "MediaMode " + params.get("mediaMode")
+ + ". Some parameter is not defined");
+ }
+ }
+ return builder;
+ }
+
+ protected ConnectionProperties.Builder getConnectionPropertiesFromParams(Map, ?> params) throws Exception {
+
+ ConnectionProperties.Builder builder = new ConnectionProperties.Builder();
+
+ String typeString;
+ String data;
+ try {
+ typeString = (String) params.get("type");
+ data = (String) params.get("data");
+ } catch (ClassCastException e) {
+ throw new Exception("Type error in some parameter: " + e.getMessage());
+ }
+
+ ConnectionType type;
+ try {
+ if (typeString != null) {
+ type = ConnectionType.valueOf(typeString);
+ } else {
+ type = ConnectionType.WEBRTC;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new Exception("Parameter 'type' " + typeString + " is not defined");
+ }
+ data = data != null ? data : "";
+
+ // Build COMMON options
+ builder.type(type).data(data).record(true);
+
+ OpenViduRole role = null;
+ KurentoOptions kurentoOptions = null;
+
+ if (ConnectionType.WEBRTC.equals(type)) {
+ String roleString;
+ try {
+ roleString = (String) params.get("role");
+ } catch (ClassCastException e) {
+ throw new Exception("Type error in parameter 'role': " + e.getMessage());
+ }
+ try {
+ if (roleString != null) {
+ role = OpenViduRole.valueOf(roleString);
+ } else {
+ role = OpenViduRole.PUBLISHER;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new Exception("Parameter role " + params.get("role") + " is not defined");
+ }
+ JsonObject kurentoOptionsJson = null;
+ if (params.get("kurentoOptions") != null) {
+ try {
+ kurentoOptionsJson = JsonParser.parseString(params.get("kurentoOptions").toString())
+ .getAsJsonObject();
+ } catch (Exception e) {
+ throw new Exception("Error in parameter 'kurentoOptions'. It is not a valid JSON object");
+ }
+ }
+ if (kurentoOptionsJson != null) {
+ try {
+ KurentoOptions.Builder builder2 = new KurentoOptions.Builder();
+ if (kurentoOptionsJson.has("videoMaxRecvBandwidth")) {
+ builder2.videoMaxRecvBandwidth(kurentoOptionsJson.get("videoMaxRecvBandwidth").getAsInt());
+ }
+ if (kurentoOptionsJson.has("videoMinRecvBandwidth")) {
+ builder2.videoMinRecvBandwidth(kurentoOptionsJson.get("videoMinRecvBandwidth").getAsInt());
+ }
+ if (kurentoOptionsJson.has("videoMaxSendBandwidth")) {
+ builder2.videoMaxSendBandwidth(kurentoOptionsJson.get("videoMaxSendBandwidth").getAsInt());
+ }
+ if (kurentoOptionsJson.has("videoMinSendBandwidth")) {
+ builder2.videoMinSendBandwidth(kurentoOptionsJson.get("videoMinSendBandwidth").getAsInt());
+ }
+ if (kurentoOptionsJson.has("allowedFilters")) {
+ JsonArray filters = kurentoOptionsJson.get("allowedFilters").getAsJsonArray();
+ String[] arrayOfFilters = new String[filters.size()];
+ Iterator it = filters.iterator();
+ int index = 0;
+ while (it.hasNext()) {
+ arrayOfFilters[index] = it.next().getAsString();
+ index++;
+ }
+ builder2.allowedFilters(arrayOfFilters);
+ }
+ kurentoOptions = builder2.build();
+ } catch (Exception e) {
+ throw new Exception("Type error in some parameter of 'kurentoOptions': " + e.getMessage());
+ }
+ }
+
+ // Build WEBRTC options
+ builder.role(role).kurentoOptions(kurentoOptions);
+
+ } else if (ConnectionType.IPCAM.equals(type)) {
+ String rtspUri;
+ Boolean adaptativeBitrate;
+ Boolean onlyPlayWithSubscribers;
+ Integer networkCache;
+ try {
+ rtspUri = (String) params.get("rtspUri");
+ adaptativeBitrate = (Boolean) params.get("adaptativeBitrate");
+ onlyPlayWithSubscribers = (Boolean) params.get("onlyPlayWithSubscribers");
+ networkCache = (Integer) params.get("networkCache");
+ } catch (ClassCastException e) {
+ throw new Exception("Type error in some parameter: " + e.getMessage());
+ }
+ adaptativeBitrate = adaptativeBitrate != null ? adaptativeBitrate : true;
+ onlyPlayWithSubscribers = onlyPlayWithSubscribers != null ? onlyPlayWithSubscribers : true;
+ networkCache = networkCache != null ? networkCache : 2000;
+
+ // Build IPCAM options
+ builder.rtspUri(rtspUri).adaptativeBitrate(adaptativeBitrate)
+ .onlyPlayWithSubscribers(onlyPlayWithSubscribers).networkCache(networkCache).build();
+ }
+
+ return builder;
+ }
+
+ protected RecordingProperties.Builder getRecordingPropertiesFromParams(Map, ?> params, Session session)
+ throws Exception {
+
+ RecordingProperties.Builder builder = new RecordingProperties.Builder();
+
+ String sessionId;
+ String name;
+ String outputModeString;
+ String resolution;
+ Boolean hasAudio;
+ Boolean hasVideo;
+ String recordingLayoutString;
+ String customLayout;
+ Long shmSize = null;
+ try {
+ sessionId = (String) params.get("session");
+ name = (String) params.get("name");
+ outputModeString = (String) params.get("outputMode");
+ resolution = (String) params.get("resolution");
+ hasAudio = (Boolean) params.get("hasAudio");
+ hasVideo = (Boolean) params.get("hasVideo");
+ recordingLayoutString = (String) params.get("recordingLayout");
+ customLayout = (String) params.get("customLayout");
+ if (params.get("shmSize") != null) {
+ shmSize = Long.parseLong(params.get("shmSize").toString());
+ }
+ } catch (ClassCastException | NumberFormatException e) {
+ throw new Exception("Type error in some parameter: " + e.getMessage());
+ }
+
+ if (sessionId == null) {
+ // "session" parameter not found
+ throw new Exception("\"session\" parameter is mandatory");
+ }
+
+ if (name != null && !name.isEmpty()) {
+ if (!sessionManager.formatChecker.isValidRecordingName(name)) {
+ throw new Exception("Parameter 'name' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-]");
+ }
+ }
+
+ OutputMode finalOutputMode = OutputMode.COMPOSED;
+ RecordingLayout recordingLayout = null;
+ if (outputModeString != null && !outputModeString.isEmpty()) {
+ try {
+ finalOutputMode = OutputMode.valueOf(outputModeString);
+ } catch (Exception e) {
+ throw new Exception("Type error in parameter 'outputMode'");
+ }
+ }
+ if (RecordingUtils.IS_COMPOSED(finalOutputMode)) {
+ if (resolution != null && !sessionManager.formatChecker.isAcceptableRecordingResolution(resolution)) {
+ throw new RuntimeException(
+ "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height");
+ }
+ if (recordingLayoutString != null && !recordingLayoutString.isEmpty()) {
+ try {
+ recordingLayout = RecordingLayout.valueOf(recordingLayoutString);
+ } catch (Exception e) {
+ throw new Exception("Type error in parameter 'recordingLayout'");
+ }
+ }
+ }
+ if ((hasAudio != null && hasVideo != null) && !hasAudio && !hasVideo) {
+ // Cannot start a recording with both "hasAudio" and "hasVideo" to false
+ throw new RuntimeException("Cannot start a recording with both \"hasAudio\" and \"hasVideo\" set to false");
+ }
+
+ // If outputMode is COMPOSED when defaultOutputMode is COMPOSED_QUICK_START,
+ // change outputMode to COMPOSED_QUICK_START (and vice versa)
+ OutputMode defaultOutputMode = session.getSessionProperties().defaultOutputMode();
+ if (OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode) && OutputMode.COMPOSED.equals(finalOutputMode)) {
+ finalOutputMode = OutputMode.COMPOSED_QUICK_START;
+ } else if (OutputMode.COMPOSED.equals(defaultOutputMode)
+ && OutputMode.COMPOSED_QUICK_START.equals(finalOutputMode)) {
+ finalOutputMode = OutputMode.COMPOSED;
+ }
+
+ builder.outputMode(
+ finalOutputMode == null ? session.getSessionProperties().defaultOutputMode() : finalOutputMode);
+ if (RecordingUtils.IS_COMPOSED(finalOutputMode)) {
+ builder.resolution(resolution != null ? resolution : "1920x1080"); // resolution == null ?
+ // sessionProperties.defaultRecordingResolution)
+ // : resolution));
+ builder.recordingLayout(recordingLayout == null ? session.getSessionProperties().defaultRecordingLayout()
+ : recordingLayout);
+ if (RecordingLayout.CUSTOM.equals(recordingLayout)) {
+ builder.customLayout(
+ customLayout == null ? session.getSessionProperties().defaultCustomLayout() : customLayout);
+ }
+ if (shmSize != null) {
+ if (shmSize < 134217728L) {
+ throw new RuntimeException("Wrong \"shmSize\" parameter. Must be 134217728 (128 MB) minimum");
+ }
+ builder.shmSize(shmSize);
+ }
+ }
+ builder.name(name).hasAudio(hasAudio != null ? hasAudio : true).hasVideo(hasVideo != null ? hasVideo : true);
+ return builder;
+ }
+
+ protected Token getTokenFromConnectionId(String connectionId, Iterator> iterator) {
+ boolean found = false;
+ Token token = null;
+ while (iterator.hasNext() && !found) {
+ Token tAux = iterator.next().getValue();
+ found = tAux.getConnectionId().equals(connectionId);
+ if (found) {
+ token = tAux;
+ }
+ }
+ return token;
+ }
+
+ protected ResponseEntity generateErrorResponse(String errorMessage, String path, HttpStatus status) {
JsonObject responseJson = new JsonObject();
responseJson.addProperty("timestamp", System.currentTimeMillis());
responseJson.addProperty("status", status.value());
responseJson.addProperty("error", status.getReasonPhrase());
responseJson.addProperty("message", errorMessage);
- responseJson.addProperty("path", path);
- return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), status);
+ responseJson.addProperty("path", RequestMappings.API + path);
+ return new ResponseEntity<>(responseJson.toString(), RestUtils.getResponseHeaders(), status);
}
- private HttpHeaders getResponseHeaders() {
- HttpHeaders responseHeaders = new HttpHeaders();
- responseHeaders.setContentType(MediaType.APPLICATION_JSON);
- return responseHeaders;
- }
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java
index b38a7d66..d4694043 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java
@@ -45,6 +45,7 @@ import org.springframework.http.HttpHeaders;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
+import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.IdentifierPrefixes;
@@ -168,6 +169,9 @@ public class RpcHandler extends DefaultJsonRpcHandler {
case ProtocolElements.RECONNECTSTREAM_METHOD:
reconnectStream(rpcConnection, request);
break;
+ case ProtocolElements.VIDEODATA_METHOD:
+ updateVideoData(rpcConnection, request);
+ break;
default:
log.error("Unrecognized request {}", request);
break;
@@ -248,7 +252,7 @@ public class RpcHandler extends DefaultJsonRpcHandler {
token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
+ RandomStringUtils.randomAlphanumeric(15);
try {
- sessionManager.newTokenForInsecureUser(session, token, null);
+ sessionManager.newTokenForInsecureUser(session, token, new ConnectionProperties.Builder().build());
} catch (Exception e) {
throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE,
"Unable to create token for session " + sessionId + ": " + e.getMessage());
@@ -260,6 +264,9 @@ public class RpcHandler extends DefaultJsonRpcHandler {
Token tokenObj = session.consumeToken(token);
if (tokenObj != null) {
+
+ session.showTokens("Token consumed");
+
String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM);
if (sessionManager.formatChecker.isServerMetadataFormatCorrect(clientMetadata)) {
@@ -493,7 +500,7 @@ public class RpcHandler extends DefaultJsonRpcHandler {
// user's stream) or if the user is the owner of the stream and has a token
// configured with this specific filter
if (isModerator || (this.userIsStreamOwner(rpcConnection.getSessionId(), participant, streamId)
- && participant.getToken().getKurentoTokenOptions().isFilterAllowed(filterType))) {
+ && participant.getToken().getKurentoOptions().isFilterAllowed(filterType))) {
JsonObject filterOptions;
try {
filterOptions = JsonParser.parseString(getStringParam(request, ProtocolElements.FILTER_OPTIONS_PARAM))
@@ -638,6 +645,20 @@ public class RpcHandler extends DefaultJsonRpcHandler {
}
}
+ private void updateVideoData(RpcConnection rpcConnection, Request request) {
+ Participant participant;
+ try {
+ participant = sanityCheckOfSession(rpcConnection, "videoData");
+ int height = getIntParam(request, "height");
+ int width = getIntParam(request, "width");
+ boolean videoActive = getBooleanParam(request, "videoActive");
+ boolean audioActive = getBooleanParam(request, "audioActive");
+ sessionManager.onVideoData(participant, request.getId(), height, width, videoActive, audioActive);
+ } catch (OpenViduException e) {
+ log.error("Error getting video data: {}", e.toString());
+ }
+ }
+
public void leaveRoomAfterConnClosed(String participantPrivateId, EndReason reason) {
try {
sessionManager.evictParticipant(this.sessionManager.getParticipant(participantPrivateId), null, null,
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/CommandExecutor.java b/openvidu-server/src/main/java/io/openvidu/server/utils/CommandExecutor.java
index 9d933ed0..608a1b84 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/CommandExecutor.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/CommandExecutor.java
@@ -21,7 +21,9 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
@@ -37,13 +39,20 @@ public class CommandExecutor {
public static String execCommand(long msTimeout, String... command) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
- return commonExecCommand(msTimeout, processBuilder);
+ return commonExecCommand(msTimeout, processBuilder, true).get(0);
+ }
+
+ public static List execCommandReturnList(long msTimeout, String... command)
+ throws IOException, InterruptedException {
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ processBuilder.redirectErrorStream(true);
+ return commonExecCommand(msTimeout, processBuilder, false);
}
public static String execCommandRedirectError(long msTimeout, File errorOutputFile, String... command)
throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder(command).redirectError(errorOutputFile);
- return commonExecCommand(msTimeout, processBuilder);
+ return commonExecCommand(msTimeout, processBuilder, true).get(0);
}
public static void execCommandRedirectStandardOutputAndError(long msTimeout, File standardOutputFile,
@@ -59,11 +68,11 @@ public class CommandExecutor {
}
}
- private static String commonExecCommand(long msTimeout, ProcessBuilder processBuilder)
+ private static List commonExecCommand(long msTimeout, ProcessBuilder processBuilder, boolean singleString)
throws IOException, InterruptedException {
Process process = processBuilder.start();
StringBuilder processOutput = new StringBuilder();
- String output;
+ List outputList = new ArrayList<>();
InputStreamReader inputStreamReader = null;
BufferedReader processOutputReader = null;
try {
@@ -71,9 +80,15 @@ public class CommandExecutor {
processOutputReader = new BufferedReader(inputStreamReader);
String readLine;
while ((readLine = processOutputReader.readLine()) != null) {
- processOutput.append(readLine + System.lineSeparator());
+ if (singleString) {
+ processOutput.append(readLine + System.lineSeparator());
+ } else {
+ outputList.add(readLine);
+ }
+ }
+ if (singleString) {
+ outputList = Arrays.asList(processOutput.toString().trim());
}
-
if (!process.waitFor(msTimeout, TimeUnit.MILLISECONDS)) {
log.error("Command {} did not receive a response in {} ms",
Arrays.toString(processBuilder.command().toArray()), msTimeout);
@@ -81,7 +96,6 @@ public class CommandExecutor {
log.error(errorMsg);
throw new IOException(errorMsg);
}
- output = processOutput.toString().trim();
} finally {
if (inputStreamReader != null) {
inputStreamReader.close();
@@ -90,7 +104,7 @@ public class CommandExecutor {
processOutputReader.close();
}
}
- return output;
+ return outputList;
}
public static String gatherLinuxHostInformation() throws IOException, InterruptedException {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileManager.java
index 3c0e0546..4cf84766 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileManager.java
@@ -28,7 +28,7 @@ import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class CustomFileManager {
+public abstract class CustomFileManager {
private static final Logger log = LoggerFactory.getLogger(CustomFileManager.class);
@@ -121,4 +121,8 @@ public class CustomFileManager {
}
}
+ public abstract void waitForFileToExistAndNotEmpty(String mediaNodeId, String absolutePathToFile) throws Exception;
+
+ public abstract int maxSecondsWaitForFile();
+
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java
index bb4a87d0..74857a43 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java
@@ -1,245 +1,27 @@
-/*
- * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
package io.openvidu.server.utils;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import javax.ws.rs.ProcessingException;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.Volume;
-import com.github.dockerjava.api.DockerClient;
-import com.github.dockerjava.api.async.ResultCallback;
-import com.github.dockerjava.api.command.*;
-import com.github.dockerjava.api.exception.ConflictException;
-import com.github.dockerjava.api.exception.DockerClientException;
-import com.github.dockerjava.api.exception.InternalServerErrorException;
-import com.github.dockerjava.api.exception.NotFoundException;
-import com.github.dockerjava.api.model.*;
-import com.github.dockerjava.core.DefaultDockerClientConfig;
-import com.github.dockerjava.core.DockerClientBuilder;
-import com.github.dockerjava.core.DockerClientConfig;
-import com.github.dockerjava.core.command.ExecStartResultCallback;
-import com.github.dockerjava.core.command.PullImageResultCallback;
+public interface DockerManager {
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+ public DockerManager init();
-import io.openvidu.client.OpenViduException;
-import io.openvidu.client.OpenViduException.Code;
-import io.openvidu.server.recording.service.WaitForContainerStoppedCallback;
+ public String runContainer(String mediaNodeId, String image, String containerName, String user,
+ List volumes, List binds, String networkMode, List envs, List command,
+ Long shmSize, boolean privileged, Map labels) throws Exception;
-public class DockerManager {
+ public void removeContainer(String mediaNodeId, String containerId, boolean force);
- private static final Logger log = LoggerFactory.getLogger(DockerManager.class);
+ public void runCommandInContainerSync(String mediaNodeId, String containerId, String command, int secondsOfWait)
+ throws IOException;
- DockerClient dockerClient;
+ public void runCommandInContainerAsync(String mediaNodeId, String containerId, String command) throws IOException;
- public DockerManager() {
- DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
- this.dockerClient = DockerClientBuilder.getInstance(config).build();
- }
-
- public void downloadDockerImage(String image, int secondsOfWait) throws Exception {
- try {
- // Pull image
- this.dockerClient.pullImageCmd(image).exec(new PullImageResultCallback()).awaitCompletion(secondsOfWait,
- TimeUnit.SECONDS);
-
- } catch (NotFoundException | InternalServerErrorException e) {
- if (dockerImageExistsLocally(image)) {
- log.info("Docker image '{}' exists locally", image);
- } else {
- throw e;
- }
- } catch (DockerClientException e) {
- log.info("Error on Pulling '{}' image. Probably because the user has stopped the execution", image);
- throw e;
- } catch (InterruptedException e) {
- log.info("Error on Pulling '{}' image. Thread was interrupted: {}", image, e.getMessage());
- throw e;
- }
- }
-
- public boolean dockerImageExistsLocally(String image) throws ProcessingException {
- boolean imageExists = false;
- try {
- this.dockerClient.inspectImageCmd(image).exec();
- imageExists = true;
- } catch (NotFoundException nfe) {
- imageExists = false;
- } catch (ProcessingException e) {
- throw e;
- }
- return imageExists;
- }
-
- public void checkDockerEnabled() throws OpenViduException {
- try {
- this.dockerImageExistsLocally("hello-world");
- log.info("Docker is installed and enabled");
- } catch (ProcessingException exception) {
- throw new OpenViduException(Code.DOCKER_NOT_FOUND, "Exception connecting to Docker daemon");
- }
- }
-
- public String runContainer(String container, String containerName, String user, List volumes,
- List binds, String networkMode, List envs, List command, Long shmSize, boolean privileged,
- Map labels)
- throws Exception {
-
- CreateContainerCmd cmd = dockerClient.createContainerCmd(container).withEnv(envs);
- if (containerName != null) {
- cmd.withName(containerName);
- }
-
- if (user != null) {
- cmd.withUser(user);
- }
-
- HostConfig hostConfig = new HostConfig().withNetworkMode(networkMode).withPrivileged(privileged);
- if (shmSize != null) {
- hostConfig.withShmSize(shmSize);
- }
- if (volumes != null) {
- cmd.withVolumes(volumes);
- }
- if (binds != null) {
- hostConfig.withBinds(binds);
- }
-
- if (labels != null) {
- cmd.withLabels(labels);
- }
-
- if (command != null) {
- cmd.withCmd(command);
- }
-
- cmd.withHostConfig(hostConfig);
-
- CreateContainerResponse response = null;
- try {
- response = cmd.exec();
- dockerClient.startContainerCmd(response.getId()).exec();
- log.info("Container ID: {}", response.getId());
- return response.getId();
- } catch (ConflictException e) {
- log.error(
- "The container name {} is already in use. Probably caused by a session with unique publisher re-publishing a stream",
- containerName);
- throw e;
- } catch (NotFoundException e) {
- log.error("Docker image {} couldn't be found in docker host", container);
- throw e;
- }
- }
-
- public void removeDockerContainer(String containerId, boolean force) {
- dockerClient.removeContainerCmd(containerId).withForce(force).exec();
- }
-
- public void cleanStrandedContainers(String imageName) {
- List existingContainers = this.dockerClient.listContainersCmd().withShowAll(true).exec();
- for (Container container : existingContainers) {
- if (container.getImage().startsWith(imageName)) {
- log.info("Stranded {} Docker container ({}) removed on startup", imageName, container.getId());
- this.dockerClient.removeContainerCmd(container.getId()).withForce(true).exec();
- }
- }
- }
-
- public String runCommandInContainer(String containerId, String command, int secondsOfWait)
- throws InterruptedException {
- ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
- .withAttachStderr(true).withCmd("bash", "-c", command).exec();
- CountDownLatch latch = new CountDownLatch(1);
- final String[] stringResponse = new String[1];
- dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback() {
- @Override
- public void onNext(Frame item) {
- stringResponse[0] = new String(item.getPayload());
- latch.countDown();
- }
- });
- latch.await(secondsOfWait, TimeUnit.SECONDS);
- return stringResponse[0];
- }
-
- public void waitForContainerStopped(String containerId, int secondsOfWait) throws Exception {
- CountDownLatch latch = new CountDownLatch(1);
- WaitForContainerStoppedCallback callback = new WaitForContainerStoppedCallback(latch);
- dockerClient.waitContainerCmd(containerId).exec(callback);
- boolean stopped = false;
- try {
- stopped = latch.await(secondsOfWait, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw e;
- }
- if (!stopped) {
- throw new Exception();
- }
- }
-
- public String getContainerIp(String containerId) {
- try {
- return CommandExecutor.execCommand(5000, "/bin/sh", "-c",
- "docker inspect -f \"{{ .NetworkSettings.IPAddress }}\" " + containerId);
- } catch (IOException | InterruptedException e) {
- log.error(e.getMessage());
- return null;
- }
- }
-
- public List getRunningContainers(String fullImageName) {
- List containerIds = new ArrayList<>();
- List existingContainers = this.dockerClient.listContainersCmd().exec();
- for (Container container : existingContainers) {
- if (container.getImage().startsWith(fullImageName)) {
- containerIds.add(container.getId());
- } else if (container.getImageId().contains(fullImageName)) {
- containerIds.add(container.getId());
- }
- }
- return containerIds;
- }
-
- public String getImageId(String fullImageName) {
- InspectImageResponse imageResponse = this.dockerClient.inspectImageCmd(fullImageName).exec();
- return imageResponse.getId();
- }
-
- public Map getLabels(String containerId) {
- InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec();
- return containerInfo.getConfig().getLabels();
- }
-
- static public String getDockerGatewayIp() {
- try {
- return CommandExecutor.execCommand(5000, "/bin/sh", "-c",
- "docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}'");
- } catch (IOException | InterruptedException e) {
- log.error(e.getMessage());
- return null;
- }
- }
+ public void waitForContainerStopped(String mediaNodeId, String containerId, int secondsOfWait) throws Exception;
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java b/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java
index 05748411..267d8f9f 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java
@@ -30,8 +30,15 @@ public class FormatChecker {
}
public boolean isValidCustomSessionId(String customSessionId) {
- // Alphanumeric string
- return customSessionId.matches("[a-zA-Z0-9_-]+");
+ return isValidAlphanumeric(customSessionId);
+ }
+
+ public boolean isValidRecordingName(String recodingName) {
+ return isValidAlphanumeric(recodingName);
+ }
+
+ private boolean isValidAlphanumeric(String str) {
+ return str.matches("[a-zA-Z0-9_-]+");
}
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java b/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java
index 7e0468a3..24242d3b 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java
@@ -20,6 +20,7 @@ package io.openvidu.server.utils;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.Reader;
import java.util.Map.Entry;
import org.kurento.jsonrpc.Props;
@@ -56,13 +57,15 @@ public class JsonUtils {
public JsonElement fromFileToJsonElement(String filePath)
throws IOException, FileNotFoundException, JsonParseException, IllegalStateException {
+ return fromReaderToJsonElement(new FileReader(filePath));
+ }
+
+ public JsonObject fromReaderToJsonObject(Reader reader) throws IOException {
+ return this.fromReaderToJsonElement(reader).getAsJsonObject();
+ }
+
+ public JsonElement fromReaderToJsonElement(Reader reader) throws IOException {
JsonElement json = null;
- FileReader reader = null;
- try {
- reader = new FileReader(filePath);
- } catch (FileNotFoundException e) {
- throw e;
- }
try {
json = JsonParser.parseReader(reader);
} catch (JsonParseException | IllegalStateException exception) {
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/LocalCustomFileManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalCustomFileManager.java
new file mode 100644
index 00000000..5e45fb50
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalCustomFileManager.java
@@ -0,0 +1,41 @@
+package io.openvidu.server.utils;
+
+import java.io.File;
+
+public class LocalCustomFileManager extends CustomFileManager {
+
+ @Override
+ public void waitForFileToExistAndNotEmpty(String mediaNodeId, String absolutePathToFile) throws Exception {
+
+ // Check 10 times per seconds
+ int MAX_SECONDS_WAIT = this.maxSecondsWaitForFile();
+ int MILLISECONDS_INTERVAL_WAIT = 100;
+ int LIMIT = MAX_SECONDS_WAIT * 1000 / MILLISECONDS_INTERVAL_WAIT;
+ int i = 0;
+
+ boolean arePresent = fileExistsAndHasBytes(absolutePathToFile);
+ while (!arePresent && i < LIMIT) {
+ try {
+ Thread.sleep(MILLISECONDS_INTERVAL_WAIT);
+ arePresent = fileExistsAndHasBytes(absolutePathToFile);
+ i++;
+ } catch (InterruptedException e) {
+ throw new Exception("Interrupted exception while waiting for file " + absolutePathToFile + " to exist");
+ }
+ }
+ if (!arePresent) {
+ throw new Exception("File " + absolutePathToFile + " does not exist and hasn't been created in "
+ + MAX_SECONDS_WAIT + " seconds");
+ }
+ }
+
+ private boolean fileExistsAndHasBytes(String fileName) {
+ File f = new File(fileName);
+ return (f.exists() && f.isFile() && f.length() > 0);
+ }
+
+ public int maxSecondsWaitForFile() {
+ return 30;
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java
new file mode 100644
index 00000000..0b697b51
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java
@@ -0,0 +1,280 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package io.openvidu.server.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.ProcessingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.command.CreateContainerCmd;
+import com.github.dockerjava.api.command.CreateContainerResponse;
+import com.github.dockerjava.api.command.ExecCreateCmdResponse;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.command.InspectImageResponse;
+import com.github.dockerjava.api.exception.ConflictException;
+import com.github.dockerjava.api.exception.DockerClientException;
+import com.github.dockerjava.api.exception.InternalServerErrorException;
+import com.github.dockerjava.api.exception.NotFoundException;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.HostConfig;
+import com.github.dockerjava.api.model.Volume;
+import com.github.dockerjava.core.DefaultDockerClientConfig;
+import com.github.dockerjava.core.DockerClientBuilder;
+import com.github.dockerjava.core.DockerClientConfig;
+import com.github.dockerjava.core.command.ExecStartResultCallback;
+import com.github.dockerjava.core.command.PullImageResultCallback;
+
+import io.openvidu.client.OpenViduException;
+import io.openvidu.client.OpenViduException.Code;
+import io.openvidu.server.recording.service.WaitForContainerStoppedCallback;
+
+public class LocalDockerManager implements DockerManager {
+
+ private static final Logger log = LoggerFactory.getLogger(DockerManager.class);
+
+ private DockerClient dockerClient;
+
+ public LocalDockerManager(boolean init) {
+ if (init) {
+ this.init();
+ }
+ }
+
+ @Override
+ public DockerManager init() {
+ DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
+ this.dockerClient = DockerClientBuilder.getInstance(config).build();
+ return this;
+ }
+
+ public void downloadDockerImage(String image, int secondsOfWait) throws Exception {
+ try {
+ // Pull image
+ this.dockerClient.pullImageCmd(image).exec(new PullImageResultCallback()).awaitCompletion(secondsOfWait,
+ TimeUnit.SECONDS);
+
+ } catch (NotFoundException | InternalServerErrorException e) {
+ if (dockerImageExistsLocally(image)) {
+ log.info("Docker image '{}' exists locally", image);
+ } else {
+ throw e;
+ }
+ } catch (DockerClientException e) {
+ log.info("Error on Pulling '{}' image. Probably because the user has stopped the execution", image);
+ throw e;
+ } catch (InterruptedException e) {
+ log.info("Error on Pulling '{}' image. Thread was interrupted: {}", image, e.getMessage());
+ throw e;
+ }
+ }
+
+ public boolean dockerImageExistsLocally(String image) throws ProcessingException {
+ boolean imageExists = false;
+ try {
+ this.dockerClient.inspectImageCmd(image).exec();
+ imageExists = true;
+ } catch (NotFoundException nfe) {
+ imageExists = false;
+ } catch (ProcessingException e) {
+ throw e;
+ }
+ return imageExists;
+ }
+
+ public void checkDockerEnabled() throws OpenViduException {
+ try {
+ this.dockerImageExistsLocally("hello-world");
+ log.info("Docker is installed and enabled");
+ } catch (ProcessingException exception) {
+ throw new OpenViduException(Code.DOCKER_NOT_FOUND, "Exception connecting to Docker daemon");
+ }
+ }
+
+ @Override
+ public String runContainer(String mediaNodeId, String image, String containerName, String user,
+ List volumes, List binds, String networkMode, List envs, List command,
+ Long shmSize, boolean privileged, Map labels) throws Exception {
+
+ CreateContainerCmd cmd = dockerClient.createContainerCmd(image).withEnv(envs);
+ if (containerName != null) {
+ cmd.withName(containerName);
+ }
+
+ if (user != null) {
+ cmd.withUser(user);
+ }
+
+ HostConfig hostConfig = new HostConfig().withNetworkMode(networkMode).withPrivileged(privileged);
+ if (shmSize != null) {
+ hostConfig.withShmSize(shmSize);
+ }
+ if (volumes != null) {
+ cmd.withVolumes(volumes);
+ }
+ if (binds != null) {
+ hostConfig.withBinds(binds);
+ }
+
+ if (labels != null) {
+ cmd.withLabels(labels);
+ }
+
+ if (command != null) {
+ cmd.withCmd(command);
+ }
+
+ cmd.withHostConfig(hostConfig);
+
+ CreateContainerResponse response = null;
+ try {
+ response = cmd.exec();
+ dockerClient.startContainerCmd(response.getId()).exec();
+ log.info("Container ID: {}", response.getId());
+ return response.getId();
+ } catch (ConflictException e) {
+ log.error(
+ "The container name {} is already in use. Probably caused by a session with unique publisher re-publishing a stream",
+ containerName);
+ throw e;
+ } catch (NotFoundException e) {
+ log.error("Docker image {} couldn't be found in docker host", image);
+ throw e;
+ }
+ }
+
+ @Override
+ public void removeContainer(String mediaNodeId, String containerId, boolean force) {
+ dockerClient.removeContainerCmd(containerId).withForce(force).exec();
+ }
+
+ public void cleanStrandedContainers(String imageName) {
+ List existingContainers = this.dockerClient.listContainersCmd().withShowAll(true).exec();
+ for (Container container : existingContainers) {
+ if (container.getImage().startsWith(imageName)) {
+ log.info("Stranded {} Docker container ({}) removed on startup", imageName, container.getId());
+ this.dockerClient.removeContainerCmd(container.getId()).withForce(true).exec();
+ }
+ }
+ }
+
+ @Override
+ public void runCommandInContainerSync(String mediaNodeId, String containerId, String command, int secondsOfWait)
+ throws IOException {
+ ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
+ .withAttachStderr(true).withCmd("bash", "-c", command).exec();
+ CountDownLatch latch = new CountDownLatch(1);
+ dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback() {
+ @Override
+ public void onComplete() {
+ latch.countDown();
+ }
+ });
+ try {
+ latch.await(secondsOfWait, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IOException("Container " + containerId + " did not return from executing command \"" + command
+ + "\" in " + secondsOfWait + " seconds");
+ }
+ }
+
+ @Override
+ public void runCommandInContainerAsync(String mediaNodeId, String containerId, String command) throws IOException {
+ ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
+ .withAttachStderr(true).withCmd("bash", "-c", command).exec();
+ dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback() {
+ });
+ }
+
+ @Override
+ public void waitForContainerStopped(String mediaNodeId, String containerId, int secondsOfWait) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ WaitForContainerStoppedCallback callback = new WaitForContainerStoppedCallback(latch);
+ dockerClient.waitContainerCmd(containerId).exec(callback);
+ boolean stopped = false;
+ try {
+ stopped = latch.await(secondsOfWait, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw e;
+ }
+ if (!stopped) {
+ throw new Exception();
+ }
+ }
+
+ public String getContainerIp(String containerId) {
+ try {
+ return CommandExecutor.execCommand(5000, "/bin/sh", "-c",
+ "docker inspect -f \"{{ .NetworkSettings.IPAddress }}\" " + containerId);
+ } catch (IOException | InterruptedException e) {
+ log.error(e.getMessage());
+ return null;
+ }
+ }
+
+ public List getRunningContainers(String fullImageName) {
+ List containerIds = new ArrayList<>();
+ List existingContainers = this.dockerClient.listContainersCmd().exec();
+ for (Container container : existingContainers) {
+ if (container.getImage().startsWith(fullImageName)) {
+ containerIds.add(container.getId());
+ } else if (container.getImageId().contains(fullImageName)) {
+ containerIds.add(container.getId());
+ }
+ }
+ return containerIds;
+ }
+
+ public String getImageId(String fullImageName) {
+ InspectImageResponse imageResponse = this.dockerClient.inspectImageCmd(fullImageName).exec();
+ return imageResponse.getId();
+ }
+
+ public Map getLabels(String containerId) {
+ InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec();
+ return containerInfo.getConfig().getLabels();
+ }
+
+ public void close() {
+ try {
+ this.dockerClient.close();
+ } catch (IOException e) {
+ log.error("Error closing DockerClient: {}", e.getMessage());
+ }
+ }
+
+ static public String getDockerGatewayIp() {
+ try {
+ return CommandExecutor.execCommand(5000, "/bin/sh", "-c",
+ "docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}'");
+ } catch (IOException | InterruptedException e) {
+ log.error(e.getMessage());
+ return null;
+ }
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/RestUtils.java b/openvidu-server/src/main/java/io/openvidu/server/utils/RestUtils.java
new file mode 100644
index 00000000..66512440
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/RestUtils.java
@@ -0,0 +1,29 @@
+package io.openvidu.server.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+
+public class RestUtils {
+
+ private static final Logger log = LoggerFactory.getLogger(RestUtils.class);
+
+ public static HttpHeaders getResponseHeaders() {
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.setContentType(MediaType.APPLICATION_JSON);
+ return responseHeaders;
+ }
+
+ public static ResponseEntity getErrorResponse(String message, HttpStatus status) {
+ if (!status.is2xxSuccessful()) {
+ log.error(message);
+ }
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.setContentType(MediaType.TEXT_PLAIN);
+ return new ResponseEntity<>(message, responseHeaders, status);
+ }
+
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java b/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java
index bee5cbd6..6544f764 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java
@@ -1,48 +1,67 @@
+/*
+ * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
package io.openvidu.server.utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
+import io.openvidu.client.OpenViduException;
+import io.openvidu.java.client.VideoCodec;
+import io.openvidu.client.OpenViduException.Code;
+import io.openvidu.server.core.Participant;
+import io.openvidu.server.core.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import io.openvidu.client.OpenViduException;
-import io.openvidu.client.OpenViduException.Code;
-import io.openvidu.java.client.VideoCodec;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class SDPMunging {
-
+
private static final Logger log = LoggerFactory.getLogger(SDPMunging.class);
+ private Set supportedVideoCodecs = new HashSet<>(Arrays.asList(
+ VideoCodec.VP8,
+ VideoCodec.H264
+ ));
+
/**
- * `codec` is a uppercase SDP-style codec name: "VP8", "H264".
- *
- * This looks for all video m-sections (lines starting with "m=video"),
- * then searches all of its related PayloadTypes trying to find those which
- * correspond to the preferred codec. If any is found, they are moved to the
- * front of the PayloadTypes list in the m= line, without removing the other
- * codecs that might be present.
- *
- * If our preferred codec is not found, the m= line is left without changes.
- *
- * This works based on the basis that RFC 3264 "Offer/Answer Model SDP" section
- * 6.1 "Unicast Streams" allows the answerer to list media formats in a
- * different order of preference from what it got in the offer:
- *
- * > Although the answerer MAY list the formats in their desired order of
- * > preference, it is RECOMMENDED that unless there is a specific reason,
- * > the answerer list formats in the same relative order they were
- * > present in the offer.
- *
- * Here we have a specific reason, thus we use this allowance to change the
- * ordering of formats. Browsers (tested with Chrome 84) honor this change and
- * use the first codec provided in the answer, so this operation actually works.
- */
- public String setCodecPreference(VideoCodec codec, String sdp, boolean removeCodecs) throws OpenViduException {
+ * `codec` is a uppercase SDP-style codec name: "VP8", "H264".
+ *
+ * This looks for all video m-sections (lines starting with "m=video"),
+ * then searches all of its related PayloadTypes trying to find those which
+ * correspond to the preferred codec. If any is found, they are moved to the
+ * front of the PayloadTypes list in the m= line, without removing the other
+ * codecs that might be present.
+ *
+ * If our preferred codec is not found, the m= line is left without changes.
+ *
+ * This works based on the basis that RFC 3264 "Offer/Answer Model SDP" section
+ * 6.1 "Unicast Streams" allows the answerer to list media formats in a
+ * different order of preference from what it got in the offer:
+ *
+ * > Although the answerer MAY list the formats in their desired order of
+ * > preference, it is RECOMMENDED that unless there is a specific reason,
+ * > the answerer list formats in the same relative order they were
+ * > present in the offer.
+ *
+ * Here we have a specific reason, thus we use this allowance to change the
+ * ordering of formats. Browsers (tested with Chrome 84) honor this change and
+ * use the first codec provided in the answer, so this operation actually works.
+ */
+ public String setCodecPreference(VideoCodec codec, String sdp) throws OpenViduException {
String codecStr = codec.name();
log.info("[setCodecPreference] codec: {}", codecStr);
@@ -122,82 +141,57 @@ public class SDPMunging {
}
// Replace the original m= line with the one we just built.
- if (!removeCodecs) {
- // Add the rest of PayloadTypes.
- newLine.append(String.join(" ", lineParts));
- }
lines[sl] = newLine.toString().trim();
}
return String.join("\r\n", lines) + "\r\n";
}
-
- /**
- * Possible Kurento's bug
- * Some browsers can't use H264 as a video codec if in the offerer SDP
- * the parameter "a=fmtp: <...> profile-level-id=42e01f" is not defined.
- * This munging is only used when the forced codec needs to be H264
- * References:
- * https://stackoverflow.com/questions/38432137/cannot-establish-webrtc-connection-different-codecs-and-payload-type-in-sdp
+
+ /**
+ * Return a SDP modified to force a specific codec
*/
- public String setfmtpH264(String sdp) {
- String codecStr = VideoCodec.H264.name();
-
- // Get all lines
- List lines = new LinkedList(Arrays.asList(sdp.split("\\R+")));
-
- // Index to reference the line with "m=video"
- int mVideoLineIndex = -1;
- List validCodecsPayload = new ArrayList<>();
-
- for(int i = 0; i < lines.size(); i++) {
- String sdpLine = lines.get(i);
-
- // Check that we are in "m=video"
- if (sdpLine.startsWith("m=video")) {
- mVideoLineIndex = i;
-
- // Search for payload-type for the specified codec
- for(int j = i+1; j < lines.size(); j++) {
- String auxLine = lines.get(j);
-
- // Check that the line we're going to analizae is not anoter "m="
- if(auxLine.startsWith("m=")) {
- break;
- }
-
- if (auxLine.startsWith("a=rtpmap")) {
- String[] rtpmapInfo = auxLine.split(":")[1].split(" ");
- String possiblePayload = rtpmapInfo[0];
- String possibleCodec = rtpmapInfo[1];
- if (possibleCodec.contains(codecStr)) {
- validCodecsPayload.add(possiblePayload);
- }
- }
- }
- // If a payload is not found, then the codec is not in the SDP
- if (validCodecsPayload.size() == 0) {
- throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, String.format("Codec %s not found", codecStr));
- }
- continue;
+ public String forceCodec(Participant participant, String sdp, boolean isOffer, Session session, boolean isPublisher,
+ boolean isReconnecting, boolean isTranscodingAllowed, VideoCodec forcedVideoCodec) throws OpenViduException {
+ try {
+ if (supportedVideoCodecs.contains(forcedVideoCodec)) {
+ String mungedSdpOffer;
+
+ log.debug("PARTICIPANT '{}' in Session '{}'. Is Publisher: '{}'. Is Subscriber: '{}'."
+ + " Is Offer SDP: '{}'. Is Answer SDP: '{}'. Is Reconnecting '{}'."
+ + " SDP before munging: \n {}", participant.getParticipantPublicId(),
+ session.getSessionId(), isPublisher, !isPublisher, isOffer, !isOffer, isReconnecting, sdp);
+
+ mungedSdpOffer = this.setCodecPreference(forcedVideoCodec, sdp);
+
+ log.debug("PARTICIPANT '{}' in Session '{}'. Is Publisher: '{}'. Is Subscriber: '{}'."
+ + " Is Offer SDP: '{}'. Is Answer SDP: '{}'. Is Reconnecting '{}'."
+ + " SDP after munging: \n {}", participant.getParticipantPublicId(),
+ session.getSessionId(), isPublisher, !isPublisher, isOffer, !isOffer, isReconnecting, mungedSdpOffer);
+
+ return mungedSdpOffer;
+ } else {
+ throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, "Codec not supported by Media Server");
}
-
+
+ } catch (OpenViduException e) {
+
+ String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
+ + participant.getParticipantPublicId() + "' in Session: '" + session.getSessionId()
+ + "'. Is publishing: '" + isPublisher + "'. Is Subscriber: '" + !isPublisher
+ + "'. Is Offer: '" + isOffer + "'. Is Answer: '" + !isOffer + "'. Is Reconnecting: '"
+ + isReconnecting + "'.\nException: " + e.getMessage() + "\nSDP:\n" + sdp;
+
+ if(!isTranscodingAllowed) {
+ throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage);
+ }
+
+ log.info("Codec: '{}' is not supported for PARTICIPANT: '{}' in Session: '{}'. Is publishing: '{}'. "
+ + "Is Subscriber: '{}' Is Offer SDP: '{}'. Is Answer SDP: '{}'. Is Reconnecting: '{}'."
+ + " Transcoding will be allowed", forcedVideoCodec, participant.getParticipantPublicId(),
+ session.getSessionId(), isPublisher, !isPublisher, isOffer, !isOffer, isReconnecting);
+
+ return sdp;
}
- if (mVideoLineIndex == -1) {
- throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, "This SDP does not offer video");
- } else {
- for (String codecPayload: validCodecsPayload) {
- if (!sdp.contains(String.format("a=fmtp:%s", codecPayload))) {
- String newfmtpLine = String.format("a=fmtp:%s level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", codecPayload);
- lines.add(mVideoLineIndex + 1, newfmtpLine);
- }
- }
- }
-
- // Return munging sdp!!
- String[] munguedSdpLines = lines.toArray(new String[lines.size()]);
- return String.join("\r\n", munguedSdpLines) + "\r\n";
-
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/UpdatableTimerTask.java b/openvidu-server/src/main/java/io/openvidu/server/utils/UpdatableTimerTask.java
index d94122e5..6a59836b 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/utils/UpdatableTimerTask.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/utils/UpdatableTimerTask.java
@@ -67,7 +67,7 @@ public class UpdatableTimerTask extends TimerTask {
try {
task.run();
} catch (Exception e) {
- log.error("Exception running UpdatableTimerTask: {}", e.getMessage());
+ log.error("Exception running UpdatableTimerTask: {} - {}", e.getMessage(), e.getStackTrace());
}
updateTimer();
}
diff --git a/openvidu-server/src/main/java/io/openvidu/server/webhook/CDRLoggerWebhook.java b/openvidu-server/src/main/java/io/openvidu/server/webhook/CDRLoggerWebhook.java
index 3aa670e5..acca20ae 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/webhook/CDRLoggerWebhook.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/webhook/CDRLoggerWebhook.java
@@ -17,9 +17,13 @@
package io.openvidu.server.webhook;
+import java.util.List;
+
+import org.apache.http.Header;
+
import io.openvidu.server.cdr.CDREvent;
+import io.openvidu.server.cdr.CDREventName;
import io.openvidu.server.cdr.CDRLogger;
-import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.kurento.endpoint.KmsEvent;
import io.openvidu.server.summary.SessionSummary;
@@ -27,9 +31,8 @@ public class CDRLoggerWebhook implements CDRLogger {
private HttpWebhookSender webhookSender;
- public CDRLoggerWebhook(OpenviduConfig openviduConfig) {
- this.webhookSender = new HttpWebhookSender(openviduConfig.getOpenViduWebhookEndpoint(),
- openviduConfig.getOpenViduWebhookHeaders(), openviduConfig.getOpenViduWebhookEvents());
+ public CDRLoggerWebhook(String webhookEndpoint, List webhookHeaders, List webhookEvents) {
+ this.webhookSender = new HttpWebhookSender(webhookEndpoint, webhookHeaders, webhookEvents);
}
@Override
diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 9448757d..04a0f0f6 100644
--- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -93,6 +93,12 @@
"description": "URL the composed-video recording dockerized Chrome will use to connect to the recording layouts inside OpenVidu Server host. This will affect all video recording layouts (default one BEST_FIT, all CUSTOM layouts). This allows changing the default URL, which is the public URL 'https://DOMAIN_OR_PUBLIC_IP:HTTPS_PORT/', for those cases where OpenVidu Server host does not allow back and forth connections using the public url from inside the host",
"defaultValue": ""
},
+ {
+ "name": "OPENVIDU_RECORDING_COMPOSED_BASICAUTH",
+ "type": "java.lang.Boolean",
+ "description": "'true' to automatically add Basic Auth credentials to the URL passed to the recording container, false otherwise",
+ "defaultValue": true
+ },
{
"name": "OPENVIDU_WEBHOOK",
"type": "java.lang.Boolean",
@@ -141,6 +147,18 @@
"description": "Minimum video bandwidth sent from OpenVidu Server to clients, in kbps. 0 means unconstrained",
"defaultValue": 300
},
+ {
+ "name": "OPENVIDU_STREAMS_FORCED_VIDEO_CODEC",
+ "type": "java.lang.String",
+ "description": "Defines which video codec is being forced to be used in the browser/client",
+ "defaultValue": "VP8"
+ },
+ {
+ "name": "OPENVIDU_STREAMS_ALLOW_TRANSCODING",
+ "type": "java.lang.Boolean",
+ "description": "Defines if transcoding is allowed or not when OPENVIDU_STREAMS_FORCED_VIDEO_CODEC is not a compatible codec with the browser/client.",
+ "defaultValue": false
+ },
{
"name": "OPENVIDU_SESSIONS_GARBAGE_INTERVAL",
"type": "java.lang.Integer",
diff --git a/openvidu-server/src/main/resources/OpenVidu.postman_collection.json b/openvidu-server/src/main/resources/OpenVidu.postman_collection.json
new file mode 100644
index 00000000..86399bcd
--- /dev/null
+++ b/openvidu-server/src/main/resources/OpenVidu.postman_collection.json
@@ -0,0 +1,1120 @@
+{
+ "info": {
+ "_postman_id": "b2fbd114-3827-4117-8281-a16afaf81dcc",
+ "name": "OpenVidu REST API",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "POST Session",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mediaMode\": \"ROUTED\",\n \"recordingMode\": \"MANUAL\",\n \"customSessionId\": \"TestSession\",\n \"defaultOutputMode\": \"COMPOSED\",\n \"defaultRecordingLayout\": \"BEST_FIT\",\n \"defaultCustomLayout\": \"CUSTOM_LAYOUT\"\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Session/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID?webRtcStats=false&pendingConnections=true",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID"
+ ],
+ "query": [
+ {
+ "key": "webRtcStats",
+ "value": "false"
+ },
+ {
+ "key": "pendingConnections",
+ "value": "true"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Sessions",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions?webRtcStats=false&pendingConnections=true",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions"
+ ],
+ "query": [
+ {
+ "key": "webRtcStats",
+ "value": "false"
+ },
+ {
+ "key": "pendingConnections",
+ "value": "true"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "DELETE Session/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Connection",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"type\": \"asdf\",\n \"role\": \"PUBLISHER\",\n \"data\": \"SERVER_DATA\",\n \"record\": true,\n \"kurentoOptions\": {\n \"videoMaxRecvBandwidth\": 1000,\n \"videoMinRecvBandwidth\": 300,\n \"videoMaxSendBandwidth\": 1000,\n \"videoMinSendBandwidth\": 300,\n \"allowedFilters\": [\n \"GStreamerFilter\",\n \"ZBarFilter\"\n ]\n },\n \"rtspUri\": \"rtsp://your.camera.ip.sdp\",\n \"adaptativeBitrate\": true,\n \"onlyPlayWithSubscribers\": true,\n \"networkCache\": 2000\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/TestSession/connection",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "TestSession",
+ "connection"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Connection/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID/connection/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID",
+ "connection",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Connections",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID/connection",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID",
+ "connection"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "PATCH Connection",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PATCH",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"role\": \"MODERATOR\",\n \"record\": false\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/TestSession/connection/con_SKoAiag7OO",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "TestSession",
+ "connection",
+ "con_SKoAiag7OO"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "DELETE Connection/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID/connection/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID",
+ "connection",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Recording Start",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"session\":\"TestSession\",\n \"name\":\"MyRecording\",\n \"outputMode\":\"COMPOSED\",\n \"hasAudio\": true,\n \"hasVideo\": true,\n \"recordingLayout\":\"BEST_FIT\",\n \"customLayout\":\"\",\n \"resolution\": \"1280x720\"\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/recordings/start",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "recordings",
+ "start"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Recording Stop",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/recordings/stop/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "recordings",
+ "stop",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Recording/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/recordings/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "recordings",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Recordings",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/recordings",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "recordings"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "DELETE Recording",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/recordings/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "recordings",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET MediaNode/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes/ID?sessions=true&extra-info=false",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes",
+ "ID"
+ ],
+ "query": [
+ {
+ "key": "sessions",
+ "value": "true"
+ },
+ {
+ "key": "extra-info",
+ "value": "false"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET MediaNodes",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes?sessions=true&extra-info=false",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes"
+ ],
+ "query": [
+ {
+ "key": "sessions",
+ "value": "true"
+ },
+ {
+ "key": "extra-info",
+ "value": "false"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Media Node",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes?wait=true",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes"
+ ],
+ "query": [
+ {
+ "key": "wait",
+ "value": "true"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "DELETE Media Node",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes/ID?wait=true&deletion-strategy=when-no-sessions",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes",
+ "ID"
+ ],
+ "query": [
+ {
+ "key": "wait",
+ "value": "true"
+ },
+ {
+ "key": "deletion-strategy",
+ "value": "when-no-sessions"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "PATCH Media Node",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PATCH",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"status\": \"terminating\"\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes/kms_LmVJiQpm",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes",
+ "kms_LmVJiQpm"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "PUT Media Nodes",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/media-nodes",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "media-nodes"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Token",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"session\": \"TestSession\",\n \"role\": \"PUBLISHER\",\n \"data\": \"SERVER_DATA\",\n \"record\": true,\n \"kurentoOptions\": {\n \"videoMaxRecvBandwidth\": 1000,\n \"videoMinRecvBandwidth\": 300,\n \"videoMaxSendBandwidth\": 1000,\n \"videoMinSendBandwidth\": 300,\n \"allowedFilters\": [ \"GStreamerFilter\", \"ZBarFilter\" ]\n }\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/tokens",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "tokens"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "DELETE Stream/ID",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/sessions/ID/stream/ID",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "sessions",
+ "ID",
+ "stream",
+ "ID"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Signal",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"session\":\"TestSession\",\n \"to\": [],\n \"type\":\"MY_TYPE\",\n \"data\":\"This is my signal data\"\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/signal",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "signal"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Config",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/config",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "config"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "GET Health",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/health",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "health"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "POST Restart",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "MY_SECRET",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "OPENVIDUAPP",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "type": "text",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"OPENVIDU_SECRET\":\"MY_SECRET\",\n \"OPENVIDU_CDR\":true,\n \"OPENVIDU_RECORDING\":true,\n \"OPENVIDU_RECORDING_PUBLIC_ACCESS\":true,\n \"OPENVIDU_RECORDING_NOTIFICATION\":\"publisher_moderator\",\n \"OPENVIDU_RECORDING_PATH\":\"/opt/openvidu/recordings\",\n \"OPENVIDU_RECORDING_CUSTOM_LAYOUT\":\"/opt/openvidu/custom-layout\",\n \"OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT\":120,\n \"OPENVIDU_WEBHOOK\":false,\n \"OPENVIDU_WEBHOOK_ENDPOINT\":\"http://localhost:7777/webhook/\",\n \"OPENVIDU_WEBHOOK_HEADERS\":[\n \"Authorization: Basic T1BFTlZJRFVBUFA6TVlfU0VDUkVU\"\n ],\n \"OPENVIDU_WEBHOOK_EVENTS\":[\n \"recordingStatusChanged\"\n ],\n \"OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH\":1000,\n \"OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH\":300,\n \"OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH\":1000,\n \"OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH\":300,\n \"OPENVIDU_SESSIONS_GARBAGE_INTERVAL\":900,\n \"OPENVIDU_SESSIONS_GARBAGE_THRESHOLD\":3600,\n \"OPENVIDU_PRO_STATS_MONITORING_INTERVAL\":30,\n \"OPENVIDU_PRO_STATS_WEBRTC_INTERVAL\":20\n}"
+ },
+ "url": {
+ "raw": "https://localhost:4443/openvidu/api/restart",
+ "protocol": "https",
+ "host": [
+ "localhost"
+ ],
+ "port": "4443",
+ "path": [
+ "openvidu",
+ "api",
+ "restart"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "protocolProfileBehavior": {}
+}
\ No newline at end of file
diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties
index 2ae45ad0..560e41fb 100644
--- a/openvidu-server/src/main/resources/application.properties
+++ b/openvidu-server/src/main/resources/application.properties
@@ -8,6 +8,8 @@ server.ssl.key-alias=openvidu-selfsigned
logging.level.root=info
spring.main.allow-bean-definition-overriding=true
+SUPPORT_DEPRECATED_API=true
+
DOTENV_PATH=.
DOMAIN_OR_PUBLIC_IP=
@@ -22,22 +24,25 @@ OPENVIDU_CDR_PATH=/opt/openvidu/cdr
OPENVIDU_WEBHOOK=false
OPENVIDU_WEBHOOK_ENDPOINT=
OPENVIDU_WEBHOOK_HEADERS=[]
-OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","mediaNodeStatusChanged"]
+OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","signalSent","mediaNodeStatusChanged","autoscaling","mediaServerCrashed"]
OPENVIDU_RECORDING=false
OPENVIDU_RECORDING_DEBUG=false
-OPENVIDU_RECORDING_VERSION=2.15.0
+OPENVIDU_RECORDING_VERSION=2.16.0
OPENVIDU_RECORDING_PATH=/opt/openvidu/recordings
OPENVIDU_RECORDING_PUBLIC_ACCESS=false
OPENVIDU_RECORDING_NOTIFICATION=publisher_moderator
OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/custom-layout
OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120
OPENVIDU_RECORDING_COMPOSED_URL=
+OPENVIDU_RECORDING_COMPOSED_BASICAUTH=true
OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
+OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
+OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java
index 6b1527c8..8b49bdc6 100644
--- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java
+++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java
@@ -18,7 +18,6 @@
package io.openvidu.server.test.integration;
import java.util.HashMap;
-import java.util.Map;
import java.util.UUID;
import org.junit.Assert;
@@ -36,7 +35,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
-import io.openvidu.java.client.OpenViduRole;
+import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.server.core.Participant;
import io.openvidu.server.core.SessionManager;
import io.openvidu.server.core.Token;
@@ -98,24 +97,24 @@ public class SessionGarbageCollectorIntegrationTest {
}
private String getSessionId() {
- String stringResponse = (String) sessionRestController.getSessionId(new HashMap<>()).getBody();
+ String stringResponse = (String) sessionRestController.initializeSession(new HashMap<>()).getBody();
return new Gson().fromJson(stringResponse, JsonObject.class).get("id").getAsString();
}
private String getToken(String sessionId) {
- Map map = new HashMap<>();
- map.put("session", sessionId);
- String stringResponse = (String) sessionRestController.newToken(map).getBody();
+ String stringResponse = (String) sessionRestController.initializeConnection(sessionId, new HashMap<>())
+ .getBody();
return new Gson().fromJson(stringResponse, JsonObject.class).get("token").getAsString();
}
private JsonObject listSessions() {
- String stringResponse = (String) sessionRestController.listSessions(false).getBody();
+ String stringResponse = (String) sessionRestController.listSessions(false, false).getBody();
return new Gson().fromJson(stringResponse, JsonObject.class);
}
private void joinParticipant(String sessionId, String token) {
- Token t = new Token(token, OpenViduRole.PUBLISHER, "SERVER_METADATA", null, null);
+ ConnectionProperties connectionProperties = new ConnectionProperties.Builder().data("SERVER_METADATA").build();
+ Token t = new Token(token, sessionId, connectionProperties, null);
String uuid = UUID.randomUUID().toString();
String participantPrivateId = "PARTICIPANT_PRIVATE_ID_" + uuid;
String finalUserId = "FINAL_USER_ID_" + uuid;
diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java
index 02dc465d..c55fa4d0 100644
--- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java
+++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java
@@ -14,9 +14,11 @@ import org.kurento.client.KurentoClient;
import org.kurento.client.MediaPipeline;
import org.kurento.client.ServerManager;
import org.mockito.Mockito;
+import org.powermock.reflect.Whitebox;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
+import io.openvidu.server.kurento.core.KurentoSessionManager;
import io.openvidu.server.kurento.kms.FixedOneKmsManager;
import io.openvidu.server.kurento.kms.Kms;
import io.openvidu.server.kurento.kms.KmsManager;
@@ -32,12 +34,13 @@ public class IntegrationTestConfiguration {
@Bean
public KmsManager kmsManager() throws Exception {
- final KmsManager spy = Mockito.spy(new FixedOneKmsManager());
+ final KmsManager spy = Mockito.spy(new FixedOneKmsManager(new KurentoSessionManager()));
doAnswer(invocation -> {
List successfullyConnectedKmss = new ArrayList<>();
List kmsProperties = invocation.getArgument(0);
for (KmsProperties kmsProp : kmsProperties) {
- Kms kms = new Kms(kmsProp, spy.getLoadManager());
+ Kms kms = new Kms(kmsProp, Whitebox.getInternalState(spy, "loadManager"),
+ Whitebox.getInternalState(spy, "quarantineKiller"));
KurentoClient kClient = mock(KurentoClient.class);
doAnswer(i -> {
diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/unit/SDPMungingTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/unit/SDPMungingTest.java
index c0beff51..4b3d4ac3 100644
--- a/openvidu-server/src/test/java/io/openvidu/server/test/unit/SDPMungingTest.java
+++ b/openvidu-server/src/test/java/io/openvidu/server/test/unit/SDPMungingTest.java
@@ -22,61 +22,39 @@ import io.openvidu.server.utils.SDPMunging;
public class SDPMungingTest {
private SDPMunging sdpMungin = new SDPMunging();
-
+
private String oldSdp;
-
+
private String newSdp;
List h264codecPayloads;
-
+
List forceCodecPayloads;
-
+
String validSDPH264Files[] = new String[]{
- "sdp_kurento_h264_with_profile_id.txt",
- "sdp_kurento_h264_without_profile_id_1.txt",
- "sdp_chrome84.txt",
- "sdp_firefox79.txt",
- "sdp_safari13-1.txt"
+ "sdp_kurento_h264.txt",
+ "sdp_chrome84.txt",
+ "sdp_firefox79.txt",
+ "sdp_safari13-1.txt"
};
-
+
String validSDPVP8Files[] = new String[]{
- "sdp_kurento_h264_with_profile_id.txt",
- "sdp_kurento_h264_without_profile_id_1.txt",
- "sdp_chrome84.txt",
- "sdp_firefox79.txt",
- "sdp_safari13-1.txt"
+ "sdp_kurento_h264.txt",
+ "sdp_chrome84.txt",
+ "sdp_firefox79.txt",
+ "sdp_safari13-1.txt"
};
-
+
String validSDPVP9Files[] = new String[] {
- "sdp_chrome84.txt",
- "sdp_firefox79.txt"
+ "sdp_chrome84.txt",
+ "sdp_firefox79.txt"
};
-
+
String notValidVP9Files[] = new String[] {
- "sdp_kurento_h264_with_profile_id.txt",
- "sdp_kurento_h264_without_profile_id_1.txt",
- "sdp_safari13-1.txt"
+ "sdp_kurento_h264.txt",
+ "sdp_safari13-1.txt"
};
-
- @Test
- @DisplayName("[setfmtpH264] Compare 'profile-level-id' ocurrences with Payload-Type H264 ocurrences")
- public void checkSDPOcurrenciesOfProfileIdForH264() throws IOException {
- for(String sdpFileName: validSDPH264Files) {
- initTestsSetfmtp(sdpFileName);
- checkOcurrences();
- }
- }
-
- @Test
- @DisplayName("[setfmtpH264] In the generated SDP for each H264 Payload-Type, exists one" +
- "'a=fmtp:' with a 'profile-level-id' defined")
- public void checkOneProfileIdForEachH264Payload() throws IOException {
- for(String sdpFileName: validSDPH264Files) {
- initTestsSetfmtp(sdpFileName);
- checkOneProfileIdForEachH264();
- }
- }
-
+
@Test
@DisplayName("[setCodecPreference] Force VP8 Codec prevalence in 'm=video' line")
public void checkPreferenceCodecVP8() throws IOException {
@@ -85,7 +63,6 @@ public class SDPMungingTest {
checkPrevalenceCodecInML();
}
}
-
@Test
@DisplayName("[setCodecPreference] Force VP8 Codec prevalence in 'm=video' line")
public void checkPreferenceCodecVP9() throws IOException {
@@ -94,7 +71,6 @@ public class SDPMungingTest {
checkPrevalenceCodecInML();
}
}
-
@Test
@DisplayName("[setCodecPreference] Force H264 Codec prevalence in 'm=video' line")
public void checkPreferenceCodecH264() throws IOException {
@@ -103,7 +79,6 @@ public class SDPMungingTest {
checkPrevalenceCodecInML();
}
}
-
@Test
@DisplayName("[setCodecPreference] Exception when codec does not exists on SDP")
public void checkPreferenceCodecException() throws IOException {
@@ -113,45 +88,21 @@ public class SDPMungingTest {
});
String expectedMessage = "The specified forced codec VP9 is not present in the SDP";
assertTrue(exception.getMessage().contains(expectedMessage));
- }
+ }
}
-
+
private String getSdpFile(String sdpNameFile) throws IOException {
- Path sdpFile = Files.createTempFile("sdp-test", ".tmp");
+ Path sdpFile = Files.createTempFile("sdp-test", ".tmp");
Files.copy(getClass().getResourceAsStream("/sdp/" + sdpNameFile), sdpFile, StandardCopyOption.REPLACE_EXISTING);
String sdpUnformatted = new String(Files.readAllBytes(sdpFile));
return String.join("\r\n", sdpUnformatted.split("\\R+")) + "\r\n";
}
-
- private void checkOcurrences() {
- // Check that new sdp actually contains a line with a=fmtp for
- // each h264 defined payload
- int allOcurrences = 0;
- for(String h264pt: this.h264codecPayloads) {
- allOcurrences += ( newSdp.split("m=video")[1].split("a=fmtp:" + h264pt).length ) - 1;
- }
- assertEquals(allOcurrences, h264codecPayloads.size());
- }
-
- private void initTestsSetfmtp(String sdpNameFile) throws IOException {
- this.oldSdp = getSdpFile(sdpNameFile);
- this.newSdp = this.sdpMungin.setfmtpH264(oldSdp);
- this.h264codecPayloads = new ArrayList<>();
-
- // Get all Payload-Type for h264
- for(String oldSdpLine: oldSdp.split("\\R+")) {
- if(oldSdpLine.startsWith("a=rtpmap") && oldSdpLine.endsWith("H264/90000")) {
- String pt = oldSdpLine.split(":")[1].split(" ")[0];
- this.h264codecPayloads.add(pt);
- }
- }
- }
-
+
private void initTestsSetCodecPrevalence(VideoCodec codec, String sdpNameFile) throws IOException {
this.oldSdp = getSdpFile(sdpNameFile);
- this.newSdp = this.sdpMungin.setCodecPreference(codec, oldSdp, true);
+ this.newSdp = this.sdpMungin.setCodecPreference(codec, oldSdp);
this.forceCodecPayloads = new ArrayList<>();
-
+
// Get all Payload-Type for video Codec
for(String oldSdpLine: oldSdp.split("\\R+")) {
if(oldSdpLine.startsWith("a=rtpmap") && oldSdpLine.endsWith(codec.name() + "/90000")) {
@@ -159,7 +110,6 @@ public class SDPMungingTest {
this.forceCodecPayloads.add(pt);
}
}
-
// Get all Payload-Types rtx related with codec
// Not the best way to do it, but enough to check if the sdp
// generated is correct
@@ -177,44 +127,14 @@ public class SDPMungingTest {
}
}
}
- }
+ }
}
}
this.forceCodecPayloads.addAll(rtxForcedCodecs);
}
-
- private void checkOneProfileIdForEachH264() throws IOException {
-
- // Check one profile-id for each h264 Payload-Type
- boolean inVideoBlock = false;
- int numFoundProfileIds = 0;
- for(String newSdpLine: newSdp.split("\\R+")) {
- if (!inVideoBlock && newSdpLine.startsWith("m=video")) {
- inVideoBlock = true;
- continue;
- }
-
- if (inVideoBlock && newSdpLine.startsWith("m=")) {
- break;
- }
-
- if (inVideoBlock && newSdpLine.startsWith("a=fmtp:")) {
- boolean foundProfileId = false;
- for(String h264pt: this.h264codecPayloads) {
- foundProfileId = newSdpLine.contains("a=fmtp:")
- && newSdpLine.contains(h264pt) && newSdpLine.contains("profile-level-id");
-
- if (foundProfileId) {
- numFoundProfileIds++;
- }
- }
- }
- }
- assertEquals(numFoundProfileIds, this.h264codecPayloads.size());
- }
-
+
private void checkPrevalenceCodecInML() {
-
+
String newml = null;
String[] newSdpLines = this.newSdp.split("\\R+");
for(String newSdpLine: newSdpLines) {
@@ -223,11 +143,11 @@ public class SDPMungingTest {
break;
}
}
-
+
if (newml == null) {
fail("'m=video' line not found in SDP");
}
-
+
List newMlCodecPrevalenceList = new ArrayList<>();
String[] lmParams = newml.split(" ");
int numOfCodecsWithPrevalence = this.forceCodecPayloads.size();
@@ -236,7 +156,6 @@ public class SDPMungingTest {
for(int i = indexStartCodecs; i < indexEndPreferencedCodecs; i++) {
newMlCodecPrevalenceList.add(lmParams[i]);
}
-
for(int j = 0; j < numOfCodecsWithPrevalence; j++) {
String codecToCheck = newMlCodecPrevalenceList.get(j);
boolean codecFoundInPrevalenceList = false;
diff --git a/openvidu-server/src/test/resources/integration-test.properties b/openvidu-server/src/test/resources/integration-test.properties
index 227addf0..9c9b57e6 100644
--- a/openvidu-server/src/test/resources/integration-test.properties
+++ b/openvidu-server/src/test/resources/integration-test.properties
@@ -24,8 +24,7 @@ OPENVIDU_WEBHOOK_HEADERS=[]
OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","mediaNodeStatusChanged"]
OPENVIDU_RECORDING=false
-OPENVIDU_RECORDING_DEBUG=false
-OPENVIDU_RECORDING_VERSION=2.15.0
+OPENVIDU_RECORDING_VERSION=2.16.0
OPENVIDU_RECORDING_PATH=/opt/openvidu/recordings
OPENVIDU_RECORDING_PUBLIC_ACCESS=false
OPENVIDU_RECORDING_NOTIFICATION=publisher_moderator
@@ -37,6 +36,8 @@ OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
+OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8
+OPENVIDU_STREAMS_ALLOW_TRANSCODING=false
OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
diff --git a/openvidu-server/src/test/resources/sdp/sdp_kurento_h264.txt b/openvidu-server/src/test/resources/sdp/sdp_kurento_h264.txt
new file mode 100644
index 00000000..18d81183
--- /dev/null
+++ b/openvidu-server/src/test/resources/sdp/sdp_kurento_h264.txt
@@ -0,0 +1,61 @@
+v=0
+o=- 3808465464 3808465464 IN IP4 0.0.0.0
+s=Kurento Media Server
+c=IN IP4 0.0.0.0
+t=0 0
+a=msid-semantic: WMS m0W2gMak7LgkgzgJeDQhxBX0ivcsejWjQ0jD
+a=group:BUNDLE 0 1
+m=audio 1 UDP/TLS/RTP/SAVPF 111 0
+a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
+a=recvonly
+a=mid:0
+a=rtcp:9 IN IP4 0.0.0.0
+a=rtpmap:111 opus/48000/2
+a=rtpmap:0 PCMU/8000
+a=setup:active
+a=rtcp-mux
+a=fmtp:111 minptime=10;useinbandfec=1
+a=ssrc:1929271881 cname:user4129876135@host-ed881df6
+a=ice-ufrag:cXmf
+a=ice-pwd:9giZcfpsuoHRuxCgbnCLRy
+a=fingerprint:sha-256 C8:D4:B5:56:A7:89:E5:E1:C8:28:0A:47:2B:49:F6:7A:E2:2E:B3:0A:40:10:AD:79:82:E7:FD:A0:ED:6C:F6:51
+m=video 1 UDP/TLS/RTP/SAVPF 96 102 127 125 108
+a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
+a=recvonly
+a=mid:1
+a=rtcp:9 IN IP4 0.0.0.0
+a=rtpmap:96 VP8/90000
+a=rtpmap:102 H264/90000
+a=rtpmap:127 H264/90000
+a=rtpmap:125 H264/90000
+a=rtpmap:108 H264/90000
+a=rtcp-fb:96 goog-remb
+a=rtcp-fb:96 ccm fir
+a=rtcp-fb:96 nack
+a=rtcp-fb:96 nack pli
+a=rtcp-fb:102 goog-remb
+a=rtcp-fb:102 ccm fir
+a=rtcp-fb:102 nack
+a=rtcp-fb:102 nack pli
+a=rtcp-fb:127 goog-remb
+a=rtcp-fb:127 ccm fir
+a=rtcp-fb:127 nack
+a=rtcp-fb:127 nack pli
+a=rtcp-fb:125 goog-remb
+a=rtcp-fb:125 ccm fir
+a=rtcp-fb:125 nack
+a=rtcp-fb:125 nack pli
+a=rtcp-fb:108 goog-remb
+a=rtcp-fb:108 ccm fir
+a=rtcp-fb:108 nack
+a=rtcp-fb:108 nack pli
+a=setup:active
+a=rtcp-mux
+a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
+a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
+a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
+a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
+a=ssrc:3762875210 cname:user4129876135@host-ed881df6
+a=ice-ufrag:cXmf
+a=ice-pwd:9giZcfpsuoHRuxCgbnCLRy
+a=fingerprint:sha-256 C8:D4:B5:56:A7:89:E5:E1:C8:28:0A:47:2B:49:F6:7A:E2:2E:B3:0A:40:10:AD:79:82:E7:FD:A0:ED:6C:F6:51
\ No newline at end of file
diff --git a/openvidu-test-browsers/pom.xml b/openvidu-test-browsers/pom.xml
index 25519eba..77e2d039 100644
--- a/openvidu-test-browsers/pom.xml
+++ b/openvidu-test-browsers/pom.xml
@@ -47,8 +47,8 @@
-
- 1.8
+
+ 11
${java.version}
${java.version}
@@ -77,16 +77,6 @@
selenium-java
${version.selenium}
-
- org.seleniumhq.selenium
- selenium-chrome-driver
- ${version.selenium}
-
-
- org.seleniumhq.selenium
- selenium-firefox-driver
- ${version.selenium}
-
com.google.code.gson
gson
@@ -97,6 +87,21 @@
unirest-java
${version.unirest}
+
+ org.jcodec
+ jcodec-javase
+ ${version.jcodec}
+
+
+ io.openvidu
+ openvidu-java-client
+ ${version.openvidu.java.client}
+
+
+ junit
+ junit
+ ${version.junit}
+
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/BrowserUser.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/BrowserUser.java
index bfe491b8..8b38e509 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/BrowserUser.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/BrowserUser.java
@@ -17,7 +17,11 @@
package io.openvidu.test.browsers;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.LoggerFactory;
@@ -64,4 +68,10 @@ public class BrowserUser {
this.driver.quit();
}
+ public void waitWithNewTime(int newWaitTime, ExpectedCondition condition) {
+ this.waiter.withTimeout(Duration.of(newWaitTime, ChronoUnit.SECONDS));
+ this.waiter.until(condition);
+ this.waiter.withTimeout(Duration.of(this.timeOfWaitInSeconds, ChronoUnit.SECONDS));
+ }
+
}
\ No newline at end of file
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/ChromeUser.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/ChromeUser.java
index b6fe64e1..3ffca920 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/ChromeUser.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/ChromeUser.java
@@ -51,7 +51,7 @@ public class ChromeUser extends BrowserUser {
options.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.IGNORE);
options.addArguments("--disable-infobars");
- options.setExperimentalOption("excludeSwitches", new String[]{"enable-automation"});
+ options.setExperimentalOption("excludeSwitches", new String[] { "enable-automation" });
Map prefs = new HashMap();
prefs.put("profile.default_content_setting_values.media_stream_mic", 1);
@@ -84,6 +84,9 @@ public class ChromeUser extends BrowserUser {
// This flag selects the entire screen as video source when screen sharing
options.addArguments("--auto-select-desktop-capture-source=Entire screen");
+ // Background Chrome
+ // options.addArguments("--headless");
+
if (runningAsRoot) {
options.addArguments("--no-sandbox");
}
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/FirefoxUser.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/FirefoxUser.java
index 6c3b39b2..76c2d216 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/FirefoxUser.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/FirefoxUser.java
@@ -22,6 +22,7 @@ import java.net.URL;
import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
@@ -29,7 +30,7 @@ import org.openqa.selenium.remote.RemoteWebDriver;
public class FirefoxUser extends BrowserUser {
- public FirefoxUser(String userName, int timeOfWaitInSeconds) {
+ public FirefoxUser(String userName, int timeOfWaitInSeconds, boolean disableOpenH264) {
super(userName, timeOfWaitInSeconds);
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
@@ -42,6 +43,10 @@ public class FirefoxUser extends BrowserUser {
// This flag force to use fake user media (synthetic video of multiple color)
profile.setPreference("media.navigator.streams.fake", true);
+ if (disableOpenH264) {
+ profile.setPreference("media.gmp-gmpopenh264.enabled", false);
+ }
+
capabilities.setCapability(FirefoxDriver.PROFILE, profile);
String REMOTE_URL = System.getProperty("REMOTE_URL_FIREFOX");
@@ -54,7 +59,7 @@ public class FirefoxUser extends BrowserUser {
}
} else {
log.info("Using local web driver");
- this.driver = new FirefoxDriver(capabilities);
+ this.driver = new FirefoxDriver(new FirefoxOptions(capabilities));
}
this.configureDriver();
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/OperaUser.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/OperaUser.java
index 0de43097..eb56e400 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/OperaUser.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/OperaUser.java
@@ -4,10 +4,8 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
-import org.openqa.selenium.UnexpectedAlertBehaviour;
import org.openqa.selenium.opera.OperaDriver;
import org.openqa.selenium.opera.OperaOptions;
-import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
@@ -16,27 +14,28 @@ public class OperaUser extends BrowserUser {
public OperaUser(String userName, int timeOfWaitInSeconds) {
super(userName, timeOfWaitInSeconds);
- OperaOptions options = new OperaOptions();
- options.setBinary("/usr/bin/opera");
DesiredCapabilities capabilities = DesiredCapabilities.operaBlink();
capabilities.setAcceptInsecureCerts(true);
- capabilities.setCapability(CapabilityType.UNEXPECTED_ALERT_BEHAVIOUR, UnexpectedAlertBehaviour.IGNORE);
-
+ OperaOptions options = new OperaOptions();
+ // This flag avoids to grant the user media
options.addArguments("--use-fake-ui-for-media-stream");
+ // This flag fakes user media with synthetic video
options.addArguments("--use-fake-device-for-media-stream");
- capabilities.setCapability(OperaOptions.CAPABILITY, options);
+ // This flag selects the entire screen as video source when screen sharing
+ options.addArguments("--auto-select-desktop-capture-source=Entire screen");
+ options.merge(capabilities);
String REMOTE_URL = System.getProperty("REMOTE_URL_OPERA");
if (REMOTE_URL != null) {
log.info("Using URL {} to connect to remote web driver", REMOTE_URL);
try {
- this.driver = new RemoteWebDriver(new URL(REMOTE_URL), capabilities);
+ this.driver = new RemoteWebDriver(new URL(REMOTE_URL), options);
} catch (MalformedURLException e) {
e.printStackTrace();
}
} else {
log.info("Using local web driver");
- this.driver = new OperaDriver(capabilities);
+ this.driver = new OperaDriver(options);
}
this.driver.manage().timeouts().setScriptTimeout(this.timeOfWaitInSeconds, TimeUnit.SECONDS);
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java
index 1228b190..6daa0339 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java
@@ -24,7 +24,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
-import java.util.Map;
+import java.util.Map.Entry;
import javax.net.ssl.SSLContext;
@@ -33,14 +33,14 @@ import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.mashape.unirest.http.HttpMethod;
import com.mashape.unirest.http.HttpResponse;
@@ -89,97 +89,144 @@ public class CustomHttpClient {
return this.commonRest(method, path, body, status);
}
- public JsonObject rest(HttpMethod method, String path, String body, int status, boolean exactReturnedFields,
- String jsonReturnedValue) throws Exception {
- JsonObject json = this.commonRest(method, path, body, status);
- JsonObject jsonObjExpected = null;
- jsonReturnedValue.replaceAll("'", "\"");
+ /**
+ * "matchKeys" to true for the returned JSON to have the exact same keys as the
+ * expected JSON (same number, position and name). If false, then any key
+ * existing in the expected JSON must exist in the returned JSON, but the
+ * returned JSON may have extra keys not available in expected JSON.
+ *
+ * "matchValues" to true for the returned JSON to have the exact same value in
+ * all the key-value pairs declared in the expected JSON. If the returned JSON
+ * does not have any of the keys of the expected JSON, an Error is thrown. The
+ * value comparison applies to NULL, JSON arrays, JSON objects or primitive
+ * values, at all nested levels.
+ *
+ * "matchArrays" to true for the returned JSON to have the exact same JSON array
+ * as value that any array property of the expected JSON. That includes value
+ * and order. To false to check only that JSON array is the type of the returned
+ * value, but not to check its content. If "matchValues" is false, then this
+ * property will not have effect and shall be considered false.
+ */
+ public JsonObject rest(HttpMethod method, String path, String body, int status, boolean matchKeys,
+ boolean matchValues, boolean matchArrays, String jsonReturnedValue) throws Exception {
+ JsonObject jsonExpected = null;
+ jsonReturnedValue = jsonReturnedValue.replaceAll("'", "\"");
try {
- jsonObjExpected = JsonParser.parseString(jsonReturnedValue).getAsJsonObject();
+ jsonExpected = JsonParser.parseString(jsonReturnedValue).getAsJsonObject();
} catch (JsonSyntaxException e1) {
- throw new Exception("Expected json element is a string without a JSON format: " + jsonReturnedValue);
+ throw new Exception("Expected JSON element is a string without a JSON format: " + jsonReturnedValue);
}
+ JsonObject jsonActual = this.commonRest(method, path, body, status);
+ check(jsonExpected, jsonActual, matchKeys, matchValues, matchArrays);
+ return jsonActual;
- if (exactReturnedFields) {
- if (jsonObjExpected.size() != json.size()) {
- throw new Exception(
- "Error in number of keys in JSON response to POST (" + json.toString() + ")" + path);
- }
- }
- for (String key : jsonObjExpected.keySet()) {
- Class> c1 = jsonObjExpected.get(key).getClass();
- Class> c2 = json.get(key).getClass();
-
- c1 = unifyNumberType(c1);
- c2 = unifyNumberType(c2);
-
- if (!c1.equals(c2)) {
- throw new Exception("Wrong class of property " + key);
- }
- }
- return json;
}
- public JsonObject rest(HttpMethod method, String path, String body, int status, boolean exactReturnedFields,
- Map jsonResponse) throws Exception {
- JsonObject json = this.commonRest(method, path, body, status);
-
- if (exactReturnedFields) {
- if (jsonResponse.size() != json.size())
- throw new Exception("Error in number of keys in JSON response to POST " + path);
- }
-
- for (Map.Entry entry : jsonResponse.entrySet()) {
- Object value = entry.getValue();
-
- if (value instanceof String) {
- try {
- JsonObject jsonObjExpected = JsonParser.parseString((String) value).getAsJsonObject();
- JsonObject jsonObjActual = json.get(entry.getKey()).getAsJsonObject();
- // COMPARE
-
- } catch (JsonSyntaxException e1) {
- try {
- JsonArray jsonArrayExpected = JsonParser.parseString((String) value).getAsJsonArray();
- JsonArray jsonArrayActual = json.get(entry.getKey()).getAsJsonArray();
- // COMPARE
-
- } catch (JsonSyntaxException e2) {
- if (((String) value) != json.get(entry.getKey()).getAsString()) {
- throw new Exception("JSON field " + entry.getKey() + " has not expected value. Expected: "
- + value + ". Actual: " + json.get(entry.getKey()).getAsString());
- }
- }
+ public static void check(JsonObject jsonExpected, JsonObject jsonActual, boolean matchKeys, boolean matchValues,
+ boolean matchArrays) throws Exception {
+ if (matchKeys) {
+ checkSameKeys(jsonExpected, jsonActual, null, matchValues, matchArrays);
+ } else {
+ for (String key : jsonExpected.keySet()) {
+ JsonElement elExpected = jsonExpected.get(key);
+ JsonElement elActual = jsonActual.get(key);
+ if (elActual == null) {
+ throw new Exception(
+ "Expected property \"" + key + "\" did not exist in actual JSON " + jsonActual.toString());
+ }
+ checkSameType(elExpected, elActual, key, matchValues);
+ }
+ }
+ }
+
+ public static void checkSameKeys(JsonElement expected, JsonElement actual, String parent, boolean matchValues,
+ boolean matchArrays) throws Exception {
+ if (!expected.isJsonObject()) {
+ if (expected.isJsonArray()) {
+ JsonArray expectedArray = expected.getAsJsonArray();
+ JsonArray actualArray = actual.getAsJsonArray();
+ if (matchArrays) {
+ checkSameType(expectedArray, actualArray, parent, matchValues);
+ }
+ } else {
+ checkSameType(expected, actual, parent, matchValues);
+ }
+ } else {
+ JsonObject exp = expected.getAsJsonObject();
+ JsonObject act = actual.getAsJsonObject();
+ if (exp.size() != act.size()) {
+ throw new Exception("Error in number of keys in JSON object. Expected " + exp.size() + ". Actual: "
+ + act.size() + ". Actual object: " + act.toString());
+ }
+ for (Entry entry : exp.entrySet()) {
+ String key = entry.getKey();
+ if (!act.has(key)) {
+ throw new Exception("Property \"" + key + "\" is missing in actual object " + act.toString());
+ }
+ checkSameKeys(entry.getValue(), act.get(key), key, matchValues, matchArrays);
+ }
+ }
+ }
+
+ public static void checkSameType(JsonElement expected, JsonElement actual, String key, boolean checkAlsoSameValue)
+ throws Exception {
+ if (!expected.getClass().equals(actual.getClass())) {
+ throw new Exception("Expected JSON element \"" + key
+ + "\" has not the same class as the actual JSON element. Expected: "
+ + expected.getClass().getSimpleName() + ". Actual: " + actual.getClass().getSimpleName());
+ }
+ if (expected.isJsonNull()) {
+ if (!actual.isJsonNull()) {
+ throw new Exception("Actual JSON element should be null");
+ }
+ }
+ if (expected.isJsonArray()) {
+ if (!actual.isJsonArray()) {
+ throw new Exception("Actual JSON element should be a JSON array");
+ }
+ JsonArray arrayExpected = expected.getAsJsonArray();
+ JsonArray arrayActual = actual.getAsJsonArray();
+ if (checkAlsoSameValue && !arrayExpected.equals(arrayActual)) {
+ throw new Exception("Property \"" + key + "\" expected an array " + arrayExpected.toString()
+ + " but found " + arrayActual.toString());
+ }
+ }
+ if (expected.isJsonObject()) {
+ if (!actual.isJsonObject()) {
+ throw new Exception("Actual JSON element should be a JSON object");
+ }
+ JsonObject objectExpected = expected.getAsJsonObject();
+ JsonObject objectActual = actual.getAsJsonObject();
+ if (checkAlsoSameValue && !objectExpected.equals(objectActual)) {
+ throw new Exception("Property \"" + key + "\" expected a JSON object " + objectExpected.toString()
+ + " but found " + objectActual.toString());
+ }
+ }
+ if (expected.isJsonPrimitive()) {
+ JsonPrimitive primitive1 = expected.getAsJsonPrimitive();
+ JsonPrimitive primitive2 = actual.getAsJsonPrimitive();
+ if (primitive1.isString()) {
+ String string1 = primitive1.getAsString();
+ String string2 = primitive2.getAsString();
+ if (checkAlsoSameValue && !string1.equals(string2)) {
+ throw new Exception("Property \"" + key + "\" expected " + string1 + " but was " + string2);
+ }
+ }
+ if (primitive1.isBoolean()) {
+ boolean boolean1 = primitive1.getAsBoolean();
+ boolean boolean2 = primitive2.getAsBoolean();
+ if (checkAlsoSameValue && !boolean1 == boolean2) {
+ throw new Exception("Property \"" + key + "\" expected " + boolean1 + " but was " + boolean2);
+ }
+ }
+ if (primitive1.isNumber()) {
+ Number number1 = primitive1.getAsNumber();
+ Number number2 = primitive2.getAsNumber();
+ if (checkAlsoSameValue && !number1.equals(number2)) {
+ throw new Exception("Property \"" + key + "\" expected " + number1 + " but was " + number2);
}
- } else if (value instanceof Integer) {
- if (((int) value) != json.get(entry.getKey()).getAsInt()) {
- throw new Exception("JSON field " + entry.getKey() + " has not expected value. Expected: " + value
- + ". Actual: " + json.get(entry.getKey()).getAsInt());
- }
- } else if (value instanceof Long) {
- if (((long) value) != json.get(entry.getKey()).getAsLong()) {
- throw new Exception("JSON field " + entry.getKey() + " has not expected value. Expected: " + value
- + ". Actual: " + json.get(entry.getKey()).getAsLong());
- }
- } else if (value instanceof Double) {
- if (((double) value) != json.get(entry.getKey()).getAsDouble()) {
- throw new Exception("JSON field " + entry.getKey() + " has not expected value. Expected: " + value
- + ". Actual: " + json.get(entry.getKey()).getAsDouble());
- }
- } else if (value instanceof Boolean) {
- if (((boolean) value) != json.get(entry.getKey()).getAsBoolean()) {
- throw new Exception("JSON field " + entry.getKey() + " has not expected value. Expected: " + value
- + ". Actual: " + json.get(entry.getKey()).getAsBoolean());
- }
- } else if (value instanceof JSONArray || value instanceof JsonArray) {
- JsonParser.parseString(entry.getValue().toString()).getAsJsonArray();
- } else if (value instanceof JSONObject || value instanceof JsonObject) {
- JsonParser.parseString(entry.getValue().toString()).getAsJsonObject();
- } else {
- throw new Exception("JSON response field cannot be parsed: " + entry.toString());
}
}
- return json;
}
public void shutdown() throws IOException {
@@ -262,11 +309,4 @@ public class CustomHttpClient {
return json;
}
- private Class> unifyNumberType(Class> myClass) {
- if (Number.class.isAssignableFrom(myClass)) {
- return Number.class;
- }
- return myClass;
- }
-
}
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/RecordingUtils.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/RecordingUtils.java
new file mode 100644
index 00000000..0a5e9971
--- /dev/null
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/RecordingUtils.java
@@ -0,0 +1,264 @@
+package io.openvidu.test.browsers.utils;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.imageio.ImageIO;
+
+import org.jcodec.api.FrameGrab;
+import org.jcodec.api.JCodecException;
+import org.jcodec.common.model.Picture;
+import org.jcodec.scale.AWTUtil;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.stream.JsonReader;
+
+import io.openvidu.java.client.Recording;
+
+public class RecordingUtils {
+
+ protected static final Logger log = LoggerFactory.getLogger(RecordingUtils.class);
+
+ public boolean recordedGreenFileFine(File file, Recording recording) throws IOException {
+ return this.recordedFileFine(file, recording, RecordingUtils::checkVideoAverageRgbGreen);
+ }
+
+ public boolean recordedRedFileFine(File file, Recording recording) throws IOException {
+ return this.recordedFileFine(file, recording, RecordingUtils::checkVideoAverageRgbRed);
+ }
+
+ private boolean recordedFileFine(File file, Recording recording,
+ Function, Boolean> colorCheckFunction) throws IOException {
+ this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(),
+ recording.getResolution(), "aac", "h264", true);
+
+ boolean isFine = false;
+ Picture frame;
+ try {
+ // Get a frame at 75% duration and check that it has the expected color
+ frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
+ BufferedImage image = AWTUtil.toBufferedImage(frame);
+ Map colorMap = this.averageColor(image);
+
+ String realResolution = image.getWidth() + "x" + image.getHeight();
+ Assert.assertEquals(
+ "Resolution (" + recording.getResolution()
+ + ") of recording entity is not equal to real video resolution (" + realResolution + ")",
+ recording.getResolution(), realResolution);
+
+ log.info("Recording map color: {}", colorMap.toString());
+ log.info("Recording frame below");
+ System.out.println(bufferedImageToBase64PngString(image));
+ isFine = colorCheckFunction.apply(colorMap);
+ } catch (IOException | JCodecException e) {
+ log.warn("Error getting frame from video recording: {}", e.getMessage());
+ isFine = false;
+ }
+ return isFine;
+ }
+
+ public static boolean checkVideoAverageRgbGreen(Map rgb) {
+ // GREEN color: {r < 15, g > 130, b <15}
+ return (rgb.get("r") < 15) && (rgb.get("g") > 130) && (rgb.get("b") < 15);
+ }
+
+ public static boolean checkVideoAverageRgbGray(Map rgb) {
+ // GRAY color: {r < 50, g < 50, b < 50} and the absolute difference between them
+ // not greater than 2
+ return (rgb.get("r") < 50) && (rgb.get("g") < 50) && (rgb.get("b") < 50)
+ && (Math.abs(rgb.get("r") - rgb.get("g")) <= 2) && (Math.abs(rgb.get("r") - rgb.get("b")) <= 2)
+ && (Math.abs(rgb.get("b") - rgb.get("g")) <= 2);
+ }
+
+ public static boolean checkVideoAverageRgbRed(Map rgb) {
+ // RED color: {r > 240, g < 15, b <15}
+ return (rgb.get("r") > 240) && (rgb.get("g") < 15) && (rgb.get("b") < 15);
+ }
+
+ private String bufferedImageToBase64PngString(BufferedImage image) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ String imageString = null;
+ try {
+ ImageIO.write(image, "png", bos);
+ byte[] imageBytes = bos.toByteArray();
+ imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
+ bos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return imageString;
+ }
+
+ public void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
+ String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
+
+ // Should be only 2 files: zip and metadata
+ File folder = new File(recPath);
+ Assert.assertEquals("There are more than 2 files (ZIP and metadata) inside individual recording folder "
+ + recPath + ": " + Arrays.toString(folder.listFiles()), 2, folder.listFiles().length);
+
+ File file1 = new File(recPath + recording.getName() + ".zip");
+ File file2 = new File(recPath + ".recording." + recording.getId());
+
+ Assert.assertTrue("File " + file1.getAbsolutePath() + " does not exist or is empty",
+ file1.exists() && file1.length() > 0);
+ Assert.assertTrue("File " + file2.getAbsolutePath() + " does not exist or is empty",
+ file2.exists() && file2.length() > 0);
+
+ List unzippedWebmFiles = new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
+
+ Assert.assertEquals("Expecting " + numberOfVideoFiles + " videos inside ZIP file but "
+ + unzippedWebmFiles.size() + " found: " + unzippedWebmFiles.toString(), numberOfVideoFiles,
+ unzippedWebmFiles.size());
+
+ File jsonSyncFile = new File(recPath + recording.getName() + ".json");
+ Assert.assertTrue("JSON sync file " + jsonSyncFile.getAbsolutePath() + "does not exist or is empty",
+ jsonSyncFile.exists() && jsonSyncFile.length() > 0);
+
+ JsonObject jsonSyncMetadata;
+ try {
+ Gson gson = new Gson();
+ JsonReader reader = new JsonReader(new FileReader(jsonSyncFile));
+ jsonSyncMetadata = gson.fromJson(reader, JsonObject.class);
+ } catch (Exception e) {
+ log.error("Cannot read JSON sync metadata file from {}. Error: {}", jsonSyncFile.getAbsolutePath(),
+ e.getMessage());
+ Assert.fail("Cannot read JSON sync metadata file from " + jsonSyncFile.getAbsolutePath());
+ return;
+ }
+
+ long totalFileSize = 0;
+ JsonArray syncArray = jsonSyncMetadata.get("files").getAsJsonArray();
+ for (File webmFile : unzippedWebmFiles) {
+ totalFileSize += webmFile.length();
+
+ Assert.assertTrue("WEBM file " + webmFile.getAbsolutePath() + " does not exist or is empty",
+ webmFile.exists() && webmFile.length() > 0);
+
+ double durationInSeconds = 0;
+ boolean found = false;
+ for (int i = 0; i < syncArray.size(); i++) {
+ JsonObject j = syncArray.get(i).getAsJsonObject();
+ if (webmFile.getName().contains(j.get("streamId").getAsString())) {
+ durationInSeconds = (double) (j.get("endTimeOffset").getAsDouble()
+ - j.get("startTimeOffset").getAsDouble()) / 1000;
+ found = true;
+ break;
+ }
+ }
+
+ Assert.assertTrue("Couldn't find in JSON sync object information for webm file " + webmFile.getName(),
+ found);
+
+ log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(),
+ durationInSeconds);
+ this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds,
+ recording.getResolution(), audioDecoder, videoDecoder, checkAudio);
+ webmFile.delete();
+ }
+
+ Assert.assertEquals("Size of recording entity (" + recording.getSessionId()
+ + ") is not equal to real file size (" + totalFileSize + ")", recording.getSize(), totalFileSize);
+
+ jsonSyncFile.delete();
+ }
+
+ public void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration, String resolution,
+ String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
+ // Check tracks, duration, resolution, framerate and decoders
+ MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath());
+
+ if (hasVideo) {
+ if (checkAudio) {
+ if (hasAudio) {
+ Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have audio",
+ metadata.hasAudio() && metadata.hasVideo());
+ Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
+ } else {
+ Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have video",
+ metadata.hasVideo());
+ Assert.assertFalse(metadata.hasAudio());
+ }
+ }
+ if (resolution != null) {
+ Assert.assertEquals(resolution, metadata.getVideoWidth() + "x" + metadata.getVideoHeight());
+ }
+ Assert.assertTrue(metadata.getVideoDecoder().toLowerCase().contains(videoDecoder));
+ } else if (hasAudio && checkAudio) {
+ Assert.assertTrue(metadata.hasAudio());
+ Assert.assertFalse(metadata.hasVideo());
+ Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
+ } else {
+ Assert.fail("Cannot check a file witho no audio and no video");
+ }
+ // Check duration with 1 decimal precision
+ DecimalFormat df = new DecimalFormat("#0.0");
+ df.setRoundingMode(RoundingMode.UP);
+ log.info("Duration of {} according to ffmpeg: {} s", file.getName(), metadata.getDuration());
+ log.info("Duration of {} according to 'duration' property: {} s", file.getName(), duration);
+ log.info("Difference in s duration: {}", Math.abs(metadata.getDuration() - duration));
+ final double difference = 10;
+ Assert.assertTrue(
+ "Difference between recording entity duration (" + duration + ") and real video duration ("
+ + metadata.getDuration() + ") is greater than " + difference + " in file " + file.getName(),
+ Math.abs((metadata.getDuration() - duration)) < difference);
+ }
+
+ public boolean thumbnailIsFine(File file, Function, Boolean> colorCheckFunction) {
+ boolean isFine = false;
+ BufferedImage image = null;
+ try {
+ image = ImageIO.read(file);
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ return false;
+ }
+ log.info("Recording thumbnail dimensions: {}x{}", image.getWidth(), image.getHeight());
+ Map colorMap = this.averageColor(image);
+ log.info("Thumbnail map color: {}", colorMap.toString());
+ isFine = colorCheckFunction.apply(colorMap);
+ return isFine;
+ }
+
+ private Map averageColor(BufferedImage bi) {
+ int x0 = 0;
+ int y0 = 0;
+ int w = bi.getWidth();
+ int h = bi.getHeight();
+ int x1 = x0 + w;
+ int y1 = y0 + h;
+ long sumr = 0, sumg = 0, sumb = 0;
+ for (int x = x0; x < x1; x++) {
+ for (int y = y0; y < y1; y++) {
+ Color pixel = new Color(bi.getRGB(x, y));
+ sumr += pixel.getRed();
+ sumg += pixel.getGreen();
+ sumb += pixel.getBlue();
+ }
+ }
+ int num = w * h;
+ Map colorMap = new HashMap<>();
+ colorMap.put("r", (long) (sumr / num));
+ colorMap.put("g", (long) (sumg / num));
+ colorMap.put("b", (long) (sumb / num));
+ return colorMap;
+ }
+
+}
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java
new file mode 100644
index 00000000..a1ca17b1
--- /dev/null
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java
@@ -0,0 +1,41 @@
+package io.openvidu.test.browsers.utils.layout;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.event.EventListener;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@SpringBootApplication
+public class CustomLayoutHandler extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
+
+ private static ConfigurableApplicationContext context;
+ public static CountDownLatch initLatch;
+
+ public static void main(String[] args, CountDownLatch initLatch) {
+ CustomLayoutHandler.initLatch = initLatch;
+ CustomLayoutHandler.context = new SpringApplicationBuilder(CustomLayoutHandler.class)
+ .properties("spring.config.location:classpath:aplication-pro-layout-handler.properties").build()
+ .run(args);
+ }
+
+ @Override
+ protected void configure(HttpSecurity security) throws Exception {
+ security.httpBasic().disable();
+ }
+
+ @EventListener(ApplicationReadyEvent.class)
+ public void afterStartup() {
+ CustomLayoutHandler.initLatch.countDown();
+ }
+
+ public static void shutDown() {
+ CustomLayoutHandler.context.close();
+ }
+
+}
diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java
similarity index 97%
rename from openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java
rename to openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java
index 2737785f..06d05e74 100644
--- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java
+++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java
@@ -15,7 +15,7 @@
*
*/
-package io.openvidu.test.browsers.utils;
+package io.openvidu.test.browsers.utils.webhook;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
@@ -59,6 +59,10 @@ public class CustomWebhook {
CustomWebhook.context.close();
}
+ public static void clean() {
+ CustomWebhook.events.clear();
+ }
+
public synchronized static JsonObject waitForEvent(String eventName, int maxSecondsWait) throws Exception {
if (events.get(eventName) == null) {
events.put(eventName, new LinkedBlockingDeque<>());
diff --git a/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties b/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties
new file mode 100644
index 00000000..10fc87dd
--- /dev/null
+++ b/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties
@@ -0,0 +1,3 @@
+server.port=5555
+server.ssl.enabled=false
+security.basic.enabled=false
\ No newline at end of file
diff --git a/openvidu-test-browsers/src/main/resources/static/index.html b/openvidu-test-browsers/src/main/resources/static/index.html
new file mode 100644
index 00000000..ca45b3f7
--- /dev/null
+++ b/openvidu-test-browsers/src/main/resources/static/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/openvidu-test-browsers/src/test/java/io/openvidu/test/browsers/CustomHttpClientTest.java b/openvidu-test-browsers/src/test/java/io/openvidu/test/browsers/CustomHttpClientTest.java
new file mode 100644
index 00000000..3fd27cbc
--- /dev/null
+++ b/openvidu-test-browsers/src/test/java/io/openvidu/test/browsers/CustomHttpClientTest.java
@@ -0,0 +1,124 @@
+package io.openvidu.test.browsers;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import io.openvidu.test.browsers.utils.CustomHttpClient;
+
+public class CustomHttpClientTest {
+
+ @Test
+ public void testOneLevel() throws Exception {
+ String expected = "{}";
+ String actual = "{}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1'}";
+ actual = "{'prop1':'val1'}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1','prop2':'val2'}";
+ actual = "{'prop1':'val1','prop2':'val2'}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1'}";
+ actual = "{'prop1':'val1','prop2':'val2'}";
+ executeCheck(expected, actual, false, true, true);
+ expected = "{'prop1':'val1','prop2':'val2'}";
+ actual = "{'prop1':'WRONG','prop2':'WRONG'}";
+ executeCheck(expected, actual, true, false, true);
+ expected = "{'prop1':'val1','prop2':[{},{}]}";
+ actual = "{'prop1':'WRONG','prop2':[{}]}";
+ executeCheck(expected, actual, true, false, true);
+ }
+
+ @Test
+ public void testMultipleLevels() throws Exception {
+ String expected = "{'prop1':{'prop2':'val2'}}";
+ String actual = "{'prop1':{'prop2':'val2'}}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1','prop2':{'prop3':'val3'}}";
+ actual = "{'prop1':'val1','prop2':{'prop3':'val3'}}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1','prop2':{'prop3':'val3'}}";
+ actual = "{'prop1':'WRONG','prop2':{'prop3':'WRONG'}}";
+ executeCheck(expected, actual, true, false, true);
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':{'prop3':'val3'}}";
+ String actual2 = "{'prop1':'WRONG','prop2':'WRONG'}";
+ executeCheck(expected2, actual2, true, false, true);
+ });
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':{'prop3':'val3'}}";
+ String actual2 = "{'prop1':'WRONG','prop2':[12,34]}";
+ executeCheck(expected2, actual2, true, false, true);
+ });
+ expected = "{'prop1':'val1','prop1':{'prop3':'val3'}}";
+ actual = "{'prop1':'val1','prop1':{'prop3':'val3'},'WRONG':'val1'}";
+ executeCheck(expected, actual, false, true, true);
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':[12,34]}";
+ String actual2 = "{'prop1':'val1','prop2':[12,35]}";
+ executeCheck(expected2, actual2, false, true, true);
+ });
+ Assert.assertThrows(IllegalStateException.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':[12,34]}";
+ String actual2 = "{'prop1':'val1','prop2':{'WRONG':true}}";
+ executeCheck(expected2, actual2, true, false, true);
+ });
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':'val1','prop1':{'prop3':null}}";
+ String actual2 = "{'prop1':'val1','prop1':{'prop3':12.4},'WRONG':'val1'}";
+ executeCheck(expected2, actual2, false, true, true);
+ });
+ expected = "{'prop1':'val1','prop2':{'prop3':null}}";
+ actual = "{'prop1':'val1','prop2':{'prop3':null},'WRONG':'val1'}";
+ executeCheck(expected, actual, false, true, true);
+ expected = "{'prop1':'val1','prop2':{'prop3':12}}";
+ actual = "{'prop1':'val1','prop2':{'prop3':12}}";
+ executeCheck(expected, actual, true, true, true);
+ expected = "{'prop1':'val1','prop2':[true,false]}";
+ actual = "{'prop1':'val1','prop2':[true,false]}";
+ executeCheck(expected, actual, true, true, true);
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':[false,true]}";
+ String actual2 = "{'prop1':'val1','prop2':[true,false]}";
+ executeCheck(expected2, actual2, true, true, true);
+ });
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':[false,true]}";
+ String actual2 = "{'prop1':'val1','prop2':[true,false]}";
+ executeCheck(expected2, actual2, true, true, true);
+ });
+ expected = "{'prop1':'val1','prop2':[false,true]}";
+ actual = "{'prop1':'val1','prop2':[]}";
+ executeCheck(expected, actual, true, false, true);
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':'val1','prop2':[false,true]}";
+ String actual2 = "{'prop1':'val1','prop2':[],'prop3':false}";
+ executeCheck(expected2, actual2, false, true, true);
+ });
+ expected = "{'prop1':1,'prop2':[]}";
+ actual = "{'prop1':1,'prop2':[{'prop2':'val2'}]}";
+ executeCheck(expected, actual, true, true, false);
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':1,'prop2':[]}";
+ String actual2 = "{'prop1':0,'prop2':[{'prop2':'val2'}]}";
+ executeCheck(expected2, actual2, true, true, false);
+ });
+ Assert.assertThrows(Exception.class, () -> {
+ String expected2 = "{'prop1':1,'prop2':[]}";
+ String actual2 = "{'prop1':1,'prop2':[{'prop2':'val2'}]}";
+ executeCheck(expected2, actual2, true, true, true);
+ });
+ }
+
+ private void executeCheck(String expected, String actual, boolean matchKeys, boolean matchValues,
+ boolean matchArrays) throws JsonSyntaxException, Exception {
+ expected = expected.replaceAll("'", "\"");
+ actual = actual.replaceAll("'", "\"");
+ CustomHttpClient.check(JsonParser.parseString(expected).getAsJsonObject(),
+ JsonParser.parseString(actual).getAsJsonObject(), matchKeys, matchValues, matchArrays);
+ }
+
+}
diff --git a/openvidu-test-e2e/docker/bionic/Dockerfile b/openvidu-test-e2e/docker/bionic/Dockerfile
index b5b2c24e..7b6ca520 100644
--- a/openvidu-test-e2e/docker/bionic/Dockerfile
+++ b/openvidu-test-e2e/docker/bionic/Dockerfile
@@ -1,6 +1,6 @@
FROM ubuntu:18.04
-LABEL maintainer="openvidu@gmail.com"
+LABEL maintainer="info@openvidu.io"
USER root
@@ -9,7 +9,7 @@ RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y software-properties-common && apt-get install -y --no-install-recommends apt-utils
# Install Kurento Media Server (KMS)
-RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.14.0 bionic kms6" | tee /etc/apt/sources.list.d/kurento.list \
+RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.15.0 bionic kms6" | tee /etc/apt/sources.list.d/kurento.list \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5AFA7A83 \
&& apt-get update \
&& apt-get -y install kurento-media-server
@@ -17,10 +17,10 @@ RUN sed -i "s/DAEMON_USER=\"kurento\"/DAEMON_USER=\"root\"/g" /etc/default/kuren
# Install Node
RUN apt-get update && apt-get install -y curl
-RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y nodejs
+RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs
-# Java 8
-RUN apt-get install -y openjdk-8-jdk-headless
+# Java 11
+RUN apt-get install -y openjdk-11-jdk-headless
# Maven
RUN apt-get install -y maven
diff --git a/openvidu-test-e2e/docker/my-custom-layout/index.html b/openvidu-test-e2e/docker/my-custom-layout/index.html
new file mode 100644
index 00000000..ca45b3f7
--- /dev/null
+++ b/openvidu-test-e2e/docker/my-custom-layout/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/openvidu-test-e2e/docker/xenial/Dockerfile b/openvidu-test-e2e/docker/xenial/Dockerfile
index 5ade8ad2..975dacf8 100644
--- a/openvidu-test-e2e/docker/xenial/Dockerfile
+++ b/openvidu-test-e2e/docker/xenial/Dockerfile
@@ -1,15 +1,15 @@
FROM ubuntu:16.04
-LABEL maintainer="openvidu@gmail.com"
+LABEL maintainer="info@openvidu.io"
USER root
-RUN apt-get update && apt-get -y upgrade
+RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y software-properties-common && apt-get install -y --no-install-recommends apt-utils
# Install Kurento Media Server (KMS)
-RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.14.0 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
+RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.15.0 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5AFA7A83 \
&& apt-get update \
&& apt-get -y install kurento-media-server
@@ -17,13 +17,7 @@ RUN sed -i "s/DAEMON_USER=\"kurento\"/DAEMON_USER=\"root\"/g" /etc/default/kuren
# Install Node
RUN apt-get update && apt-get install -y curl
-RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y nodejs
-
-# Java 8
-RUN apt-get install -y openjdk-8-jdk-headless
-
-# Maven
-RUN apt-get install -y maven
+RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs
# git
RUN apt-get install -y git
@@ -42,6 +36,21 @@ RUN apt-get install -y ffmpeg
# docker
RUN apt-get update && apt-get -y install docker.io
+# Java 11
+RUN add-apt-repository ppa:openjdk-r/ppa && \
+ apt-get update && \
+ apt-get install -y openjdk-11-jdk-headless
+
+# This is a fix: JDK 11 in Ubuntu 16.04 misses Java certs
+RUN wget https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz -P /tmp/jdk8
+RUN tar -zxvf /tmp/jdk8/openjdk-*.tar.gz -C /tmp/jdk8 --strip-components=1 && \
+ cp /tmp/jdk8/jre/lib/security/cacerts /etc/ssl/certs/java && \
+ update-ca-certificates -f && \
+ rm -rf /tmp/jdk8
+
+# Maven
+RUN apt-get install -y maven
+
# Cleanup
RUN rm -rf /var/lib/apt/lists/*
RUN apt-get autoremove --purge -y && apt-get autoclean
diff --git a/openvidu-test-e2e/jenkins/Jenkinsfile b/openvidu-test-e2e/jenkins/Jenkinsfile
index 762d0aa7..e722c6e6 100644
--- a/openvidu-test-e2e/jenkins/Jenkinsfile
+++ b/openvidu-test-e2e/jenkins/Jenkinsfile
@@ -1,99 +1,178 @@
node('container') {
- sh 'docker rm -f chrome firefox e2e || true'
+
+ sh 'docker rm -f e2e chrome firefox opera || true'
+ sh 'rm -rf /opt/openvidu/* || true'
+ sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m -P /opt/openvidu'
+ sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav -P /opt/openvidu'
+ sh 'wget --directory-prefix=/opt/openvidu/test-layouts/layout1 https://raw.githubusercontent.com/OpenVidu/openvidu/master/openvidu-test-e2e/docker/my-custom-layout/index.html'
+
+ docker.image('openvidu/openvidu-test-e2e:$DISTRO').pull()
docker.image('selenium/standalone-chrome:latest').pull()
docker.image('selenium/standalone-firefox:latest').pull()
- docker.image('selenium/standalone-chrome:latest').withRun('-p 6666:4444 --name chrome --shm-size=1g -v /opt/openvidu:/opt/openvidu') { c ->
- sh 'rm -rf /opt/openvidu/barcode.* && wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m -P /opt/openvidu'
- sh 'rm -rf /opt/openvidu/fakeaudio.* && wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav -P /opt/openvidu'
- docker.image('selenium/standalone-firefox:latest').withRun('-p 6667:4444 --name firefox --shm-size=1g') { d ->
- def mycontainer = docker.image('openvidu/openvidu-test-e2e:$DISTRO')
- mycontainer.pull()
- mycontainer.inside("--name e2e -p 4200:4200 -p 4443:4443 -u root -e MY_UID=0 -v /var/run/docker.sock:/var/run/docker.sock:rw -v /dev/shm:/dev/shm -v /opt/openvidu:/opt/openvidu --privileged") {
- stage('Preparation') {
- sh 'rm -rf ~/.m2 || true'
- sh 'rm -rf openvidu || true'
- sh 'rm -rf kurento-java || true'
- sh 'rm -rf /opt/openvidu/recordings/* || true'
- sh 'git clone https://github.com/OpenVidu/openvidu.git'
- sh 'cd openvidu && git fetch origin && git checkout $OPENVIDU_COMMIT'
- sh(script: '''#!/bin/bash
- if $KURENTO_JAVA_SNAPSHOT ; then
- git clone https://github.com/Kurento/kurento-java.git
- cd kurento-java && MVN_VERSION=$(mvn --batch-mode -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
- cd ../openvidu && mvn --batch-mode versions:set-property -Dproperty=version.kurento -DnewVersion=$MVN_VERSION
- mvn dependency:get -DrepoUrl=https://maven.openvidu.io/repository/snapshots/ -Dartifact=org.kurento:kurento-client:$MVN_VERSION
- mvn dependency:get -DrepoUrl=https://maven.openvidu.io/repository/snapshots/ -Dartifact=org.kurento:kurento-jsonrpc-client-jetty:$MVN_VERSION
- mvn dependency:get -DrepoUrl=https://maven.openvidu.io/repository/snapshots/ -Dartifact=org.kurento:kurento-jsonrpc-server:$MVN_VERSION
- mvn dependency:get -DrepoUrl=https://maven.openvidu.io/repository/snapshots/ -Dartifact=org.kurento:kurento-test:$MVN_VERSION
- fi
- '''.stripIndent())
- sh(script: '''#!/bin/bash
- if $KURENTO_MEDIA_SERVER_DEV ; then
- echo "Upgrading KMS to dev version"
- sudo apt-get update && sudo apt-get install -y aptitude
- sudo aptitude remove -y kurento-media-server
- DISTRO=`lsb_release --codename | cut -f2`
- sudo echo "deb [arch=amd64] http://ubuntu.openvidu.io/dev $DISTRO kms6" | sudo tee /etc/apt/sources.list.d/kurento.list
- sudo apt-get update && sudo apt-get --yes -o Dpkg::Options::="--force-confnew" install kurento-media-server
- fi
- '''.stripIndent())
- }
- stage('OpenVidu parent build') {
- sh 'cd openvidu/openvidu-java-client && mvn --batch-mode versions:set -DnewVersion=1.0.0-TEST'
- sh 'cd openvidu && mvn --batch-mode versions:set-property -Dproperty=version.openvidu.java.client -DnewVersion=1.0.0-TEST'
- sh 'cd openvidu && mvn --batch-mode -DskipTests=true clean install'
- }
- stage('OpenVidu Browser build') {
- sh 'cd openvidu/openvidu-browser && npm install --unsafe-perm && npm run build && npm link'
- }
- stage('OpenVidu Node Client build') {
- sh 'cd openvidu/openvidu-node-client && npm install --unsafe-perm && npm run build && npm link'
- }
- stage('OpenVidu TestApp build') {
- sh 'cd openvidu/openvidu-testapp && npm install --unsafe-perm && npm link openvidu-browser && npm link openvidu-node-client && export NG_CLI_ANALYTICS=ci && ./node_modules/@angular/cli/bin/ng build --prod'
- }
- stage('OpenVidu Server unit tests') {
- sh 'cd openvidu/openvidu-server && mvn --batch-mode -Dtest=io.openvidu.server.test.unit.*Test test'
- }
- stage('OpenVidu Server integration tests') {
- sh 'cd openvidu/openvidu-server && mvn --batch-mode -Dtest=io.openvidu.server.test.integration.*Test test'
- }
- stage('OpenVidu Server build') {
- sh 'cd openvidu/openvidu-server/src/dashboard && npm install --unsafe-perm && npm link openvidu-browser && export NG_CLI_ANALYTICS=ci && npm run build-prod'
- sh 'cd openvidu/openvidu-server && mvn --batch-mode clean compile package'
- }
- stage ('Environment Launch') {
- sh 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/CN=www.mydom.com/O=My Company LTD./C=US" -keyout openvidu/openvidu-testapp/dist/key.pem -out openvidu/openvidu-testapp/dist/cert.pem'
- sh 'cd openvidu/openvidu-testapp/dist && http-server -S -p 4200 &> /testapp.log &'
- sh '/usr/bin/kurento-media-server &> /kms.log &'
- sh 'until $(curl --insecure --output /dev/null --silent http://127.0.0.1:8888/kurento); do echo "Waiting for KMS..."; sleep 1; done'
- sh(script: '''#!/bin/bash
- if [ "$DOCKER_RECORDING_VERSION" != "default" ]; then
- echo "Using custom openvidu-recording tag: $DOCKER_RECORDING_VERSION"
- cd openvidu/openvidu-server/target && java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_VERSION=$DOCKER_RECORDING_VERSION -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook openvidu-server-*.jar &> openvidu-server.log &
- else
- echo "Using default openvidu-recording tag"
- cd openvidu/openvidu-server/target && java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook openvidu-server-*.jar &> openvidu-server.log &
- fi
- '''.stripIndent())
- sh 'until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do echo "Waiting for openvidu-server..."; sleep 2; done'
- }
- stage ('OpenVidu E2E tests') {
- try {
- sh(script: '''#!/bin/bash
- cd openvidu/openvidu-test-e2e && sudo mvn --batch-mode -DAPP_URL=https://172.17.0.1:4200/ -DOPENVIDU_URL=https://172.17.0.1:4443/ -DREMOTE_URL_CHROME=http://172.17.0.1:6666/wd/hub/ -DREMOTE_URL_FIREFOX=http://172.17.0.1:6667/wd/hub/ test
- if [[ "$?" -ne 0 ]] ; then
- echo "ERROR RUNNING TESTS"
- cat openvidu/openvidu-server/target/openvidu-server.log
- fi
- '''.stripIndent())
- }
- finally {
- junit 'openvidu/openvidu-test-e2e/**/target/surefire-reports/TEST-*.xml'
- archiveArtifacts artifacts: '**/openvidu-server.log'
+ docker.image('selenium/standalone-opera:latest').pull()
+
+ docker.image('openvidu/openvidu-test-e2e:$DISTRO').inside('--name e2e -p 4200:4200 -p 4443:4443 -p 5555:5555 -u root -e MY_UID=0 -v /var/run/docker.sock:/var/run/docker.sock:rw -v /dev/shm:/dev/shm -v /opt/openvidu:/opt/openvidu --privileged') {
+
+ stage('Preparation') {
+ sh 'rm -rf ~/.m2 || true'
+ sh 'rm -rf openvidu || true'
+ sh 'rm -rf kurento-java || true'
+ sh 'git clone https://github.com/OpenVidu/openvidu.git'
+ sh 'cd openvidu && git fetch --all && git checkout $OPENVIDU_COMMIT'
+ sh(script: '''#!/bin/bash -xe
+ if [[ $KURENTO_JAVA_COMMIT != "default" ]]; then
+ git clone https://github.com/Kurento/kurento-java.git
+ cd kurento-java
+ git checkout -f $KURENTO_JAVA_COMMIT
+ mvn clean install
+ fi
+ '''.stripIndent())
+ sh(script: '''#!/bin/bash -xe
+ if $KURENTO_MEDIA_SERVER_DEV ; then
+ echo "Upgrading KMS to dev version"
+ sudo apt-get update && sudo apt-get install -y aptitude
+ sudo aptitude remove -y kurento-media-server
+ DISTRO=`lsb_release --codename | cut -f2`
+ sudo echo "deb [arch=amd64] http://ubuntu.openvidu.io/dev $DISTRO kms6" | sudo tee /etc/apt/sources.list.d/kurento.list
+ sudo apt-get update && sudo apt-get --yes -o Dpkg::Options::="--force-confnew" install kurento-media-server
+ fi
+ '''.stripIndent())
+ }
+
+ stage('OpenVidu Browser build') {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu
+ if [[ $OPENVIDU_BROWSER_COMMIT != "default" ]]; then
+ git checkout -f $OPENVIDU_BROWSER_COMMIT
+ fi
+ cd openvidu-browser
+ npm install --unsafe-perm && npm run build && npm pack
+ cp openvidu-browser-*.tgz /opt/openvidu
+ cd ..
+ git checkout -f $OPENVIDU_COMMIT
+ '''.stripIndent())
+ }
+
+ stage('OpenVidu Node Client build') {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu
+ if [[ $OPENVIDU_NODE_CLIENT_COMMIT != "default" ]]; then
+ git checkout -f $OPENVIDU_NODE_CLIENT_COMMIT
+ fi
+ cd openvidu-node-client
+ npm install --unsafe-perm && npm run build && npm pack
+ cp openvidu-node-client-*.tgz /opt/openvidu
+ cd ..
+ git checkout -f $OPENVIDU_COMMIT
+ '''.stripIndent())
+ }
+
+ stage('OpenVidu TestApp build') {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu
+ if [[ $OPENVIDU_TESTAPP_COMMIT != "default" ]]; then
+ git checkout -f $OPENVIDU_TESTAPP_COMMIT
+ fi
+ cd openvidu-testapp
+ npm install --unsafe-perm
+ npm install /opt/openvidu/openvidu-browser-*.tgz
+ npm install /opt/openvidu/openvidu-node-client-*.tgz
+ export NG_CLI_ANALYTICS="false" && ./node_modules/@angular/cli/bin/ng build --prod --output-path=/opt/openvidu/testapp
+ cd ..
+ git checkout -f $OPENVIDU_COMMIT
+ '''.stripIndent())
+ }
+
+ stage('OpenVidu Java Client build') {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu
+ if [[ $OPENVIDU_JAVA_CLIENT_COMMIT != "default" ]]; then
+ git checkout -f $OPENVIDU_JAVA_CLIENT_COMMIT
+ fi
+ cd openvidu-java-client
+ mvn --batch-mode versions:set -DnewVersion=TEST
+ mvn clean compile package
+ mvn install:install-file -Dfile=target/openvidu-java-client-TEST.jar -DgroupId=io.openvidu -DartifactId=openvidu-java-client -Dversion=TEST -Dpackaging=jar
+ cd ..
+ git checkout -f $OPENVIDU_COMMIT
+ '''.stripIndent())
+ }
+
+ stage('OpenVidu parent build') {
+ sh(script: '''#!/bin/bash -xe
+ if [[ $KURENTO_JAVA_COMMIT != "default" ]]; then
+ cd kurento-java && MVN_VERSION=$(mvn --batch-mode -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
+ cd ../openvidu && mvn --batch-mode versions:set-property -Dproperty=version.kurento -DnewVersion=$MVN_VERSION
+ fi
+ '''.stripIndent())
+ sh 'cd openvidu && mvn --batch-mode versions:set-property -Dproperty=version.openvidu.java.client -DnewVersion=TEST'
+ sh 'cd openvidu && mvn --batch-mode -DskipTests=true clean install'
+ }
+
+ stage('OpenVidu Server unit tests') {
+ sh 'cd openvidu/openvidu-server && mvn --batch-mode -Dtest=io.openvidu.server.test.unit.*Test test'
+ }
+
+ stage('OpenVidu Server integration tests') {
+ sh 'cd openvidu/openvidu-server && mvn --batch-mode -Dtest=io.openvidu.server.test.integration.*Test test'
+ }
+
+ stage('OpenVidu Server build') {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu/openvidu-server/src/dashboard && npm install --unsafe-perm && npm install /opt/openvidu/openvidu-browser-*.tgz && export NG_CLI_ANALYTICS="false" && npm run build-prod
+ cd ../..
+ mvn --batch-mode package
+ cp target/openvidu-server*.jar /opt/openvidu
+ '''.stripIndent())
+ }
+
+ stage ('Environment Launch') {
+ sh 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/CN=www.mydom.com/O=My Company LTD./C=US" -keyout /opt/openvidu/testapp/key.pem -out /opt/openvidu/testapp/cert.pem'
+ sh 'cd /opt/openvidu/testapp && http-server -S -p 4200 &> /opt/openvidu/testapp.log &'
+ sh '/usr/bin/kurento-media-server &> /kms.log &'
+ sh 'until $(curl --insecure --output /dev/null --silent http://127.0.0.1:8888/kurento); do echo "Waiting for KMS..."; sleep 1; done'
+ sh(script: '''#!/bin/bash -xe
+ if [ "$DOCKER_RECORDING_VERSION" != "default" ]; then
+ echo "Using custom openvidu-recording tag: $DOCKER_RECORDING_VERSION"
+ java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_RECORDING_VERSION=$DOCKER_RECORDING_VERSION -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log &
+ else
+ echo "Using default openvidu-recording tag"
+ java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log &
+ fi
+ '''.stripIndent())
+ sh 'until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do echo "Waiting for openvidu-server..."; sleep 2; done'
+ }
+
+ docker.image('selenium/standalone-chrome:latest').withRun('-p 6666:4444 --name chrome --shm-size=1g -v /opt/openvidu:/opt/openvidu') { a ->
+ docker.image('selenium/standalone-firefox:latest').withRun('-p 6667:4444 --name firefox --shm-size=1g') { b ->
+ docker.image('selenium/standalone-opera:latest').withRun('-p 6668:4444 --name opera --shm-size=1g') { c ->
+
+ stage ('OpenVidu E2E tests') {
+ try {
+ sh(script: '''#!/bin/bash -xe
+ cd openvidu
+ if [[ $OPENVIDU_TESTE2E_COMMIT != "default" ]]; then
+ git checkout -f $OPENVIDU_TESTE2E_COMMIT
+ fi
+ cd openvidu-test-browsers
+ mvn --batch-mode versions:set -DnewVersion=TEST && mvn clean install
+ cd ..
+ mvn --batch-mode versions:set-property -Dproperty=version.openvidu.java.client -DnewVersion=TEST
+ mvn --batch-mode versions:set-property -Dproperty=version.openvidu.test.browsers -DnewVersion=TEST
+ cd openvidu-test-e2e
+ mvn -DskipTests=true clean install
+ sudo mvn --batch-mode -Dtest=OpenViduTestAppE2eTest -DAPP_URL=https://172.17.0.1:4200/ -DOPENVIDU_URL=https://172.17.0.1:4443/ -DREMOTE_URL_CHROME=http://172.17.0.1:6666/wd/hub/ -DREMOTE_URL_FIREFOX=http://172.17.0.1:6667/wd/hub/ -DREMOTE_URL_OPERA=http://172.17.0.1:6668/wd/hub/ -DEXTERNAL_CUSTOM_LAYOUT_URL=http://172.17.0.1:5555 -DEXTERNAL_CUSTOM_LAYOUT_PARAMS=sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET test
+ git checkout -f $OPENVIDU_COMMIT
+ '''.stripIndent())
+ }
+ finally {
+ junit 'openvidu/openvidu-test-e2e/**/target/surefire-reports/TEST-*.xml'
+ archiveArtifacts artifacts: '**/openvidu-server.log'
+ }
}
+
}
}
}
}
-}
+}
\ No newline at end of file
diff --git a/openvidu-test-e2e/pom.xml b/openvidu-test-e2e/pom.xml
index f2eabf8c..0613d8a0 100644
--- a/openvidu-test-e2e/pom.xml
+++ b/openvidu-test-e2e/pom.xml
@@ -47,8 +47,8 @@
-
- 1.8
+
+ 11
${java.version}
${java.version}
@@ -91,18 +91,6 @@
${version.selenium}
test
-
- org.seleniumhq.selenium
- selenium-chrome-driver
- ${version.selenium}
- test
-
-
- org.seleniumhq.selenium
- selenium-firefox-driver
- ${version.selenium}
- test
-
org.seleniumhq.selenium
selenium-api
@@ -120,12 +108,6 @@
gson
${version.gson}
-
- org.jcodec
- jcodec-javase
- 0.2.3
- test
-
com.mashape.unirest
unirest-java
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java
new file mode 100644
index 00000000..5e7edc67
--- /dev/null
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java
@@ -0,0 +1,341 @@
+package io.openvidu.test.e2e;
+
+import static org.openqa.selenium.OutputType.BASE64;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpStatus;
+import org.junit.Assert;
+import org.junit.jupiter.api.AfterEach;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.mashape.unirest.http.HttpMethod;
+
+import io.github.bonigarcia.wdm.WebDriverManager;
+import io.openvidu.java.client.OpenVidu;
+import io.openvidu.java.client.OpenViduHttpException;
+import io.openvidu.java.client.OpenViduJavaClientException;
+import io.openvidu.java.client.VideoCodec;
+import io.openvidu.test.browsers.BrowserUser;
+import io.openvidu.test.browsers.ChromeUser;
+import io.openvidu.test.browsers.FirefoxUser;
+import io.openvidu.test.browsers.OperaUser;
+import io.openvidu.test.browsers.ChromeAndroidUser;
+import io.openvidu.test.browsers.utils.CommandLineExecutor;
+import io.openvidu.test.browsers.utils.CustomHttpClient;
+import io.openvidu.test.browsers.utils.RecordingUtils;
+
+public class AbstractOpenViduTestAppE2eTest {
+
+ final protected String DEFAULT_JSON_SESSION = "{'id':'STR','object':'session','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false,'forcedVideoCodec':'STR','allowTranscoding':false}";
+ final protected String DEFAULT_JSON_PENDING_CONNECTION = "{'id':'STR','object':'connection','type':'WEBRTC','status':'pending','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':null,'location':null,'platform':null,'token':'STR','serverData':'STR','record':true,'role':'STR','kurentoOptions':null,'rtspUri':null,'adaptativeBitrate':null,'onlyPlayWithSubscribers':null,'networkCache':null,'clientData':null,'publishers':null,'subscribers':null}";
+ final protected String DEFAULT_JSON_ACTIVE_CONNECTION = "{'id':'STR','object':'connection','type':'WEBRTC','status':'active','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'STR','token':'STR','serverData':'STR','record':true,'role':'STR','kurentoOptions':null,'rtspUri':null,'adaptativeBitrate':null,'onlyPlayWithSubscribers':null,'networkCache':null,'clientData':'STR','publishers':[],'subscribers':[]}";
+ final protected String DEFAULT_JSON_IPCAM_CONNECTION = "{'id':'STR','object':'connection','type':'IPCAM','status':'active','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'IPCAM','token':null,'serverData':'STR','record':true,'role':null,'kurentoOptions':null,'rtspUri':'STR','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':2000,'clientData':null,'publishers':[],'subscribers':[]}";
+ final protected String DEFAULT_JSON_TOKEN = "{'id':'STR','token':'STR','connectionId':'STR','createdAt':0,'session':'STR','role':'STR','data':'STR','kurentoOptions':{}}";
+
+ protected static String OPENVIDU_SECRET = "MY_SECRET";
+ protected static String OPENVIDU_URL = "https://localhost:4443/";
+ protected static String APP_URL = "http://localhost:4200/";
+ protected static String EXTERNAL_CUSTOM_LAYOUT_URL = "http://localhost:5555";
+ protected static String EXTERNAL_CUSTOM_LAYOUT_PARAMS = "sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET";
+ protected static Exception ex = null;
+ protected final Object lock = new Object();
+
+ protected static final Logger log = LoggerFactory.getLogger(OpenViduTestAppE2eTest.class);
+ protected static final CommandLineExecutor commandLine = new CommandLineExecutor();
+ protected static final String RECORDING_IMAGE = "openvidu/openvidu-recording";
+
+ protected MyUser user;
+ protected Collection otherUsers = new ArrayList<>();
+ protected volatile static boolean isRecordingTest;
+ protected volatile static boolean isKurentoRestartTest;
+
+ protected static VideoCodec defaultForcedVideoCodec;
+ protected static boolean defaultAllowTranscoding;
+
+ protected static OpenVidu OV;
+
+ protected RecordingUtils recordingUtils = new RecordingUtils();
+
+ protected static void checkFfmpegInstallation() {
+ String ffmpegOutput = commandLine.executeCommand("which ffmpeg");
+ if (ffmpegOutput == null || ffmpegOutput.isEmpty()) {
+ log.error("ffmpeg package is not installed in the host machine");
+ Assert.fail();
+ return;
+ } else {
+ log.info("ffmpeg is installed and accesible");
+ }
+ }
+
+ protected static void setupBrowserDrivers() {
+ WebDriverManager.chromedriver().setup();
+ WebDriverManager.firefoxdriver().setup();
+ WebDriverManager.operadriver().setup();
+ }
+
+ protected static void cleanFoldersAndSetUpOpenViduJavaClient() {
+ try {
+ log.info("Cleaning folder /opt/openvidu/recordings");
+ FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ }
+ OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
+ }
+
+ protected static void loadEnvironmentVariables() {
+ String appUrl = System.getProperty("APP_URL");
+ if (appUrl != null) {
+ APP_URL = appUrl;
+ }
+ log.info("Using URL {} to connect to openvidu-testapp", APP_URL);
+
+ String externalCustomLayoutUrl = System.getProperty("EXTERNAL_CUSTOM_LAYOUT_URL");
+ if (externalCustomLayoutUrl != null) {
+ EXTERNAL_CUSTOM_LAYOUT_URL = externalCustomLayoutUrl;
+ }
+ log.info("Using URL {} to connect to external custom layout", EXTERNAL_CUSTOM_LAYOUT_URL);
+
+ String externalCustomLayoutParams = System.getProperty("EXTERNAL_CUSTOM_LAYOUT_PARAMS");
+ if (externalCustomLayoutParams != null) {
+ // Parse external layout parameters and build a URL formatted params string
+ List params = Stream.of(externalCustomLayoutParams.split(",", -1)).collect(Collectors.toList());
+ if (params.size() % 2 != 0) {
+ log.error(
+ "Wrong configuration property EXTERNAL_CUSTOM_LAYOUT_PARAMS. Must be a comma separated list with an even number of elements. e.g: EXTERNAL_CUSTOM_LAYOUT_PARAMS=param1,value1,param2,value2");
+ Assert.fail();
+ return;
+ } else {
+ EXTERNAL_CUSTOM_LAYOUT_PARAMS = "";
+ for (int i = 0; i < params.size(); i++) {
+ if (i % 2 == 0) {
+ // Param name
+ EXTERNAL_CUSTOM_LAYOUT_PARAMS += params.get(i) + "=";
+ } else {
+ // Param value
+ EXTERNAL_CUSTOM_LAYOUT_PARAMS += params.get(i);
+ if (i < params.size() - 1) {
+ EXTERNAL_CUSTOM_LAYOUT_PARAMS += "&";
+ }
+ }
+ }
+ }
+ }
+ log.info("Using URL {} to connect to external custom layout", EXTERNAL_CUSTOM_LAYOUT_PARAMS);
+
+ String openviduUrl = System.getProperty("OPENVIDU_URL");
+ if (openviduUrl != null) {
+ OPENVIDU_URL = openviduUrl;
+ }
+ log.info("Using URL {} to connect to openvidu-server", OPENVIDU_URL);
+
+ String openvidusecret = System.getProperty("OPENVIDU_SECRET");
+ if (openvidusecret != null) {
+ OPENVIDU_SECRET = openvidusecret;
+ }
+ log.info("Using secret {} to connect to openvidu-server", OPENVIDU_SECRET);
+ }
+
+ protected void setupBrowser(String browser) {
+
+ BrowserUser browserUser;
+
+ switch (browser) {
+ case "chrome":
+ browserUser = new ChromeUser("TestUser", 50, false);
+ break;
+ case "firefox":
+ browserUser = new FirefoxUser("TestUser", 50, false);
+ break;
+ case "firefoxDisabledOpenH264":
+ browserUser = new FirefoxUser("TestUser", 50, true);
+ break;
+ case "opera":
+ browserUser = new OperaUser("TestUser", 50);
+ break;
+ case "chromeAndroid":
+ browserUser = new ChromeAndroidUser("TestUser", 50);
+ break;
+ case "chromeAlternateScreenShare":
+ browserUser = new ChromeUser("TestUser", 50, "OpenVidu TestApp", false);
+ break;
+ case "chromeAsRoot":
+ browserUser = new ChromeUser("TestUser", 50, true);
+ break;
+ default:
+ browserUser = new ChromeUser("TestUser", 50, false);
+ }
+
+ this.user = new MyUser(browserUser);
+
+ user.getDriver().get(APP_URL);
+
+ WebElement urlInput = user.getDriver().findElement(By.id("openvidu-url"));
+ urlInput.clear();
+ urlInput.sendKeys(OPENVIDU_URL);
+ WebElement secretInput = user.getDriver().findElement(By.id("openvidu-secret"));
+ secretInput.clear();
+ secretInput.sendKeys(OPENVIDU_SECRET);
+
+ user.getEventManager().startPolling();
+ }
+
+ protected void setupChromeWithFakeVideo(Path videoFileLocation) {
+ this.user = new MyUser(new ChromeUser("TestUser", 50, videoFileLocation));
+ user.getDriver().get(APP_URL);
+ WebElement urlInput = user.getDriver().findElement(By.id("openvidu-url"));
+ urlInput.clear();
+ urlInput.sendKeys(OPENVIDU_URL);
+ WebElement secretInput = user.getDriver().findElement(By.id("openvidu-secret"));
+ secretInput.clear();
+ secretInput.sendKeys(OPENVIDU_SECRET);
+ user.getEventManager().startPolling();
+ }
+
+ protected static void getDefaultTranscodingValues() throws Exception {
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ JsonObject ovConfig = restClient.rest(HttpMethod.GET, "/openvidu/api/config", HttpStatus.SC_OK);
+ defaultForcedVideoCodec = VideoCodec.valueOf(ovConfig.get("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC").getAsString());
+ defaultAllowTranscoding = ovConfig.get("OPENVIDU_STREAMS_ALLOW_TRANSCODING").getAsBoolean();
+ }
+
+ @AfterEach
+ protected void dispose() {
+ if (user != null) {
+ user.dispose();
+ }
+ Iterator it = otherUsers.iterator();
+ while (it.hasNext()) {
+ MyUser other = it.next();
+ other.dispose();
+ it.remove();
+ }
+ try {
+ OV.fetch();
+ } catch (OpenViduJavaClientException | OpenViduHttpException e1) {
+ log.error("Error fetching sessions: {}", e1.getMessage());
+ }
+ OV.getActiveSessions().forEach(session -> {
+ try {
+ session.close();
+ log.info("Session {} successfully closed", session.getSessionId());
+ } catch (OpenViduJavaClientException e) {
+ log.error("Error closing session: {}", e.getMessage());
+ } catch (OpenViduHttpException e) {
+ log.error("Error closing session: {}", e.getMessage());
+ }
+ });
+ if (isRecordingTest) {
+ removeAllRecordingContiners();
+ try {
+ FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
+ } catch (IOException e) {
+ log.error(e.getMessage());
+ }
+ isRecordingTest = false;
+ }
+ if (isKurentoRestartTest) {
+ this.restartKms();
+ isKurentoRestartTest = false;
+ }
+ OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
+ }
+
+ protected void listEmptyRecordings() {
+ // List existing recordings (empty)
+ user.getDriver().findElement(By.id("list-recording-btn")).click();
+ user.getWaiter()
+ .until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Recording list []"));
+ }
+
+ protected ExpectedCondition waitForVideoDuration(WebElement element, int durationInSeconds) {
+ return new ExpectedCondition() {
+ @Override
+ public Boolean apply(WebDriver input) {
+ return element.getAttribute("duration").matches(
+ durationInSeconds - 1 + "\\.[5-9][0-9]{0,5}|" + durationInSeconds + "\\.[0-5][0-9]{0,5}");
+ }
+ };
+ }
+
+ protected void gracefullyLeaveParticipants(int numberOfParticipants) throws Exception {
+ int accumulatedConnectionDestroyed = 0;
+ for (int j = 1; j <= numberOfParticipants; j++) {
+ user.getDriver().findElement(By.id("remove-user-btn")).sendKeys(Keys.ENTER);
+ user.getEventManager().waitUntilEventReaches("sessionDisconnected", j);
+ accumulatedConnectionDestroyed = (j != numberOfParticipants)
+ ? (accumulatedConnectionDestroyed + numberOfParticipants - j)
+ : (accumulatedConnectionDestroyed);
+ user.getEventManager().waitUntilEventReaches("connectionDestroyed", accumulatedConnectionDestroyed);
+ }
+ }
+
+ protected String getBase64Screenshot(MyUser user) throws Exception {
+ String screenshotBase64 = ((TakesScreenshot) user.getDriver()).getScreenshotAs(BASE64);
+ return "data:image/png;base64," + screenshotBase64;
+ }
+
+ protected void startKms() {
+ log.info("Starting KMS");
+ commandLine.executeCommand("/usr/bin/kurento-media-server &>> /kms.log &");
+ }
+
+ protected void stopKms() {
+ log.info("Stopping KMS");
+ commandLine.executeCommand("kill -9 $(pidof kurento-media-server)");
+ }
+
+ protected void restartKms() {
+ this.stopKms();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ this.startKms();
+ }
+
+ protected void checkDockerContainerRunning(String imageName, int amount) {
+ int number = Integer.parseInt(commandLine.executeCommand("docker ps | grep " + imageName + " | wc -l"));
+ Assert.assertEquals("Wrong number of Docker containers for image " + imageName + " running", amount, number);
+ }
+
+ protected void removeAllRecordingContiners() {
+ commandLine.executeCommand("docker ps -a | awk '{ print $1,$2 }' | grep " + RECORDING_IMAGE
+ + " | awk '{print $1 }' | xargs -I {} docker rm -f {}");
+ }
+
+ protected String mergeJson(String json, String newProperties, String[] removeProperties) {
+ JsonObject jsonObj = JsonParser.parseString(json.replaceAll("'", "\"")).getAsJsonObject();
+ JsonObject newJsonObj = JsonParser.parseString(newProperties.replaceAll("'", "\"")).getAsJsonObject();
+ newJsonObj.entrySet().forEach(entry -> {
+ jsonObj.remove(entry.getKey());
+ jsonObj.add(entry.getKey(), entry.getValue());
+ });
+ for (String prop : removeProperties) {
+ jsonObj.remove(prop);
+ }
+ return jsonObj.toString().replaceAll("\"", "'");
+ }
+
+}
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/MyUser.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/MyUser.java
index 3a7952b1..8ed6a980 100644
--- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/MyUser.java
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/MyUser.java
@@ -28,7 +28,7 @@ public class MyUser {
}
public void dispose() {
- this.eventManager.stopPolling(true);
+ this.eventManager.stopPolling(true, true);
this.browserUser.dispose();
}
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java
index 1bb933b4..be49e3b1 100644
--- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java
@@ -83,6 +83,7 @@ public class OpenViduEventManager {
private Map eventNumbers;
private Map eventCountdowns;
private AtomicBoolean isInterrupted = new AtomicBoolean(false);
+ private CountDownLatch pollingLatch = new CountDownLatch(1);
private int timeOfWaitInSeconds;
public OpenViduEventManager(WebDriver driver, int timeOfWaitInSeconds) {
@@ -100,7 +101,7 @@ public class OpenViduEventManager {
public void uncaughtException(Thread th, Throwable ex) {
if (ex.getClass().getSimpleName().equals("UnhandledAlertException")
&& ex.getMessage().contains("unexpected alert open")) {
- stopPolling(false);
+ stopPolling(false, false);
System.err
.println("Alert opened (" + ex.getMessage() + "). Waiting 1 second and restarting polling");
try {
@@ -130,20 +131,23 @@ public class OpenViduEventManager {
e.printStackTrace();
}
}
+ log.info("Polling thread is now interrupted!");
+ this.pollingLatch.countDown();
});
this.pollingThread.setUncaughtExceptionHandler(h);
this.pollingThread.start();
}
- public void stopPolling(boolean stopThread) {
- this.eventCallbacks.clear();
- this.eventCountdowns.clear();
- this.eventNumbers.clear();
-
+ public void stopPolling(boolean stopThread, boolean cleanExistingEvents) {
if (stopThread) {
this.isInterrupted.set(true);
this.pollingThread.interrupt();
}
+ if (cleanExistingEvents) {
+ this.eventCallbacks.clear();
+ this.eventCountdowns.clear();
+ this.eventNumbers.clear();
+ }
}
public void on(String eventName, Consumer callback) {
@@ -186,6 +190,26 @@ public class OpenViduEventManager {
this.setCountDown(eventName, new CountDownLatch(0));
}
+ public synchronized void clearAllCurrentEvents() {
+ this.eventNumbers.keySet().forEach(eventName -> {
+ this.clearCurrentEvents(eventName);
+ });
+ }
+
+ public void resetEventThread() throws InterruptedException {
+ this.stopPolling(true, true);
+ this.pollingLatch.await();
+ this.execService.shutdownNow();
+ this.execService.awaitTermination(10, TimeUnit.SECONDS);
+ this.execService = Executors.newCachedThreadPool();
+ this.stopPolling(false, true);
+ this.clearAllCurrentEvents();
+ this.isInterrupted.set(false);
+ this.pollingLatch = new CountDownLatch(1);
+ this.eventQueue.clear();
+ this.startPolling();
+ }
+
public boolean assertMediaTracks(WebElement videoElement, boolean audioTransmission, boolean videoTransmission,
String parentSelector) {
return this.assertMediaTracks(Collections.singleton(videoElement), audioTransmission, videoTransmission,
@@ -196,6 +220,10 @@ public class OpenViduEventManager {
boolean videoTransmission) {
boolean success = true;
for (WebElement video : videoElements) {
+ if (!waitUntilSrcObjectDefined(video, "", 5000)) {
+ System.err.println("srcObject of HTMLVideoElement was not defined!");
+ return false;
+ }
success = success && (audioTransmission == this.hasAudioTracks(video, ""))
&& (videoTransmission == this.hasVideoTracks(video, ""));
if (!success)
@@ -208,6 +236,10 @@ public class OpenViduEventManager {
boolean videoTransmission, String parentSelector) {
boolean success = true;
for (WebElement video : videoElements) {
+ if (!waitUntilSrcObjectDefined(video, "", 5000)) {
+ System.err.println("srcObject of HTMLVideoElement was not defined!");
+ return false;
+ }
success = success && (audioTransmission == this.hasAudioTracks(video, parentSelector))
&& (videoTransmission == this.hasVideoTracks(video, parentSelector));
if (!success)
@@ -251,9 +283,8 @@ public class OpenViduEventManager {
}
String[] events = rawEvents.replaceFirst("^ ", "").split(" ");
- JsonParser parser = new JsonParser();
for (String e : events) {
- JsonObject event = (JsonObject) parser.parse(e);
+ JsonObject event = JsonParser.parseString(e).getAsJsonObject();
final String eventType = event.get("type").getAsString();
this.eventQueue.add(event);
@@ -314,21 +345,44 @@ public class OpenViduEventManager {
}
private boolean hasAudioTracks(WebElement videoElement, String parentSelector) {
- boolean audioTracks = (boolean) ((JavascriptExecutor) driver).executeScript(
- "return ((document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
- + videoElement.getAttribute("id") + "').srcObject.getAudioTracks().length > 0)"
- + "&& (document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
- + videoElement.getAttribute("id") + "').srcObject.getAudioTracks()[0].enabled))");
+ String script = "return ((document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ")
+ + "#" + videoElement.getAttribute("id") + "').srcObject.getAudioTracks().length > 0)"
+ + " && (document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
+ + videoElement.getAttribute("id") + "').srcObject.getAudioTracks()[0].enabled))";
+ boolean audioTracks = (boolean) ((JavascriptExecutor) driver).executeScript(script);
return audioTracks;
}
private boolean hasVideoTracks(WebElement videoElement, String parentSelector) {
- boolean videoTracks = (boolean) ((JavascriptExecutor) driver).executeScript(
- "return ((document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
- + videoElement.getAttribute("id") + "').srcObject.getVideoTracks().length > 0)"
- + "&& (document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
- + videoElement.getAttribute("id") + "').srcObject.getVideoTracks()[0].enabled))");
+ String script = "return ((document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ")
+ + "#" + videoElement.getAttribute("id") + "').srcObject.getVideoTracks().length > 0)"
+ + " && (document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ") + "#"
+ + videoElement.getAttribute("id") + "').srcObject.getVideoTracks()[0].enabled))";
+ boolean videoTracks = (boolean) ((JavascriptExecutor) driver).executeScript(script);
return videoTracks;
}
+ private boolean waitUntilSrcObjectDefined(WebElement videoElement, String parentSelector, int maxMsWait) {
+ final int sleepInterval = 50;
+ int maxIterations = maxMsWait / sleepInterval;
+ int counter = 0;
+ boolean defined = srcObjectDefined(videoElement, parentSelector);
+ while (!defined && counter < maxIterations) {
+ try {
+ Thread.sleep(sleepInterval);
+ } catch (InterruptedException e) {
+ }
+ defined = srcObjectDefined(videoElement, parentSelector);
+ counter++;
+ }
+ return defined;
+ }
+
+ private boolean srcObjectDefined(WebElement videoElement, String parentSelector) {
+ String script = "return (!!(document.querySelector('" + parentSelector + (parentSelector.isEmpty() ? "" : " ")
+ + "#" + videoElement.getAttribute("id") + "').srcObject))";
+ boolean defined = (boolean) ((JavascriptExecutor) driver).executeScript(script);
+ return defined;
+ }
+
}
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java
new file mode 100644
index 00000000..f56be54f
--- /dev/null
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java
@@ -0,0 +1,632 @@
+package io.openvidu.test.e2e;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.http.HttpStatus;
+import org.junit.Assert;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.Alert;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.stream.JsonReader;
+import com.mashape.unirest.http.HttpMethod;
+
+import io.openvidu.java.client.Connection;
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.ConnectionType;
+import io.openvidu.java.client.KurentoOptions;
+import io.openvidu.java.client.OpenVidu;
+import io.openvidu.java.client.OpenViduHttpException;
+import io.openvidu.java.client.OpenViduRole;
+import io.openvidu.java.client.Recording;
+import io.openvidu.java.client.Session;
+import io.openvidu.test.browsers.utils.CustomHttpClient;
+import io.openvidu.test.browsers.utils.Unzipper;
+
+public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
+
+ protected volatile static boolean isNetworkQualityTest;
+
+ @BeforeAll()
+ protected static void setupAll() {
+ checkFfmpegInstallation();
+ loadEnvironmentVariables();
+ setupBrowserDrivers();
+ cleanFoldersAndSetUpOpenViduJavaClient();
+ }
+
+ @Override
+ @AfterEach
+ protected void dispose() {
+ super.dispose();
+ if (isNetworkQualityTest) {
+ // Disable network quality API
+ try {
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ if (restClient.rest(HttpMethod.GET, "/openvidu/api/config", 200).get("OPENVIDU_PRO_NETWORK_QUALITY")
+ .getAsBoolean()) {
+ String body = "{'OPENVIDU_PRO_NETWORK_QUALITY':false}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/restart", body, 200);
+ waitUntilOpenViduRestarted(30);
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ Assert.fail("Error restarting OpenVidu Server to disable Network quality API");
+ } finally {
+ isNetworkQualityTest = false;
+ }
+ }
+ }
+
+ @Test
+ @DisplayName("Individual dynamic record")
+ void individualDynamicRecordTest() throws Exception {
+ isRecordingTest = true;
+
+ setupBrowser("chrome");
+
+ log.info("Individual dynamic record");
+
+ CustomHttpClient restClient = new CustomHttpClient(OpenViduTestAppE2eTest.OPENVIDU_URL, "OPENVIDUAPP",
+ OpenViduTestAppE2eTest.OPENVIDU_SECRET);
+
+ // Connect 3 users. Record only the first one
+ for (int i = 0; i < 3; i++) {
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ if (i > 0) {
+ user.getDriver().findElement(By.id("session-settings-btn-" + i)).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("record-checkbox")).click();
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+ }
+ }
+
+ String sessionName = "TestSession";
+
+ user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER));
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 9);
+
+ // Start the recording for one of the not recorded users
+ JsonObject sessionInfo = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/" + sessionName,
+ HttpStatus.SC_OK);
+ JsonArray connections = sessionInfo.get("connections").getAsJsonObject().get("content").getAsJsonArray();
+ String connectionId1 = null;
+ String streamId1 = null;
+ // Get connectionId and streamId
+ for (JsonElement connection : connections) {
+ if (connection.getAsJsonObject().get("record").getAsBoolean()) {
+ connectionId1 = connection.getAsJsonObject().get("connectionId").getAsString();
+ streamId1 = connection.getAsJsonObject().get("publishers").getAsJsonArray().get(0).getAsJsonObject()
+ .get("streamId").getAsString();
+ break;
+ }
+ }
+
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
+ "{'session':'" + sessionName + "','outputMode':'INDIVIDUAL'}", HttpStatus.SC_OK);
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 3);
+ Thread.sleep(1000);
+
+ // Start the recording for one of the not recorded users
+ sessionInfo = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/" + sessionName, HttpStatus.SC_OK);
+ connections = sessionInfo.get("connections").getAsJsonObject().get("content").getAsJsonArray();
+ String connectionId2 = null;
+ String streamId2 = null;
+ // Get connectionId and streamId
+ for (JsonElement connection : connections) {
+ if (!connection.getAsJsonObject().get("record").getAsBoolean()) {
+ connectionId2 = connection.getAsJsonObject().get("connectionId").getAsString();
+ streamId2 = connection.getAsJsonObject().get("publishers").getAsJsonArray().get(0).getAsJsonObject()
+ .get("streamId").getAsString();
+ break;
+ }
+ }
+
+ // Generate 3 total recordings of 1 second length for this same stream
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
+ "{'record':true}", HttpStatus.SC_OK);
+ Thread.sleep(1000);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
+ "{'record':false}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
+ "{'record':true}", HttpStatus.SC_OK);
+ Thread.sleep(1000);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
+ "{'record':false}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
+ "{'record':true}", HttpStatus.SC_OK);
+ Thread.sleep(1000);
+
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/" + sessionName, HttpStatus.SC_OK);
+ user.getEventManager().waitUntilEventReaches("recordingStopped", 3);
+
+ gracefullyLeaveParticipants(3);
+
+ String recPath = "/opt/openvidu/recordings/" + sessionName + "/";
+ Recording recording = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL, OpenViduTestAppE2eTest.OPENVIDU_SECRET)
+ .getRecording(sessionName);
+ this.recordingUtils.checkIndividualRecording(recPath, recording, 4, "opus", "vp8", true);
+
+ // Analyze INDIVIDUAL recording metadata
+ new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
+ File jsonSyncFile = new File(recPath + recording.getName() + ".json");
+ JsonReader reader = new JsonReader(new FileReader(jsonSyncFile));
+ JsonObject jsonMetadata = new Gson().fromJson(reader, JsonObject.class);
+ JsonArray syncArray = jsonMetadata.get("files").getAsJsonArray();
+ int count1 = 0;
+ int count2 = 0;
+ List names = Stream.of(streamId2 + ".webm", streamId2 + "-1.webm", streamId2 + "-2.webm")
+ .collect(Collectors.toList());
+ for (JsonElement fileJson : syncArray) {
+ JsonObject file = fileJson.getAsJsonObject();
+ String fileStreamId = file.get("streamId").getAsString();
+ if (fileStreamId.equals(streamId1)) {
+ // Normal recorded user
+ Assert.assertEquals("Wrong connectionId file metadata property", connectionId1,
+ file.get("connectionId").getAsString());
+ long msDuration = file.get("endTimeOffset").getAsLong() - file.get("startTimeOffset").getAsLong();
+ Assert.assertTrue("Wrong recording duration of individual file. Difference: " + (msDuration - 4000),
+ msDuration - 4000 < 750);
+ count1++;
+ } else if (fileStreamId.equals(streamId2)) {
+ // Dynamically recorded user
+ Assert.assertEquals("Wrong connectionId file metadata property", connectionId2,
+ file.get("connectionId").getAsString());
+ long msDuration = file.get("endTimeOffset").getAsLong() - file.get("startTimeOffset").getAsLong();
+ Assert.assertTrue(
+ "Wrong recording duration of individual file. Difference: " + Math.abs(msDuration - 1000),
+ Math.abs(msDuration - 1000) < 150);
+ Assert.assertTrue("File name not found among " + names.toString(),
+ names.remove(file.get("name").getAsString()));
+ count2++;
+ } else {
+ Assert.fail("Metadata file element does not belong to a known stream (" + fileStreamId + ")");
+ }
+ }
+ Assert.assertEquals("Wrong number of recording files for stream " + streamId1, 1, count1);
+ Assert.assertEquals("Wrong number of recording files for stream " + streamId2, 3, count2);
+ Assert.assertTrue("Some expected file name didn't existed: " + names.toString(), names.isEmpty());
+ }
+
+ @Test
+ @DisplayName("REST API PRO test")
+ void restApiProTest() throws Exception {
+
+ log.info("REST API PRO test");
+
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+
+ /**
+ * PATCH /openvidu/api/sessions//connection/
+ **/
+ String body = "{'customSessionId': 'CUSTOM_SESSION_ID'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK);
+ body = "{'role':'PUBLISHER','record':false,'data':'MY_SERVER_PRO_DATA'}";
+ JsonObject res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_OK);
+ final String token = res.get("token").getAsString();
+ final String connectionId = res.get("connectionId").getAsString();
+ final long createdAt = res.get("createdAt").getAsLong();
+
+ /** UPDATE PENDING CONNECTION **/
+
+ // Test with REST API
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':false}", HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'record':123}", HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'PUBLISHER','record':'WRONG'}", HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/WRONG/connection/" + connectionId,
+ "{'role':'PUBLISHER','record':'WRONG'}", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/WRONG",
+ "{'role':'PUBLISHER','record':true}", HttpStatus.SC_NOT_FOUND);
+
+ // No change should return 200. At this point role=PUBLISHER and record=false
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId, "{}",
+ HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'PUBLISHER'}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'record':false}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'PUBLISHER','record':false,'data':'OTHER_DATA'}", HttpStatus.SC_OK);
+
+ // Updating only role should let record value untouched
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'MODERATOR'}", HttpStatus.SC_OK, true, true, true,
+ mergeJson(DEFAULT_JSON_PENDING_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'MODERATOR','serverData':'MY_SERVER_PRO_DATA','record':false,'token':'"
+ + token + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + "}",
+ new String[0]));
+ // Updating only record should let role value untouched
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'record':true}", HttpStatus.SC_OK, true, true, true,
+ mergeJson(DEFAULT_JSON_PENDING_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'MODERATOR','serverData':'MY_SERVER_PRO_DATA','token':'" + token
+ + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + "}",
+ new String[0]));
+
+ // Test with openvidu-java-client
+ OpenVidu OV = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL, OpenViduTestAppE2eTest.OPENVIDU_SECRET);
+ Assert.assertTrue("OpenVidu object should have changed", OV.fetch());
+ Session session = OV.getActiveSessions().get(0);
+ try {
+ session.updateConnection("WRONG_CONNECTION_ID", new ConnectionProperties.Builder().build());
+ Assert.fail("Expected OpenViduHttpException exception");
+ } catch (OpenViduHttpException exception) {
+ Assert.assertEquals("Wrong HTTP status", HttpStatus.SC_NOT_FOUND, exception.getStatus());
+ }
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ Connection connection = session.updateConnection(connectionId,
+ new ConnectionProperties.Builder().role(OpenViduRole.SUBSCRIBER).record(false).build());
+ Assert.assertEquals("Wrong role Connection property", OpenViduRole.SUBSCRIBER, connection.getRole());
+ Assert.assertFalse("Wrong record Connection property", connection.record());
+ Assert.assertEquals("Wrong data Connection property", "MY_SERVER_PRO_DATA", connection.getServerData());
+
+ setupBrowser("chrome");
+
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+
+ // Set token
+ WebElement tokenInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokenInput.clear();
+ tokenInput.sendKeys(token);
+ // Force publishing even SUBSCRIBER
+ user.getDriver().findElement(By.id("force-publishing-checkbox")).click();
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .join-btn")).sendKeys(Keys.ENTER);
+
+ try {
+ user.getWaiter().until(ExpectedConditions.alertIsPresent());
+ Alert alert = user.getDriver().switchTo().alert();
+ Assert.assertTrue("Alert does not contain expected text",
+ alert.getText().equals("OPENVIDU_PERMISSION_DENIED: You don't have permissions to publish"));
+ alert.accept();
+ } catch (Exception e) {
+ Assert.fail("Alert exception");
+ }
+ Thread.sleep(500);
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+
+ Assert.assertTrue("Session object should have changed", session.fetch());
+ connection = session.getActiveConnections().get(0);
+ final Long activeAt = connection.activeAt();
+ Assert.assertTrue("activeAt should be greater than createdAt in Connection object", activeAt > createdAt);
+ Assert.assertEquals("Wrong role in Connection object", OpenViduRole.SUBSCRIBER, connection.getRole());
+ Assert.assertFalse("Wrong record in Connection object", connection.record());
+
+ /** UPDATE ACTIVE CONNECTION **/
+
+ // Test with REST API
+
+ // No change should return 200. At this point role=SUBSCRIBER and record=false
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId, "{}",
+ HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'SUBSCRIBER'}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'record':false}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'SUBSCRIBER','record':false}", HttpStatus.SC_OK);
+
+ // Updating only role should let record value untouched
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'MODERATOR'}", HttpStatus.SC_OK, false, true, true,
+ mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'MODERATOR','record':false,'token':'" + token
+ + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':"
+ + activeAt + ",'serverData':'MY_SERVER_PRO_DATA'}",
+ new String[] { "location", "platform", "clientData" }));
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 1);
+
+ // Updating only record should let role value untouched
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'record':true}", HttpStatus.SC_OK, false, true, true,
+ mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'MODERATOR','record':true,'token':'" + token
+ + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':"
+ + activeAt + ",'serverData':'MY_SERVER_PRO_DATA'}",
+ new String[] { "location", "platform", "clientData" }));
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 2);
+
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'SUBSCRIBER','record':true,'data':'OTHER DATA'}", HttpStatus.SC_OK, false, true, true,
+ mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'SUBSCRIBER','record':true,'token':'" + token
+ + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':"
+ + activeAt + ",'serverData':'MY_SERVER_PRO_DATA'}",
+ new String[] { "location", "platform", "clientData" }));
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 3);
+
+ restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ "{'role':'PUBLISHER'}", HttpStatus.SC_OK, false, true, true,
+ mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION,
+ "{'id':'" + connectionId + "','connectionId':'" + connectionId
+ + "','role':'PUBLISHER','record':true,'token':'" + token
+ + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':"
+ + activeAt + ",'serverData':'MY_SERVER_PRO_DATA'}",
+ new String[] { "location", "platform", "clientData" }));
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 4);
+
+ // Test with openvidu-node-client
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("connection-id-field")).clear();
+ user.getDriver().findElement(By.id("connection-id-field")).sendKeys(connectionId);
+ user.getDriver().findElement(By.id("update-connection-api-btn")).click();
+ user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
+ "Connection updated: {\"role\":\"PUBLISHER\",\"record\":true,\"data\":\"MY_SERVER_PRO_DATA\"}"));
+ user.getDriver().findElement(By.id("record-checkbox")).click();
+ user.getDriver().findElement(By.id("token-role-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-SUBSCRIBER")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("update-connection-api-btn")).click();
+ user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
+ "Connection updated: {\"role\":\"SUBSCRIBER\",\"record\":false,\"data\":\"MY_SERVER_PRO_DATA\"}"));
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 6);
+
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+
+ // Test with openvidu-java-client
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ try {
+ session.updateConnection("WRONG_CONNECTION_ID", new ConnectionProperties.Builder().build());
+ Assert.fail("Expected OpenViduHttpException exception");
+ } catch (OpenViduHttpException exception) {
+ Assert.assertEquals("Wrong HTTP status", HttpStatus.SC_NOT_FOUND, exception.getStatus());
+ }
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ connection = session.updateConnection(connectionId,
+ new ConnectionProperties.Builder().role(OpenViduRole.PUBLISHER).build());
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 7);
+
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ Assert.assertEquals("Wrong connectionId in Connection object", connectionId, connection.getConnectionId());
+ Assert.assertEquals("Wrong role in Connection object", OpenViduRole.PUBLISHER, connection.getRole());
+ Assert.assertFalse("Wrong record in Connection object", connection.record());
+ Assert.assertEquals("Wrong status in Connection object", "active", connection.getStatus());
+ connection = session.updateConnection(connectionId,
+ new ConnectionProperties.Builder().role(OpenViduRole.SUBSCRIBER).build());
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 8);
+
+ Assert.assertEquals("Wrong role in Connection object", OpenViduRole.SUBSCRIBER, connection.getRole());
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ connection = session.updateConnection(connectionId, new ConnectionProperties.Builder()
+ .role(OpenViduRole.MODERATOR).record(false).data("NO CHANGE").build());
+
+ user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 9);
+
+ Assert.assertFalse("Session object should not have changed", session.fetch());
+ Assert.assertEquals("Wrong role in Connection object", OpenViduRole.MODERATOR, connection.getRole());
+ Assert.assertFalse("Wrong record in Connection object", connection.record());
+ Assert.assertEquals("Wrong data in Connection object", "MY_SERVER_PRO_DATA", connection.getServerData());
+ Assert.assertEquals("Wrong status in Connection object", "active", connection.getStatus());
+
+ user.getEventManager().resetEventThread();
+
+ user.getWaiter().until(ExpectedConditions.elementToBeClickable(By.cssSelector(".republish-error-btn")));
+ user.getDriver().findElement(By.cssSelector(".republish-error-btn")).click();
+
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 1);
+
+ // connectionId should be equal to the one brought by the token
+ Assert.assertEquals("Wrong connectionId", connectionId,
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_OK)
+ .get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject()
+ .get("connectionId").getAsString());
+
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
+
+ // GET /openvidu/api/sessions should return empty again
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", null, HttpStatus.SC_OK, true, true, true,
+ "{'numberOfElements':0,'content':[]}");
+
+ /** GET /openvidu/api/config **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/config", null, HttpStatus.SC_OK, true, false, true,
+ "{'VERSION':'STR','DOMAIN_OR_PUBLIC_IP':'STR','HTTPS_PORT':0,'OPENVIDU_PUBLICURL':'STR','OPENVIDU_CDR':false,'OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH':0,'OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH':0,"
+ + "'OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH':0,'OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH':0,'OPENVIDU_SESSIONS_GARBAGE_INTERVAL':0,'OPENVIDU_SESSIONS_GARBAGE_THRESHOLD':0,"
+ + "'OPENVIDU_RECORDING':false,'OPENVIDU_RECORDING_VERSION':'STR','OPENVIDU_RECORDING_PATH':'STR','OPENVIDU_RECORDING_PUBLIC_ACCESS':false,'OPENVIDU_RECORDING_NOTIFICATION':'STR',"
+ + "'OPENVIDU_RECORDING_CUSTOM_LAYOUT':'STR','OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT':0,'OPENVIDU_WEBHOOK':false,'OPENVIDU_SERVER_DEPENDENCY_VERSION':'STR','KMS_URIS':[],"
+ + "'OPENVIDU_PRO_STATS_MONITORING_INTERVAL':0,'OPENVIDU_PRO_STATS_WEBRTC_INTERVAL':0,'OPENVIDU_PRO_CLUSTER_ID':'STR',"
+ + "'OPENVIDU_PRO_CLUSTER_ENVIRONMENT':'STR','OPENVIDU_PRO_CLUSTER_MEDIA_NODES':0,'OPENVIDU_PRO_CLUSTER_PATH':'STR','OPENVIDU_PRO_CLUSTER_AUTOSCALING':false,"
+ + "'OPENVIDU_PRO_ELASTICSEARCH':true,'OPENVIDU_PRO_ELASTICSEARCH_VERSION':'STR','OPENVIDU_PRO_ELASTICSEARCH_HOST':'STR','OPENVIDU_PRO_KIBANA':true,'OPENVIDU_PRO_KIBANA_VERSION':'STR',"
+ + "'OPENVIDU_PRO_KIBANA_HOST':'STR','OPENVIDU_PRO_RECORDING_STORAGE':'STR','OPENVIDU_PRO_NETWORK_QUALITY':false,'OPENVIDU_STREAMS_ALLOW_TRANSCODING':false,'OPENVIDU_STREAMS_FORCED_VIDEO_CODEC':'STR'}");
+
+ /** GET /openvidu/api/health **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/health", null, HttpStatus.SC_OK, true, true, true,
+ "{'status':'UP'}");
+ }
+
+ @Test
+ @DisplayName("openvidu-java-client PRO test")
+ void openViduJavaClientProTest() throws Exception {
+
+ log.info("openvidu-java-client PRO test");
+
+ // Create default Connection
+ Session session = OV.createSession();
+ Assert.assertFalse(session.fetch());
+ Connection connectionDefault = session.createConnection();
+ Assert.assertFalse(session.fetch());
+ Assert.assertEquals("Wrong role property", OpenViduRole.PUBLISHER, connectionDefault.getRole());
+ Assert.assertTrue("Wrong record property", connectionDefault.record());
+ Assert.assertEquals("Wrong data property", "", connectionDefault.getServerData());
+ // Update Connection
+ session.updateConnection(connectionDefault.getConnectionId(), new ConnectionProperties.Builder()
+ .role(OpenViduRole.SUBSCRIBER).record(false).data("WILL HAVE NO EFFECT").build());
+ Assert.assertEquals("Wrong role property", OpenViduRole.SUBSCRIBER, connectionDefault.getRole());
+ Assert.assertFalse("Wrong record property", connectionDefault.record());
+ Assert.assertEquals("Wrong data property", "", connectionDefault.getServerData());
+ Assert.assertFalse(session.fetch());
+
+ // Create custom properties Connection
+ long timestamp = System.currentTimeMillis();
+ Connection connection = session.createConnection(
+ new ConnectionProperties.Builder().record(false).role(OpenViduRole.MODERATOR).data("SERVER_SIDE_DATA")
+ .kurentoOptions(new KurentoOptions.Builder().videoMaxRecvBandwidth(555)
+ .videoMinRecvBandwidth(555).videoMaxSendBandwidth(555).videoMinSendBandwidth(555)
+ .allowedFilters(new String[] { "555" }).build())
+ .build());
+ Assert.assertEquals("Wrong status Connection property", "pending", connection.getStatus());
+ Assert.assertTrue("Wrong timestamp Connection property", connection.createdAt() > timestamp);
+ Assert.assertTrue("Wrong activeAt Connection property", connection.activeAt() == null);
+ Assert.assertTrue("Wrong location Connection property", connection.getLocation() == null);
+ Assert.assertTrue("Wrong platform Connection property", connection.getPlatform() == null);
+ Assert.assertTrue("Wrong clientData Connection property", connection.getClientData() == null);
+ Assert.assertTrue("Wrong publishers Connection property", connection.getPublishers().size() == 0);
+ Assert.assertTrue("Wrong subscribers Connection property", connection.getSubscribers().size() == 0);
+ Assert.assertTrue("Wrong token Connection property", connection.getToken().contains(session.getSessionId()));
+ Assert.assertEquals("Wrong type property", ConnectionType.WEBRTC, connection.getType());
+ Assert.assertEquals("Wrong data property", "SERVER_SIDE_DATA", connection.getServerData());
+ Assert.assertFalse("Wrong record property", connection.record());
+ Assert.assertEquals("Wrong role property", OpenViduRole.MODERATOR, connection.getRole());
+ Assert.assertTrue("Wrong rtspUri property", connection.getRtspUri() == null);
+ Assert.assertTrue("Wrong adaptativeBitrate property", connection.adaptativeBitrate() == null);
+ Assert.assertTrue("Wrong onlyPlayWithSubscribers property", connection.onlyPlayWithSubscribers() == null);
+ Assert.assertTrue("Wrong networkCache property", connection.getNetworkCache() == null);
+ }
+
+ @Test
+ @DisplayName("Network quality test")
+ void networkQualityTest() throws Exception {
+
+ isNetworkQualityTest = true;
+
+ log.info("Network quality test");
+
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ String body = "{'OPENVIDU_PRO_NETWORK_QUALITY':true, 'OPENVIDU_PRO_NETWORK_QUALITY_INTERVAL':5}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/restart", body, 200);
+ waitUntilOpenViduRestarted(30);
+
+ setupBrowser("chrome");
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.className("join-btn")).click();
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 1);
+ JsonObject res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/TestSession/connection",
+ HttpStatus.SC_OK);
+ final String connectionId = res.getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject()
+ .get("id").getAsString();
+
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .publish-checkbox")).click();
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
+
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ Queue threadAssertions = new ConcurrentLinkedQueue();
+ user.getEventManager().on("networkQualityLevelChanged", (event) -> {
+ try {
+ threadAssertions.add("networkQualityLevelChanged".equals(event.get("type").getAsString()));
+ threadAssertions.add(event.get("oldValue") == null);
+ threadAssertions.add(event.has("newValue") && event.get("newValue").getAsInt() > 0
+ && event.get("newValue").getAsInt() < 6);
+ latch1.countDown();
+ } catch (Exception e) {
+ log.error("Error analysing NetworkQualityLevelChangedEvent: {}. {}", e.getCause(), e.getMessage());
+ fail("Error analysing NetworkQualityLevelChangedEvent: " + e.getCause() + ". " + e.getMessage());
+ }
+ });
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+ user.getEventManager().waitUntilEventReaches("networkQualityLevelChanged", 2);
+
+ if (!latch1.await(30000, TimeUnit.MILLISECONDS)) {
+ gracefullyLeaveParticipants(1);
+ fail();
+ return;
+ }
+
+ user.getEventManager().off("networkQualityLevelChanged");
+ log.info("Thread assertions: {}", threadAssertions.toString());
+ for (Iterator iter = threadAssertions.iterator(); iter.hasNext();) {
+ Assert.assertTrue("Some Event property was wrong", iter.next());
+ iter.remove();
+ }
+
+ // Both events should have publisher's connection ID
+ Assert.assertTrue("Wrong connectionId in event NetworkQualityLevelChangedEvent", user.getDriver()
+ .findElement(By.cssSelector("#openvidu-instance-0 .mat-expansion-panel:last-child .event-content"))
+ .getAttribute("textContent").contains(connectionId));
+ Assert.assertTrue("Wrong connectionId in event NetworkQualityLevelChangedEvent", user.getDriver()
+ .findElement(By.cssSelector("#openvidu-instance-1 .mat-expansion-panel:last-child .event-content"))
+ .getAttribute("textContent").contains(connectionId));
+
+ gracefullyLeaveParticipants(1);
+ }
+
+ private void waitUntilOpenViduRestarted(int maxSecondsWait) throws Exception {
+ boolean restarted = false;
+ int msInterval = 500;
+ int attempts = 0;
+ final int maxAttempts = maxSecondsWait * 1000 / msInterval;
+ Thread.sleep(500);
+ while (!restarted && attempts < maxAttempts) {
+ try {
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/health", 200);
+ restarted = true;
+ } catch (Exception e) {
+ try {
+ log.warn("Waiting for OpenVidu Server...");
+ Thread.sleep(msInterval);
+ } catch (InterruptedException e1) {
+ log.error("Sleep interrupted");
+ }
+ attempts++;
+ }
+ }
+ if (!restarted && attempts == maxAttempts) {
+ throw new TimeoutException();
+ }
+ }
+
+}
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
index 93ce5a63..287d0297 100644
--- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
@@ -18,24 +18,11 @@
package io.openvidu.test.e2e;
import static org.junit.Assert.fail;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.openqa.selenium.OutputType.BASE64;
-import java.awt.Color;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.math.RoundingMode;
-import java.nio.file.Path;
import java.nio.file.Paths;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -44,17 +31,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import javax.imageio.ImageIO;
-
-import org.apache.commons.io.FileUtils;
import org.apache.http.HttpStatus;
-import org.jcodec.api.FrameGrab;
-import org.jcodec.api.JCodecException;
-import org.jcodec.common.model.Picture;
-import org.jcodec.scale.AWTUtil;
import org.junit.Assert;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -63,30 +43,25 @@ import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Keys;
-import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.test.context.junit.jupiter.SpringExtension;
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.Gson;
import com.google.gson.JsonArray;
+import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-import com.google.gson.stream.JsonReader;
import com.mashape.unirest.http.HttpMethod;
-import io.github.bonigarcia.wdm.WebDriverManager;
import io.openvidu.java.client.Connection;
+import io.openvidu.java.client.ConnectionProperties;
+import io.openvidu.java.client.ConnectionType;
import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.OpenVidu;
import io.openvidu.java.client.OpenViduHttpException;
-import io.openvidu.java.client.OpenViduJavaClientException;
import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.Publisher;
import io.openvidu.java.client.Recording;
@@ -96,18 +71,12 @@ import io.openvidu.java.client.RecordingMode;
import io.openvidu.java.client.RecordingProperties;
import io.openvidu.java.client.Session;
import io.openvidu.java.client.SessionProperties;
-import io.openvidu.java.client.TokenOptions;
import io.openvidu.java.client.VideoCodec;
-import io.openvidu.test.browsers.BrowserUser;
-import io.openvidu.test.browsers.ChromeAndroidUser;
-import io.openvidu.test.browsers.ChromeUser;
import io.openvidu.test.browsers.FirefoxUser;
-import io.openvidu.test.browsers.OperaUser;
-import io.openvidu.test.browsers.utils.CommandLineExecutor;
import io.openvidu.test.browsers.utils.CustomHttpClient;
-import io.openvidu.test.browsers.utils.CustomWebhook;
-import io.openvidu.test.browsers.utils.MultimediaFileMetadata;
-import io.openvidu.test.browsers.utils.Unzipper;
+import io.openvidu.test.browsers.utils.RecordingUtils;
+import io.openvidu.test.browsers.utils.layout.CustomLayoutHandler;
+import io.openvidu.test.browsers.utils.webhook.CustomWebhook;
/**
* E2E tests for openvidu-testapp.
@@ -118,159 +87,15 @@ import io.openvidu.test.browsers.utils.Unzipper;
@Tag("e2e")
@DisplayName("E2E tests for OpenVidu TestApp")
@ExtendWith(SpringExtension.class)
-public class OpenViduTestAppE2eTest {
-
- static String OPENVIDU_SECRET = "MY_SECRET";
- static String OPENVIDU_URL = "https://localhost:4443/";
- static String APP_URL = "http://localhost:4200/";
- static Exception ex = null;
- private final Object lock = new Object();
-
- private static final Logger log = LoggerFactory.getLogger(OpenViduTestAppE2eTest.class);
- private static final CommandLineExecutor commandLine = new CommandLineExecutor();
- private static final String RECORDING_IMAGE = "openvidu/openvidu-recording";
-
- MyUser user;
- Collection otherUsers = new ArrayList<>();
- volatile static boolean isRecordingTest;
- volatile static boolean isKurentoRestartTest;
- private static OpenVidu OV;
+public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
@BeforeAll()
- static void setupAll() {
-
- String ffmpegOutput = commandLine.executeCommand("which ffmpeg");
- if (ffmpegOutput == null || ffmpegOutput.isEmpty()) {
- log.error("ffmpeg package is not installed in the host machine");
- Assert.fail();
- return;
- } else {
- log.info("ffmpeg is installed and accesible");
- }
-
- WebDriverManager.chromedriver().setup();
- WebDriverManager.firefoxdriver().setup();
-
- String appUrl = System.getProperty("APP_URL");
- if (appUrl != null) {
- APP_URL = appUrl;
- }
- log.info("Using URL {} to connect to openvidu-testapp", APP_URL);
-
- String openviduUrl = System.getProperty("OPENVIDU_URL");
- if (openviduUrl != null) {
- OPENVIDU_URL = openviduUrl;
- }
- log.info("Using URL {} to connect to openvidu-server", OPENVIDU_URL);
-
- String openvidusecret = System.getProperty("OPENVIDU_SECRET");
- if (openvidusecret != null) {
- OPENVIDU_SECRET = openvidusecret;
- }
- log.info("Using secret {} to connect to openvidu-server", OPENVIDU_SECRET);
-
- try {
- log.info("Cleaning folder /opt/openvidu/recordings");
- FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
- } catch (IOException e) {
- log.error(e.getMessage());
- }
- OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
- }
-
- void setupBrowser(String browser) {
-
- BrowserUser browserUser;
-
- switch (browser) {
- case "chrome":
- browserUser = new ChromeUser("TestUser", 50, false);
- break;
- case "firefox":
- browserUser = new FirefoxUser("TestUser", 50);
- break;
- case "opera":
- browserUser = new OperaUser("TestUser", 50);
- break;
- case "chromeAndroid":
- browserUser = new ChromeAndroidUser("TestUser", 50);
- break;
- case "chromeAlternateScreenShare":
- browserUser = new ChromeUser("TestUser", 50, "OpenVidu TestApp", false);
- break;
- case "chromeAsRoot":
- browserUser = new ChromeUser("TestUser", 50, true);
- break;
- default:
- browserUser = new ChromeUser("TestUser", 50, false);
- }
-
- this.user = new MyUser(browserUser);
-
- user.getDriver().get(APP_URL);
-
- WebElement urlInput = user.getDriver().findElement(By.id("openvidu-url"));
- urlInput.clear();
- urlInput.sendKeys(OPENVIDU_URL);
- WebElement secretInput = user.getDriver().findElement(By.id("openvidu-secret"));
- secretInput.clear();
- secretInput.sendKeys(OPENVIDU_SECRET);
-
- user.getEventManager().startPolling();
- }
-
- void setupChromeWithFakeVideo(Path videoFileLocation) {
- this.user = new MyUser(new ChromeUser("TestUser", 50, videoFileLocation));
- user.getDriver().get(APP_URL);
- WebElement urlInput = user.getDriver().findElement(By.id("openvidu-url"));
- urlInput.clear();
- urlInput.sendKeys(OPENVIDU_URL);
- WebElement secretInput = user.getDriver().findElement(By.id("openvidu-secret"));
- secretInput.clear();
- secretInput.sendKeys(OPENVIDU_SECRET);
- user.getEventManager().startPolling();
- }
-
- @AfterEach
- void dispose() {
- if (user != null) {
- user.dispose();
- }
- Iterator it = otherUsers.iterator();
- while (it.hasNext()) {
- MyUser other = it.next();
- other.dispose();
- it.remove();
- }
- try {
- OV.fetch();
- } catch (OpenViduJavaClientException | OpenViduHttpException e1) {
- log.error("Error fetching sessions: {}", e1.getMessage());
- }
- OV.getActiveSessions().forEach(session -> {
- try {
- session.close();
- log.info("Session {} successfully closed", session.getSessionId());
- } catch (OpenViduJavaClientException e) {
- log.error("Error closing session: {}", e.getMessage());
- } catch (OpenViduHttpException e) {
- log.error("Error closing session: {}", e.getMessage());
- }
- });
- if (isRecordingTest) {
- removeAllRecordingContiners();
- try {
- FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
- } catch (IOException e) {
- log.error(e.getMessage());
- }
- isRecordingTest = false;
- }
- if (isKurentoRestartTest) {
- this.restartKms();
- isKurentoRestartTest = false;
- }
- OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
+ protected static void setupAll() throws Exception {
+ checkFfmpegInstallation();
+ loadEnvironmentVariables();
+ setupBrowserDrivers();
+ cleanFoldersAndSetUpOpenViduJavaClient();
+ getDefaultTranscodingValues();
}
@Test
@@ -297,6 +122,54 @@ public class OpenViduTestAppE2eTest {
gracefullyLeaveParticipants(2);
}
+ @Test
+ @DisplayName("One2One Firefox [Video + Audio]")
+ void oneToOneVideoAudioSessionFirefox() throws Exception {
+
+ setupBrowser("firefox");
+
+ log.info("One2One Firefox [Video + Audio]");
+
+ user.getDriver().findElement(By.id("auto-join-checkbox")).click();
+ user.getDriver().findElement(By.id("one2one-btn")).click();
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
+
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 4 videos but found " + numberOfVideos, 4, numberOfVideos);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+
+ gracefullyLeaveParticipants(2);
+ }
+
+ @Test
+ @DisplayName("One2One Opera [Video + Audio]")
+ void oneToOneVideoAudioSessionOpera() throws Exception {
+
+ setupBrowser("opera");
+
+ log.info("One2One Opera [Video + Audio]");
+
+ user.getDriver().findElement(By.id("auto-join-checkbox")).click();
+ user.getDriver().findElement(By.id("one2one-btn")).click();
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
+
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 4 videos but found " + numberOfVideos, 4, numberOfVideos);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+
+ gracefullyLeaveParticipants(2);
+ }
+
@Test
@DisplayName("One2One Chrome [Video + Audio] - Force VP8")
void oneToOneVideoAudioSessionChromeForceVP8() throws Exception {
@@ -345,7 +218,7 @@ public class OpenViduTestAppE2eTest {
statButton.click();
Thread.sleep(1000);
String videoCodecUsed = user.getDriver().findElement(By.id("video-codec-used")).getText();
- assertEquals(videoCodecUsed, "video/" + codec.name());
+ Assert.assertEquals("Expected video codec", videoCodecUsed, "video/" + codec.name());
user.getDriver().findElement(By.id("close-dialog-btn")).click();
}
}
@@ -547,36 +420,10 @@ public class OpenViduTestAppE2eTest {
gracefullyLeaveParticipants(4);
}
- @Test
- @DisplayName("One2One Firefox [Video + Audio]")
- void oneToOneVideoAudioSessionFirefox() throws Exception {
-
- setupBrowser("firefox");
-
- log.info("One2One Firefox [Video + Audio]");
-
- user.getDriver().findElement(By.id("auto-join-checkbox")).click();
- user.getDriver().findElement(By.id("one2one-btn")).click();
-
- user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
- user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
- user.getEventManager().waitUntilEventReaches("streamCreated", 4);
- user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
-
- final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
- Assert.assertEquals("Expected 4 videos but found " + numberOfVideos, 4, numberOfVideos);
- Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
- .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
-
- gracefullyLeaveParticipants(2);
- }
-
@Test
@DisplayName("Cross-Browser test")
void crossBrowserTest() throws Exception {
- setupBrowser("chrome");
-
log.info("Cross-Browser test");
Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
@@ -588,8 +435,10 @@ public class OpenViduTestAppE2eTest {
}
};
- Thread t = new Thread(() -> {
- MyUser user2 = new MyUser(new FirefoxUser("TestUser", 30));
+ final CountDownLatch latch = new CountDownLatch(2);
+
+ Thread threadFirefox = new Thread(() -> {
+ MyUser user2 = new MyUser(new FirefoxUser("TestUser", 30, false));
otherUsers.add(user2);
user2.getDriver().get(APP_URL);
WebElement urlInput = user2.getDriver().findElement(By.id("openvidu-url"));
@@ -614,36 +463,61 @@ public class OpenViduTestAppE2eTest {
Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
.assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+ latch.countDown();
+ if (!latch.await(30, TimeUnit.SECONDS)) {
+ Assert.fail("The other browser didn't play the stream within the timeout");
+ }
+
user2.getEventManager().waitUntilEventReaches("streamDestroyed", 1);
user2.getEventManager().waitUntilEventReaches("connectionDestroyed", 1);
user2.getDriver().findElement(By.id("remove-user-btn")).click();
user2.getEventManager().waitUntilEventReaches("sessionDisconnected", 1);
} catch (Exception e) {
e.printStackTrace();
- user2.dispose();
Thread.currentThread().interrupt();
+ Assert.fail("Exception on Firefox participant: " + e.getMessage());
+ } finally {
+ user2.dispose();
}
- user2.dispose();
});
- t.setUncaughtExceptionHandler(h);
- t.start();
- user.getDriver().findElement(By.id("add-user-btn")).click();
- user.getDriver().findElement(By.className("join-btn")).click();
+ Thread threadChrome = new Thread(() -> {
+ setupBrowser("chrome");
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.className("join-btn")).click();
- user.getEventManager().waitUntilEventReaches("connectionCreated", 2);
- user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
- user.getEventManager().waitUntilEventReaches("streamCreated", 2);
- user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+ try {
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 2);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 2);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
- final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
- Assert.assertEquals("Expected 2 videos but found " + numberOfVideos, 2, numberOfVideos);
- Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
- .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 2 videos but found " + numberOfVideos, 2, numberOfVideos);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
- gracefullyLeaveParticipants(1);
+ latch.countDown();
+ if (!latch.await(30, TimeUnit.SECONDS)) {
+ Assert.fail("The other browser didn't play the stream within the timeout");
+ }
- t.join();
+ gracefullyLeaveParticipants(1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail("Exception on Chrome participant: " + e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ user.dispose();
+ }
+ });
+
+ threadFirefox.setUncaughtExceptionHandler(h);
+ threadChrome.setUncaughtExceptionHandler(h);
+ threadFirefox.start();
+ threadChrome.start();
+ threadFirefox.join();
+ threadChrome.join();
synchronized (lock) {
if (OpenViduTestAppE2eTest.ex != null) {
@@ -1007,9 +881,13 @@ public class OpenViduTestAppE2eTest {
user.getEventManager().waitUntilEventReaches("streamCreated", 2);
user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+ // Give some time for the screen sharing warning to stop resizing the viewport
+ Thread.sleep(3000);
+
// Unpublish video
final CountDownLatch latch1 = new CountDownLatch(2);
user.getEventManager().on("streamPropertyChanged", (event) -> {
+ System.out.println(event.toString());
threadAssertions.add("videoActive".equals(event.get("changedProperty").getAsString()));
threadAssertions.add(!event.get("newValue").getAsBoolean());
latch1.countDown();
@@ -1033,6 +911,7 @@ public class OpenViduTestAppE2eTest {
// Unpublish audio
final CountDownLatch latch2 = new CountDownLatch(2);
user.getEventManager().on("streamPropertyChanged", (event) -> {
+ System.out.println(event.toString());
threadAssertions.add("audioActive".equals(event.get("changedProperty").getAsString()));
threadAssertions.add(!event.get("newValue").getAsBoolean());
latch2.countDown();
@@ -1065,9 +944,11 @@ public class OpenViduTestAppE2eTest {
+ "}";
System.out.println("Publisher dimensions: " + event.get("newValue").getAsJsonObject().toString());
System.out.println("Real dimensions of viewport: " + expectedDimensions);
- threadAssertions.add("videoDimensions".equals(event.get("changedProperty").getAsString()));
- threadAssertions.add(expectedDimensions.equals(event.get("newValue").getAsJsonObject().toString()));
- latch3.countDown();
+ if ("videoDimensions".equals(event.get("changedProperty").getAsString())) {
+ if (expectedDimensions.equals(event.get("newValue").getAsJsonObject().toString())) {
+ latch3.countDown();
+ }
+ }
});
user.getDriver().manage().window().setSize(new Dimension(newWidth, newHeight));
@@ -1082,7 +963,7 @@ public class OpenViduTestAppE2eTest {
user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 6);
- if (!latch3.await(5000, TimeUnit.MILLISECONDS)) {
+ if (!latch3.await(6000, TimeUnit.MILLISECONDS)) {
gracefullyLeaveParticipants(2);
fail();
return;
@@ -1091,22 +972,17 @@ public class OpenViduTestAppE2eTest {
System.out.println(getBase64Screenshot(user));
user.getEventManager().off("streamPropertyChanged");
- log.info("Thread assertions: {}", threadAssertions.toString());
- for (Iterator iter = threadAssertions.iterator(); iter.hasNext();) {
- Assert.assertTrue("Some Event property was wrong", iter.next());
- iter.remove();
- }
gracefullyLeaveParticipants(2);
}
@Test
- @DisplayName("Local record")
- void localRecordTest() throws Exception {
+ @DisplayName("Local browser record")
+ void localBrowserRecordTest() throws Exception {
setupBrowser("chrome");
- log.info("Local record");
+ log.info("Local browser record");
user.getDriver().findElement(By.id("add-user-btn")).click();
user.getDriver().findElement(By.className("join-btn")).click();
@@ -1158,13 +1034,13 @@ public class OpenViduTestAppE2eTest {
}
@Test
- @DisplayName("Remote composed record")
- void remoteComposedRecordTest() throws Exception {
+ @DisplayName("Composed record")
+ void composedRecordTest() throws Exception {
isRecordingTest = true;
setupBrowser("chrome");
- log.info("Remote composed record");
+ log.info("Composed record");
final String sessionName = "COMPOSED_RECORDED_SESSION";
final String resolution = "1280x720";
@@ -1279,9 +1155,10 @@ public class OpenViduTestAppE2eTest {
Assert.assertTrue("File " + file3.getAbsolutePath() + " does not exist or is empty",
file3.exists() && file3.length() > 0);
- Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine",
- this.recordedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
- Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", this.thumbnailIsFine(file3));
+ Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordingUtils
+ .recordedGreenFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
+ Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine",
+ this.recordingUtils.thumbnailIsFine(file3, RecordingUtils::checkVideoAverageRgbGreen));
// Try to get the stopped recording
user.getDriver().findElement(By.id("get-recording-btn")).click();
@@ -1309,113 +1186,188 @@ public class OpenViduTestAppE2eTest {
}
@Test
- @DisplayName("Remote composed quick start record")
- void remoteComposedQuickStartRecordTest() throws Exception {
+ @DisplayName("Composed quick start record")
+ void composedQuickStartRecordTest() throws Exception {
isRecordingTest = true;
setupBrowser("chrome");
- log.info("Remote composed quick start record");
+ log.info("Composed quick start record");
- final String sessionName = "COMPOSED_QUICK_START_RECORDED_SESSION";
+ CountDownLatch initLatch = new CountDownLatch(1);
+ io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch);
- // 1. MANUAL mode and recording explicitly stopped
+ try {
- user.getDriver().findElement(By.id("add-user-btn")).click();
- user.getDriver().findElement(By.id("session-name-input-0")).clear();
- user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
+ if (!initLatch.await(30, TimeUnit.SECONDS)) {
+ Assert.fail("Timeout waiting for webhook springboot app to start");
+ CustomWebhook.shutDown();
+ return;
+ }
- user.getDriver().findElement(By.id("session-settings-btn-0")).click();
- Thread.sleep(1000);
- user.getDriver().findElement(By.id("output-mode-select")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("option-COMPOSED_QUICK_START")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("save-btn")).click();
- Thread.sleep(1000);
+ final String sessionName = "COMPOSED_QUICK_START_RECORDED_SESSION";
+ JsonObject event;
- // Join the subscriber user to the session
- user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .publish-checkbox")).click();
- user.getDriver().findElement(By.className("join-btn")).click();
- user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+ // 1. MANUAL mode and recording explicitly stopped
- // Check the recording container is up and running but no ongoing recordings
- checkDockerContainerRunning(RECORDING_IMAGE, 1);
- Assert.assertEquals("Wrong number of recordings found", 0, OV.listRecordings().size());
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-0")).clear();
+ user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
- // Join the publisher user to the session
- user.getDriver().findElement(By.id("add-user-btn")).click();
- user.getDriver().findElement(By.id("session-name-input-1")).clear();
- user.getDriver().findElement(By.id("session-name-input-1")).sendKeys(sessionName);
- user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("output-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-COMPOSED_QUICK_START")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
- user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
- user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
- user.getEventManager().waitUntilEventReaches("streamCreated", 2);
- user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+ // Join the subscriber user to the session
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .publish-checkbox")).click();
+ user.getDriver().findElement(By.className("join-btn")).click();
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
- // Start recording
- OV.fetch();
- String recId = OV.startRecording(sessionName).getId();
- user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
- checkDockerContainerRunning("openvidu/openvidu-recording", 1);
+ // Check the recording container is up and running but no ongoing recordings
+ checkDockerContainerRunning(RECORDING_IMAGE, 1);
+ Assert.assertEquals("Wrong number of recordings found", 0, OV.listRecordings().size());
- Thread.sleep(1000);
+ // Join the publisher user to the session
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-1")).clear();
+ user.getDriver().findElement(By.id("session-name-input-1")).sendKeys(sessionName);
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
- Assert.assertEquals("Wrong number of recordings found", 1, OV.listRecordings().size());
- OV.stopRecording(recId);
- user.getEventManager().waitUntilEventReaches("recordingStopped", 2);
- checkDockerContainerRunning("openvidu/openvidu-recording", 1);
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 2);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
- Assert.assertEquals("Wrong number of sessions", 1, OV.getActiveSessions().size());
- Session session = OV.getActiveSessions().get(0);
- session.close();
+ // Start recording
+ OV.fetch();
+ String recId = OV.startRecording(sessionName).getId();
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
+ CustomWebhook.waitForEvent("recordingStatusChanged", 5);
+ checkDockerContainerRunning("openvidu/openvidu-recording", 1);
- checkDockerContainerRunning("openvidu/openvidu-recording", 0);
+ Thread.sleep(2000);
- // 2. ALWAYS mode and recording stopped by session close up
- user.getDriver().findElement(By.id("remove-all-users-btn")).click();
- user.getDriver().findElement(By.id("add-user-btn")).click();
- user.getDriver().findElement(By.id("session-name-input-0")).clear();
- user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
+ Assert.assertEquals("Wrong number of recordings found", 1, OV.listRecordings().size());
+ OV.stopRecording(recId);
+ user.getEventManager().waitUntilEventReaches("recordingStopped", 2);
+ checkDockerContainerRunning("openvidu/openvidu-recording", 1);
- user.getDriver().findElement(By.id("session-settings-btn-0")).click();
- Thread.sleep(1000);
- user.getDriver().findElement(By.id("recording-mode-select")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("option-ALWAYS")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("output-mode-select")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("option-COMPOSED_QUICK_START")).click();
- Thread.sleep(500);
- user.getDriver().findElement(By.id("save-btn")).click();
- Thread.sleep(1000);
+ Assert.assertEquals("Wrong number of sessions", 1, OV.getActiveSessions().size());
+ Session session = OV.getActiveSessions().get(0);
+ session.close();
- user.getDriver().findElement(By.className("join-btn")).click();
- user.getEventManager().waitUntilEventReaches("connectionCreated", 5);
- user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
- user.getEventManager().waitUntilEventReaches("streamCreated", 3);
- user.getEventManager().waitUntilEventReaches("streamPlaying", 3);
- user.getEventManager().waitUntilEventReaches("recordingStarted", 3);
+ checkDockerContainerRunning("openvidu/openvidu-recording", 0);
- checkDockerContainerRunning("openvidu/openvidu-recording", 1);
+ Assert.assertEquals("Wrong recording status", Recording.Status.ready,
+ OV.getRecording(sessionName).getStatus());
- OV.fetch();
- session = OV.getActiveSessions().get(0);
- session.close();
+ // 2. ALWAYS mode and recording stopped by session close up
+ CustomWebhook.clean();
+ user.getDriver().findElement(By.id("remove-all-users-btn")).click();
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-0")).clear();
+ user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
- checkDockerContainerRunning("openvidu/openvidu-recording", 0);
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("recording-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-ALWAYS")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("output-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-COMPOSED_QUICK_START")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.className("join-btn")).click();
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 5);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 3);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 3);
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 3);
+
+ event = CustomWebhook.waitForEvent("recordingStatusChanged", 5); // started
+ Assert.assertEquals("Wrong status in recordingStatusChanged event", "started",
+ event.get("status").getAsString());
+
+ checkDockerContainerRunning("openvidu/openvidu-recording", 1);
+
+ OV.fetch();
+ session = OV.getActiveSessions().get(0);
+ session.close();
+
+ checkDockerContainerRunning("openvidu/openvidu-recording", 0);
+
+ Assert.assertEquals("Wrong recording status", Recording.Status.ready,
+ OV.getRecording(sessionName + "-1").getStatus());
+
+ // 3. Session closed before recording started should trigger
+ CustomWebhook.clean();
+ user.getDriver().findElement(By.id("remove-all-users-btn")).click();
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-0")).clear();
+ user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
+
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("recording-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-ALWAYS")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("output-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-COMPOSED_QUICK_START")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.className("join-btn")).click();
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 6);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 3);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
+ checkDockerContainerRunning("openvidu/openvidu-recording", 1);
+
+ OV.fetch();
+ session = OV.getActiveSessions().get(0);
+ session.close();
+
+ // Recording hasn't had time to start. Should trigger stopped, started, failed
+ event = CustomWebhook.waitForEvent("recordingStatusChanged", 1); // stopped
+ Assert.assertEquals("Wrong status in recordingStatusChanged event", "stopped",
+ event.get("status").getAsString());
+ event = CustomWebhook.waitForEvent("recordingStatusChanged", 5); // started
+ Assert.assertEquals("Wrong status in recordingStatusChanged event", "started",
+ event.get("status").getAsString());
+ event = CustomWebhook.waitForEvent("recordingStatusChanged", 1); // failed
+ Assert.assertEquals("Wrong status in recordingStatusChanged event", "failed",
+ event.get("status").getAsString());
+
+ checkDockerContainerRunning("openvidu/openvidu-recording", 0);
+
+ Assert.assertEquals("Wrong recording status", Recording.Status.failed,
+ OV.getRecording(sessionName + "-2").getStatus());
+
+ } finally {
+ CustomWebhook.shutDown();
+ }
}
@Test
- @DisplayName("Remote individual record")
- void remoteIndividualRecordTest() throws Exception {
+ @DisplayName("Individual record")
+ void individualRecordTest() throws Exception {
isRecordingTest = true;
setupBrowser("chrome");
- log.info("Remote individual record");
+ log.info("Individual record");
final String sessionName = "TestSession";
final String recordingName = "CUSTOM_NAME";
@@ -1489,7 +1441,7 @@ public class OpenViduTestAppE2eTest {
String recPath = recordingsPath + sessionName + "/";
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName);
- this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
+ this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
// Try to get the stopped recording
user.getDriver().findElement(By.id("get-recording-btn")).click();
@@ -1515,13 +1467,13 @@ public class OpenViduTestAppE2eTest {
}
@Test
- @DisplayName("Remote record cross-browser audio-only and video-only")
- void remoteRecordAudioOnlyVideoOnlyTest() throws Exception {
+ @DisplayName("Record cross-browser audio-only and video-only")
+ void audioOnlyVideoOnlyRecordTest() throws Exception {
isRecordingTest = true;
setupBrowser("chromeAlternateScreenShare");
- log.info("Remote record cross-browser audio-only and video-only");
+ log.info("Record cross-browser audio-only and video-only");
final String SESSION_NAME = "TestSession";
final String RECORDING_COMPOSED_VIDEO = "COMPOSED_VIDEO_ONLY";
@@ -1540,7 +1492,7 @@ public class OpenViduTestAppE2eTest {
};
Thread t = new Thread(() -> {
- MyUser user2 = new MyUser(new FirefoxUser("FirefoxUser", 30));
+ MyUser user2 = new MyUser(new FirefoxUser("FirefoxUser", 30, false));
otherUsers.add(user2);
user2.getDriver().get(APP_URL);
WebElement urlInput = user2.getDriver().findElement(By.id("openvidu-url"));
@@ -1713,24 +1665,24 @@ public class OpenViduTestAppE2eTest {
// Check video-only COMPOSED recording
String recPath = recordingsPath + SESSION_NAME + "/";
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME);
- this.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true, recording.getDuration(),
- recording.getResolution(), null, "h264", true);
+ this.recordingUtils.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true,
+ recording.getDuration(), recording.getResolution(), null, "h264", true);
// Check audio-only COMPOSED recording
recPath = recordingsPath + SESSION_NAME + "-1/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-1");
- this.checkMultimediaFile(new File(recPath + recording.getName() + ".webm"), true, false,
+ this.recordingUtils.checkMultimediaFile(new File(recPath + recording.getName() + ".webm"), true, false,
recording.getDuration(), null, "opus", null, true);
// Check video-only INDIVIDUAL recording
recPath = recordingsPath + SESSION_NAME + "-2/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-2");
- this.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true);
+ this.recordingUtils.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true);
// Check audio-only INDIVIDUAL recording
recPath = recordingsPath + SESSION_NAME + "-3/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-3");
- this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
+ this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@@ -1746,6 +1698,129 @@ public class OpenViduTestAppE2eTest {
}
}
+ @Test
+ @DisplayName("Custom layout recording")
+ void customLayoutRecordTest() throws Exception {
+ isRecordingTest = true;
+
+ setupBrowser("chrome");
+
+ log.info("Custom layout recording");
+
+ final String SESSION_NAME = "CUSTOM_LAYOUT_SESSION";
+
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-0")).clear();
+ user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(SESSION_NAME);
+
+ // Custom layout from local storage
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("recording-mode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-ALWAYS")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("recording-layout-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-CUSTOM")).click();
+ Thread.sleep(500);
+ WebElement tokeInput = user.getDriver().findElement(By.id("default-custom-layout-input"));
+ tokeInput.clear();
+ tokeInput.sendKeys("layout1");
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.className("join-btn")).sendKeys(Keys.ENTER);
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 1);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 1);
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 1);
+
+ Thread.sleep(4000);
+
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.id("recording-id-field")).clear();
+ user.getDriver().findElement(By.id("recording-id-field")).sendKeys(SESSION_NAME);
+ user.getDriver().findElement(By.id("stop-recording-btn")).click();
+ user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
+ "Recording stopped [" + SESSION_NAME + "]"));
+ user.getEventManager().waitUntilEventReaches("recordingStopped", 1);
+ user.getDriver().findElement(By.id("close-session-btn")).click();
+ user.getEventManager().waitUntilEventReaches("streamDestroyed", 1);
+ user.getEventManager().waitUntilEventReaches("sessionDisconnected", 1);
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+
+ String recordingsPath = "/opt/openvidu/recordings/" + SESSION_NAME + "/";
+ File file1 = new File(recordingsPath + SESSION_NAME + ".mp4");
+ File file2 = new File(recordingsPath + SESSION_NAME + ".jpg");
+
+ Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordingUtils
+ .recordedRedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME)));
+ Assert.assertTrue("Thumbnail " + file2.getAbsolutePath() + " is not fine",
+ this.recordingUtils.thumbnailIsFine(file2, RecordingUtils::checkVideoAverageRgbRed));
+
+ // Custom layout from external URL
+ CountDownLatch initLatch = new CountDownLatch(1);
+ CustomLayoutHandler.main(new String[0], initLatch);
+ try {
+
+ if (!initLatch.await(30, TimeUnit.SECONDS)) {
+ Assert.fail("Timeout waiting for webhook springboot app to start");
+ CustomLayoutHandler.shutDown();
+ return;
+ }
+
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ tokeInput = user.getDriver().findElement(By.id("default-custom-layout-input"));
+ tokeInput.clear();
+ tokeInput.sendKeys(EXTERNAL_CUSTOM_LAYOUT_URL + "?" + EXTERNAL_CUSTOM_LAYOUT_PARAMS);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.className("join-btn")).sendKeys(Keys.ENTER);
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 2);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 2);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
+
+ Thread.sleep(4000);
+
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.id("recording-id-field")).clear();
+ user.getDriver().findElement(By.id("recording-id-field")).sendKeys(SESSION_NAME + "-1");
+ user.getDriver().findElement(By.id("stop-recording-btn")).click();
+ user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
+ "Recording stopped [" + SESSION_NAME + "-1]"));
+ user.getEventManager().waitUntilEventReaches("recordingStopped", 2);
+ user.getDriver().findElement(By.id("close-session-btn")).click();
+ user.getEventManager().waitUntilEventReaches("streamDestroyed", 2);
+ user.getEventManager().waitUntilEventReaches("sessionDisconnected", 2);
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+
+ recordingsPath = "/opt/openvidu/recordings/" + SESSION_NAME + "-1/";
+ file1 = new File(recordingsPath + SESSION_NAME + "-1.mp4");
+ file2 = new File(recordingsPath + SESSION_NAME + "-1.jpg");
+
+ Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine",
+ this.recordingUtils.recordedRedFileFine(file1,
+ new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-1")));
+ Assert.assertTrue("Thumbnail " + file2.getAbsolutePath() + " is not fine",
+ this.recordingUtils.thumbnailIsFine(file2, RecordingUtils::checkVideoAverageRgbRed));
+
+ } finally {
+ CustomLayoutHandler.shutDown();
+ }
+ }
+
@Test
@DisplayName("REST API: Fetch all, fetch one, force disconnect, force unpublish, close session")
void restApiFetchForce() throws Exception {
@@ -1816,15 +1891,15 @@ public class OpenViduTestAppE2eTest {
"Number: 1. Changes: false"));
// Force unpublish wrong
- user.getDriver().findElement(By.id("resource-id-field")).clear();
- user.getDriver().findElement(By.id("resource-id-field")).sendKeys("FAIL");
+ user.getDriver().findElement(By.id("stream-id-field")).clear();
+ user.getDriver().findElement(By.id("stream-id-field")).sendKeys("FAIL");
user.getDriver().findElement(By.id("force-unpublish-api-btn")).click();
user.getWaiter()
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Error [404]"));
// Force unpublish right
- user.getDriver().findElement(By.id("resource-id-field")).clear();
- user.getDriver().findElement(By.id("resource-id-field")).sendKeys(streamId);
+ user.getDriver().findElement(By.id("stream-id-field")).clear();
+ user.getDriver().findElement(By.id("stream-id-field")).sendKeys(streamId);
user.getDriver().findElement(By.id("force-unpublish-api-btn")).click();
user.getWaiter().until(
ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Stream unpublished"));
@@ -1834,15 +1909,15 @@ public class OpenViduTestAppE2eTest {
Assert.assertEquals("Expected 3 videos but found " + numberOfVideos, 3, numberOfVideos);
// Force disconnect wrong
- user.getDriver().findElement(By.id("resource-id-field")).clear();
- user.getDriver().findElement(By.id("resource-id-field")).sendKeys("FAIL");
+ user.getDriver().findElement(By.id("connection-id-field")).clear();
+ user.getDriver().findElement(By.id("connection-id-field")).sendKeys("FAIL");
user.getDriver().findElement(By.id("force-disconnect-api-btn")).click();
user.getWaiter()
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Error [404]"));
// Force disconnect right
- user.getDriver().findElement(By.id("resource-id-field")).clear();
- user.getDriver().findElement(By.id("resource-id-field")).sendKeys(connectionId);
+ user.getDriver().findElement(By.id("connection-id-field")).clear();
+ user.getDriver().findElement(By.id("connection-id-field")).sendKeys(connectionId);
user.getDriver().findElement(By.id("force-disconnect-api-btn")).click();
user.getWaiter()
.until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "User disconnected"));
@@ -1904,7 +1979,7 @@ public class OpenViduTestAppE2eTest {
// Analyze Chrome fake video stream without gray filter (GREEN color)
Map rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
- Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
+ Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
// Try to apply none allowed filter
user.getDriver().findElement(By.cssSelector(".filter-btn")).click();
@@ -1943,7 +2018,7 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(500);
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
- Assert.assertTrue("Video is not average gray", checkVideoAverageRgbGray(rgb));
+ Assert.assertTrue("Video is not average gray", RecordingUtils.checkVideoAverageRgbGray(rgb));
// Execute filter method
WebElement filterMethodInput = user.getDriver().findElement(By.id("filter-method-field"));
@@ -1961,7 +2036,7 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(500);
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
- Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
+ Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@@ -1986,7 +2061,7 @@ public class OpenViduTestAppE2eTest {
subscriberVideo = user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 video"));
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
- Assert.assertTrue("Video is not average gray", checkVideoAverageRgbGray(rgb));
+ Assert.assertTrue("Video is not average gray", RecordingUtils.checkVideoAverageRgbGray(rgb));
// Remove filter
user.getDriver().findElement(By.cssSelector(".filter-btn")).click();
@@ -2000,7 +2075,7 @@ public class OpenViduTestAppE2eTest {
// Analyze Chrome fake video stream with gray filter (GREEN color)
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
- Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
+ Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@@ -2146,8 +2221,6 @@ public class OpenViduTestAppE2eTest {
log.info("openvidu-java-client test");
- System.out.println(getBase64Screenshot(user));
-
user.getDriver().findElement(By.id("one2one-btn")).click();
final String customSessionId = "openviduJavaClientSession";
@@ -2172,15 +2245,29 @@ public class OpenViduTestAppE2eTest {
KurentoOptions kurentoOptions = new KurentoOptions.Builder().videoMaxRecvBandwidth(250)
.allowedFilters(new String[] { "GStreamerFilter" }).build();
- TokenOptions tokenOptionsModerator = new TokenOptions.Builder().role(OpenViduRole.MODERATOR)
- .data(serverDataModerator).kurentoOptions(kurentoOptions).build();
- String tokenModerator = session.generateToken(tokenOptionsModerator);
+ ConnectionProperties moderatorConnectionProperties = new ConnectionProperties.Builder()
+ .role(OpenViduRole.MODERATOR).data(serverDataModerator).kurentoOptions(kurentoOptions).build();
+ Connection connectionModerator = session.createConnection(moderatorConnectionProperties);
- TokenOptions tokenOptionsSubscriber = new TokenOptions.Builder().role(OpenViduRole.SUBSCRIBER)
- .data(serverDataSubscriber).build();
- String tokenSubscriber = session.generateToken(tokenOptionsSubscriber);
+ ConnectionProperties subscriberConnectionProperties = new ConnectionProperties.Builder()
+ .type(ConnectionType.WEBRTC).role(OpenViduRole.SUBSCRIBER).data(serverDataSubscriber).build();
+ Connection connectionSubscriber = session.createConnection(subscriberConnectionProperties);
- Assert.assertFalse("Session.fetch() should return false until a user has connected", session.fetch());
+ Assert.assertFalse("Session.fetch() should return false after Session.createConnection", session.fetch());
+ Assert.assertFalse("OpenVidu.fetch() should return false after Session.fetch()", OV.fetch());
+
+ Assert.assertEquals("Wrong number of active connections", 0, session.getActiveConnections().size());
+ Assert.assertEquals("Wrong number of connections", 2, session.getConnections().size());
+ Assert.assertEquals("Wrong status property", "pending", connectionModerator.getStatus());
+ Assert.assertEquals("Wrong role property", OpenViduRole.MODERATOR, connectionModerator.getRole());
+ Assert.assertTrue("Wrong record property", connectionModerator.record());
+ Assert.assertNull("Wrong location property", connectionModerator.getLocation());
+ Assert.assertNull("Wrong platform property", connectionModerator.getPlatform());
+ Assert.assertTrue("Wrong createdAt property", connectionModerator.createdAt() > 0);
+ Assert.assertNull("Wrong activeAt property", connectionModerator.activeAt());
+ Assert.assertNull("Wrong clientData property", connectionModerator.getClientData());
+ Assert.assertEquals("Wrong publishers property", 0, connectionModerator.getPublishers().size());
+ Assert.assertEquals("Wrong subscribers property", 0, connectionModerator.getSubscribers().size());
// Set client data 1
WebElement clientDataInput = user.getDriver().findElement(By.cssSelector("#client-data-input-0"));
@@ -2192,7 +2279,7 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(1000);
WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
tokeInput.clear();
- tokeInput.sendKeys(tokenModerator);
+ tokeInput.sendKeys(connectionModerator.getToken());
user.getDriver().findElement(By.id("save-btn")).click();
Thread.sleep(1000);
@@ -2207,7 +2294,7 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(1000);
tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
tokeInput.clear();
- tokeInput.sendKeys(tokenSubscriber);
+ tokeInput.sendKeys(connectionSubscriber.getToken());
user.getDriver().findElement(By.id("save-btn")).click();
Thread.sleep(1000);
@@ -2247,17 +2334,14 @@ public class OpenViduTestAppE2eTest {
Assert.assertEquals("Expected 2 active connections but found " + session.getActiveConnections().size(), 2,
session.getActiveConnections().size());
- Connection connectionModerator;
- Connection connectionSubscriber;
- if (OpenViduRole.MODERATOR.equals(session.getActiveConnections().get(0).getRole())) {
- connectionModerator = session.getActiveConnections().get(0);
- connectionSubscriber = session.getActiveConnections().get(1);
- } else {
- connectionModerator = session.getActiveConnections().get(1);
- connectionSubscriber = session.getActiveConnections().get(0);
- }
+ // Verify status
+ Assert.assertEquals("Wrong status for moderator connection", "active", connectionModerator.getStatus());
+ Assert.assertEquals("Wrong status for subscriber connection", "active", connectionSubscriber.getStatus());
- Assert.assertEquals(OpenViduRole.SUBSCRIBER, connectionSubscriber.getRole());
+ // Verify createdAt and activeAt
+ Assert.assertTrue("Wrong createdAt property", connectionModerator.createdAt() > 0);
+ Assert.assertTrue("Wrong activeAt property", connectionModerator.activeAt() > 0);
+ Assert.assertTrue("Wrong activeAt property", connectionModerator.activeAt() > connectionModerator.createdAt());
// Verify platform
Assert.assertTrue("Wrong platform for moderator connection",
@@ -2293,7 +2377,7 @@ public class OpenViduTestAppE2eTest {
// Verify publisher properties
Publisher pub = connectionModerator.getPublishers().get(0);
Assert.assertEquals("{\"width\":640,\"height\":480}", pub.getVideoDimensions());
- Assert.assertEquals(new Integer(30), pub.getFrameRate());
+ Assert.assertEquals(Integer.valueOf(30), pub.getFrameRate());
Assert.assertEquals("CAMERA", pub.getTypeOfVideo());
Assert.assertTrue(pub.hasVideo());
Assert.assertTrue(pub.isVideoActive());
@@ -2337,9 +2421,9 @@ public class OpenViduTestAppE2eTest {
String widthAndHeight = user.getEventManager().getDimensionOfViewport();
JsonObject obj = JsonParser.parseString(widthAndHeight).getAsJsonObject();
Assert.assertEquals(
- "{\"width\":" + obj.get("width").getAsLong() + ",\"height\":" + (obj.get("height").getAsLong()) + "}",
+ "{\"width\":" + obj.get("width").getAsLong() + ",\"height\":" + obj.get("height").getAsLong() + "}",
pub.getVideoDimensions());
- Assert.assertEquals(new Integer(30), pub.getFrameRate());
+ Assert.assertEquals(Integer.valueOf(30), pub.getFrameRate());
Assert.assertEquals("SCREEN", pub.getTypeOfVideo());
Assert.assertTrue(pub.hasVideo());
Assert.assertTrue(pub.isVideoActive());
@@ -2350,6 +2434,7 @@ public class OpenViduTestAppE2eTest {
RecordingProperties recordingProperties;
try {
OV.startRecording("NOT_EXISTS");
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e) {
Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 404, e.getStatus());
}
@@ -2357,6 +2442,7 @@ public class OpenViduTestAppE2eTest {
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
try {
OV.startRecording(sessionAux.getSessionId());
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e) {
Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 406, e.getStatus());
} finally {
@@ -2364,25 +2450,49 @@ public class OpenViduTestAppE2eTest {
sessionAux.close();
try {
sessionAux.fetch();
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e2) {
Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e2.getStatus());
}
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
}
+ // Not recorded session
+ Session notRecordedSession = OV.createSession();
+ notRecordedSession.createConnection(new ConnectionProperties.Builder().type(ConnectionType.IPCAM)
+ .rtspUri("rtsp://does-not-matter.com").build());
try {
recordingProperties = new RecordingProperties.Builder().hasAudio(false).hasVideo(false).build();
- OV.startRecording(session.getSessionId(), recordingProperties);
+ OV.startRecording(notRecordedSession.getSessionId(), recordingProperties);
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e) {
Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 422, e.getStatus());
}
try {
recordingProperties = new RecordingProperties.Builder().resolution("99x1080").build();
- OV.startRecording(session.getSessionId(), recordingProperties);
+ OV.startRecording(notRecordedSession.getSessionId(), recordingProperties);
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e) {
Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 422, e.getStatus());
}
+ notRecordedSession.close();
+ // Recorded session
+ try {
+ recordingProperties = new RecordingProperties.Builder().hasAudio(false).hasVideo(false).build();
+ OV.startRecording(session.getSessionId(), recordingProperties);
+ Assert.fail("Expected OpenViduHttpException");
+ } catch (OpenViduHttpException e) {
+ Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 409, e.getStatus());
+ }
+ try {
+ recordingProperties = new RecordingProperties.Builder().resolution("99x1080").build();
+ OV.startRecording(session.getSessionId(), recordingProperties);
+ Assert.fail("Expected OpenViduHttpException");
+ } catch (OpenViduHttpException e) {
+ Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 409, e.getStatus());
+ }
try {
OV.startRecording(session.getSessionId());
+ Assert.fail("Expected OpenViduHttpException");
} catch (OpenViduHttpException e) {
Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 409, e.getStatus());
}
@@ -2427,8 +2537,8 @@ public class OpenViduTestAppE2eTest {
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
- this.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2, "opus", "vp8",
- false);
+ this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2,
+ "opus", "vp8", false);
user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .change-publisher-btn")).click();
user.getEventManager().waitUntilEventReaches("streamDestroyed", 4);
@@ -2443,9 +2553,10 @@ public class OpenViduTestAppE2eTest {
.recordingLayout(RecordingLayout.BEST_FIT).resolution("1280x720").hasVideo(true).hasAudio(false)
.name(customRecordingName).build();
+ // Start recording method should block until video exists and size > 0
Recording recording2 = OV.startRecording(session.getSessionId(), recordingProperties);
recording2 = OV.stopRecording(recording2.getId());
- Assert.assertEquals("Wrong recording status", Recording.Status.failed, recording2.getStatus());
+ Assert.assertEquals("Wrong recording status", Recording.Status.ready, recording2.getStatus());
OV.deleteRecording(recording2.getId());
recording2 = OV.startRecording(session.getSessionId(), recordingProperties);
@@ -2491,8 +2602,9 @@ public class OpenViduTestAppE2eTest {
file3.exists() && file3.length() > 0);
Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine",
- this.recordedFileFine(file1, recording2));
- Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", this.thumbnailIsFine(file3));
+ this.recordingUtils.recordedGreenFileFine(file1, recording2));
+ Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine",
+ this.recordingUtils.thumbnailIsFine(file3, RecordingUtils::checkVideoAverageRgbGreen));
try {
OV.deleteRecording("NOT_EXISTS");
@@ -2507,12 +2619,12 @@ public class OpenViduTestAppE2eTest {
try {
session.forceUnpublish("NOT_EXISTS");
} catch (OpenViduHttpException e) {
- Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus());
+ Assert.assertEquals("Wrong HTTP status on Session.forceUnpublish()", 404, e.getStatus());
}
try {
session.forceDisconnect("NOT_EXISTS");
} catch (OpenViduHttpException e) {
- Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus());
+ Assert.assertEquals("Wrong HTTP status on Session.forceDisconnect()", 404, e.getStatus());
}
if (OpenViduRole.MODERATOR.equals(session.getActiveConnections().get(0).getRole())) {
@@ -2524,19 +2636,26 @@ public class OpenViduTestAppE2eTest {
}
pub = connectionModerator.getPublishers().get(0);
+ // TODO: test delete unused Connection
+
session.forceUnpublish(pub);
user.getEventManager().waitUntilEventReaches("streamDestroyed", 6);
- Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
+ Assert.assertFalse(
+ "OpenVidu.fetch() should return false because Session.forceUnpublish() already updates local objects",
+ OV.fetch());
session.getActiveConnections().forEach(con -> {
Assert.assertEquals("Wrong number of Publishers", 0, con.getPublishers().size());
Assert.assertEquals("Wrong number of Subscribers", 0, con.getSubscribers().size());
});
+ // Delete active Connection
session.forceDisconnect(connectionModerator);
user.getEventManager().waitUntilEventReaches("sessionDisconnected", 1);
user.getEventManager().waitUntilEventReaches("connectionDestroyed", 1);
- Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
+ Assert.assertFalse(
+ "OpenVidu.fetch() should return false because Session.forceDisconnect() already updates local objects",
+ OV.fetch());
session.close();
@@ -2544,7 +2663,71 @@ public class OpenViduTestAppE2eTest {
Assert.assertFalse("Session.fetch() should return true", OV.fetch());
+ // Delete pending Connection
+ session = OV.createSession();
+ Connection con = session.createConnection();
+ Assert.assertEquals("Wrong number of Connections", 1, session.getConnections().size());
+ Assert.assertEquals("Wrong number of active Connections", 0, session.getActiveConnections().size());
+ Assert.assertFalse(session.fetch());
+ session.forceDisconnect(con);
+ Assert.assertEquals("Wrong number of Connections", 0, session.getConnections().size());
+ Assert.assertEquals("Wrong number of active Connections", 0, session.getActiveConnections().size());
+ Assert.assertFalse(session.fetch());
+
+ // Test IPCAM
+ final String rtsp = "rtsp://dummyurl.com";
+ Connection ipcamera = session.createConnection(new ConnectionProperties.Builder().type(ConnectionType.IPCAM)
+ .rtspUri(rtsp).adaptativeBitrate(false).onlyPlayWithSubscribers(false).networkCache(50).build());
+ Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
+ Assert.assertFalse("Session.fetch() should return false", session.fetch());
+ Assert.assertEquals("Wrong number of active connections", 1, session.getActiveConnections().size());
+ Assert.assertEquals("Wrong number of connections", 1, session.getConnections().size());
+ ipcamera = session.getConnection(ipcamera.getConnectionId());
+ Assert.assertEquals("Wrong type property of Connection object", "IPCAM", ipcamera.getType().name());
+ Assert.assertNull("Property role of an IPCAM connection should be null", ipcamera.getRole());
+ Assert.assertEquals("Wrong property rtspUri", rtsp, ipcamera.getRtspUri());
+ Assert.assertFalse("Wrong property adaptativeBitrate", ipcamera.adaptativeBitrate());
+ Assert.assertFalse("Wrong property onlyPlayWithSubscribers", ipcamera.onlyPlayWithSubscribers());
+ Assert.assertEquals("Wrong property networkCache", Integer.valueOf(50), ipcamera.getNetworkCache());
+
gracefullyLeaveParticipants(2);
+ session.close();
+
+ /** Test transcoding defined properties */
+ SessionProperties.Builder basePropertiesBuilder = new SessionProperties.Builder().mediaMode(MediaMode.ROUTED)
+ .recordingMode(RecordingMode.ALWAYS).defaultOutputMode(OutputMode.INDIVIDUAL);
+
+ SessionProperties propertiesDefaultCodec = basePropertiesBuilder.build();
+ SessionProperties propertiesH264AllowTranscoding = basePropertiesBuilder.forcedVideoCodec(VideoCodec.H264)
+ .allowTranscoding(true).build();
+ SessionProperties propertiesVP9AllowTranscoding = basePropertiesBuilder.forcedVideoCodec(VideoCodec.VP9)
+ .allowTranscoding(true).build();
+
+ Session sessionDefaultCodec = OV.createSession(propertiesDefaultCodec);
+ Session sessionH264AllowTranscoding = OV.createSession(propertiesH264AllowTranscoding);
+ Session sessionVP9AllowTranscoding = OV.createSession(propertiesVP9AllowTranscoding);
+ assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
+ sessionVP9AllowTranscoding);
+
+ // Fetch sessions
+ Assert.assertFalse(sessionDefaultCodec.fetch());
+ Assert.assertFalse(sessionH264AllowTranscoding.fetch());
+ Assert.assertFalse(sessionVP9AllowTranscoding.fetch());
+
+ // Check transcoding session properties
+ assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
+ sessionVP9AllowTranscoding);
+
+ // Fetch all sessions
+ Assert.assertFalse(OV.fetch());
+
+ // Check transcoding session properties
+ assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
+ sessionVP9AllowTranscoding);
+
+ sessionDefaultCodec.close();
+ sessionH264AllowTranscoding.close();
+ sessionVP9AllowTranscoding.close();
}
@Test
@@ -2560,127 +2743,177 @@ public class OpenViduTestAppE2eTest {
String wrongCredentials = "Basic "
+ Base64.getEncoder().encodeToString(("OPENVIDUAPP:WRONG_SECRET").getBytes());
Assert.assertEquals("Expected unauthorized status", HttpStatus.SC_UNAUTHORIZED,
- restClient.getAndReturnStatus("/config", wrongCredentials));
+ restClient.getAndReturnStatus("/openvidu/api/config", wrongCredentials));
- /** GET /api/sessions (before session created) **/
- restClient.rest(HttpMethod.GET, "/api/sessions/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
- restClient.rest(HttpMethod.GET, "/api/sessions", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("numberOfElements", new Integer(0), "content", new JsonArray()));
+ /** GET /openvidu/api/sessions (before session created) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", null, HttpStatus.SC_OK, true, true, true,
+ "{'numberOfElements': 0, 'content': []}");
- /** POST /api/sessions **/
+ /** POST /openvidu/api/sessions **/
// 400
String body = "{'mediaMode': 'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
body = "{'mediaMode': 'ROUTED', 'recordingMode': false}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'customSessionId': 999}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
body = "{'mediaMode': 'ROUTED', 'recordingMode': 'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'defaultOutputMode': 'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'defaultOutputMode': 'INDIVIDUAL', 'defaultRecordingLayout': 'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
// 200
body = "{'mediaMode': 'ROUTED', 'recordingMode': 'MANUAL', 'customSessionId': 'CUSTOM_SESSION_ID', 'defaultOutputMode': 'COMPOSED', 'defaultRecordingLayout': 'BEST_FIT'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_OK, true,
- "{'id':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':0,'content':[]},'recording':true}");
-
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK, true, false, true,
+ DEFAULT_JSON_SESSION);
// Default values
- JsonObject res = restClient.rest(HttpMethod.POST, "/api/sessions", "{}", HttpStatus.SC_OK, true,
- "{'id':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':0,'content':[]},'recording':true}");
- restClient.rest(HttpMethod.DELETE, "/api/sessions/" + res.get("id").getAsString(), HttpStatus.SC_NO_CONTENT);
+ JsonObject res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", "{}", HttpStatus.SC_OK, true, false,
+ true, DEFAULT_JSON_SESSION);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/" + res.get("id").getAsString(),
+ HttpStatus.SC_NO_CONTENT);
// 409
body = "{'customSessionId': 'CUSTOM_SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_CONFLICT);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_CONFLICT);
- /** GET /api/sessions (after session created) **/
- restClient.rest(HttpMethod.GET, "/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
- "{'sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':0,'content':[]},'recording':true}");
- restClient.rest(HttpMethod.GET, "/api/sessions", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("numberOfElements", new Integer(1), "content", new JsonArray()));
+ /** GET /openvidu/api/sessions (after session created) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true, false,
+ true, DEFAULT_JSON_SESSION);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", null, HttpStatus.SC_OK, true, true, false,
+ "{'numberOfElements': 1, 'content': []}");
- /** POST /api/tokens **/
+ /** GET /openvidu/api/sessions/ID/connection (with no connections) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/NOT_EXISTS/connection", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null, HttpStatus.SC_OK,
+ true, true, true, "{'numberOfElements':0,'content':[]}");
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/NOT_EXISTS/connection/NOT_EXISTS",
+ HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/NOT_EXISTS",
+ HttpStatus.SC_NOT_FOUND);
+
+ /** POST /openvidu/api/tokens **/
// 400
body = "{}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': true}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 999}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': false}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'allowedFilters': 'NOT_EXISTS'}}";
- restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
// 200
- body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'allowedFilters': ['GStreamerFilter']}}";
- res = restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_OK, true,
- "{'id':'STR','session':'STR','role':'STR','data':'STR','token':'STR','kurentoOptions':{'allowedFilters':['STR']}}");
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'videoMaxSendBandwidth':777,'allowedFilters': ['GStreamerFilter']}}";
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_OK, true, false, true,
+ mergeJson(DEFAULT_JSON_TOKEN,
+ "{'kurentoOptions':{'videoMaxSendBandwidth':777,'allowedFilters':['STR']}}", new String[0]));
final String token1 = res.get("token").getAsString();
- Assert.assertEquals("JSON return value from /api/tokens should have equal srtings in 'id' and 'token'",
+ Assert.assertEquals("JSON return value from /openvidu/api/tokens should have equal srtings in 'id' and 'token'",
res.get("id").getAsString(), token1);
Assert.assertEquals("Wrong session parameter", "CUSTOM_SESSION_ID", res.get("session").getAsString());
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null,
+ HttpStatus.SC_OK, true, true, false, "{'numberOfElements':1,'content':[]}");
+ JsonObject connection1 = res.getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject();
+ final String connectionId1 = connection1.get("id").getAsString();
+ final long createdAt1 = connection1.get("createdAt").getAsLong();
+ /** POST /openvidu/api/sessions/CUSTOM_SESSION_ID/connection **/
+ // 400
+ body = "{'type':false}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_BAD_REQUEST);
+ body = "{'type':'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_BAD_REQUEST);
+ body = "{'type':'WEBRTC','role':123}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_BAD_REQUEST);
+ body = "{'type':'WEBRTC','role':123}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_BAD_REQUEST);
+ body = "{'type':'WEBRTC','role':'MODERATOR','data':true}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_BAD_REQUEST);
+ // 200
+ String kurentoOpts = "'kurentoOptions':{'videoMaxSendBandwidth':777,'allowedFilters':['GStreamerFilter']}";
+ body = "{'type':'WEBRTC','role':'MODERATOR','data':'SERVER_DATA'," + kurentoOpts + "}";
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_OK, true, false, true,
+ mergeJson(DEFAULT_JSON_PENDING_CONNECTION, "{" + kurentoOpts + "}", new String[0]));
+ restClient.rest(HttpMethod.DELETE,
+ "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + res.get("id").getAsString(),
+ HttpStatus.SC_NO_CONTENT);
// Default values
- body = "{'session': 'CUSTOM_SESSION_ID'}";
- res = restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_OK, true,
- "{'id':'STR','session':'STR','role':'STR','data':'STR','token':'STR'}");
- final String token2 = res.get("id").getAsString();
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", "{}",
+ HttpStatus.SC_OK);
+ final String token2 = res.get("token").getAsString();
+ final String connectionId2 = res.get("id").getAsString();
- /** POST /api/signal (NOT ACTIVE SESSION) **/
+ /** GET /openvidu/api/sessions (with pending connections) **/
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
+ false, true, DEFAULT_JSON_SESSION);
+ Assert.assertEquals("GET session should not bring pending connections", 0,
+ res.get("connections").getAsJsonObject().get("numberOfElements").getAsInt());
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID?pendingConnections=true", null,
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_SESSION);
+ Assert.assertEquals("GET session should bring pending connections if query params pendingConnections=true", 2,
+ res.get("connections").getAsJsonObject().get("numberOfElements").getAsInt());
+
+ /** GET /openvidu/api/sessions/ID/connection (with pending connections) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null, HttpStatus.SC_OK,
+ true, true, false, "{'numberOfElements':2,'content':[]}");
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId1, null,
+ HttpStatus.SC_OK, true, true, true,
+ mergeJson(DEFAULT_JSON_PENDING_CONNECTION,
+ "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1
+ + "','sessionId':'CUSTOM_SESSION_ID','token':'" + token1
+ + "','serverData':'SERVER_DATA','role':'MODERATOR'," + kurentoOpts + ",'createdAt':"
+ + createdAt1 + "}",
+ new String[0]));
+
+ /** POST /openvidu/api/signal (NOT ACTIVE SESSION) **/
body = "{}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session': true}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session':'CUSTOM_SESSION_ID','to':12}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session':'CUSTOM_SESSION_ID','to':[],'data':false}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session':'CUSTOM_SESSION_ID','to':[],'data': 'SERVERMESSAGE', 'type': true}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_BAD_REQUEST);
body = "{'session':'CUSTOM_SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No connections
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No connections
- /** POST /api/recordings/start (NOT ACTIVE SESSION) **/
+ /** POST /openvidu/api/recordings/start (NOT ACTIVE SESSION) **/
// 400
body = "{}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
- body = "{'session': true}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
- body = "{'session':'SESSION_ID','name':999}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
- body = "{'session':'SESSION_ID','name':'NAME','outputMode':'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
- body = "{'session':'SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'NOT_EXISTS'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
- body = "{'session':'SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':true,'hasVideo':true,'resolution':999}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
-
- // 422
- body = "{'session':'SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':false,'hasVideo':false}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY);
- body = "{'session':'SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':true,'hasVideo':true,'resolution':'1920x2000'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
// 404
body = "{'session':'SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_NOT_FOUND);
+ body = "{'session':'SESSION_ID','name':999}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_NOT_FOUND);
// 406
body = "{'session':'CUSTOM_SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_NOT_ACCEPTABLE);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_NOT_ACCEPTABLE);
// 409 (RELAYED media mode)
- res = restClient.rest(HttpMethod.POST, "/api/sessions", "{'mediaMode':'RELAYED'}", HttpStatus.SC_OK, true,
- "{'id':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':0,'content':[]},'recording':true}");
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", "{'mediaMode':'RELAYED'}", HttpStatus.SC_OK,
+ true, false, false, DEFAULT_JSON_SESSION);
body = "{'session':'" + res.get("id").getAsString() + "'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_CONFLICT);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/" + res.get("id").getAsString(), HttpStatus.SC_NO_CONTENT);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_CONFLICT);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/" + res.get("id").getAsString(),
+ HttpStatus.SC_NO_CONTENT);
// Start session
setupBrowser("chrome");
@@ -2689,9 +2922,9 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(1000);
// Set token 1
- WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
- tokeInput.clear();
- tokeInput.sendKeys(token1);
+ WebElement tokenInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokenInput.clear();
+ tokenInput.sendKeys(token1);
user.getDriver().findElement(By.id("save-btn")).click();
Thread.sleep(1000);
@@ -2699,9 +2932,9 @@ public class OpenViduTestAppE2eTest {
Thread.sleep(1000);
// Set token 2
- tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
- tokeInput.clear();
- tokeInput.sendKeys(token2);
+ tokenInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokenInput.clear();
+ tokenInput.sendKeys(token2);
user.getDriver().findElement(By.id("save-btn")).click();
Thread.sleep(1000);
@@ -2717,116 +2950,268 @@ public class OpenViduTestAppE2eTest {
Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
.assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
- /** GET /api/recordings (before recording started) **/
- restClient.rest(HttpMethod.GET, "/api/recordings/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
- restClient.rest(HttpMethod.GET, "/api/recordings", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("count", new Integer(0), "items", new JsonArray()));
+ /** GET /openvidu/api/sessions/ID/connection (with active connections) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null, HttpStatus.SC_OK,
+ true, true, false, "{'numberOfElements':2,'content':[]}");
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId1, null,
+ HttpStatus.SC_OK, false, true, false,
+ "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1
+ + "','object':'connection','type':'WEBRTC','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'"
+ + token1 + "','role':'MODERATOR','serverData':'SERVER_DATA','record':true}");
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId2, null,
+ HttpStatus.SC_OK, false, true, false,
+ "{'id':'" + connectionId2 + "','connectionId':'" + connectionId2
+ + "','object':'connection','type':'WEBRTC','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'"
+ + token2 + "','role':'PUBLISHER','serverData':'','record':true}");
+
+ /** GET /openvidu/api/recordings (before recording started) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings", null, HttpStatus.SC_OK, true, true, true,
+ "{'count':0,'items':[]}");
+
+ /** POST /openvidu/api/recordings/start (ACTIVE SESSION) **/
+ // 400
+ body = "{'session': true}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session':'CUSTOM_SESSION_ID','name':999}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':true,'hasVideo':true,'resolution':999}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST);
+
+ // 422
+ body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':false,'hasVideo':false}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY);
+ body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':true,'hasVideo':true,'resolution':'1920x2000'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY);
- /** POST /api/recordings/start (ACTIVE SESSION) **/
// 200
body = "{'session':'CUSTOM_SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_OK, true,
- "{'id':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':null,'status':'STR'}");
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_OK, true, false, true,
+ "{'id':'STR','object':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':null,'status':'STR'}");
user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
Thread.sleep(2000);
// 409 (already recording)
- restClient.rest(HttpMethod.POST, "/api/recordings/start", body, HttpStatus.SC_CONFLICT);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_CONFLICT);
- /** POST /api/recordings/stop **/
+ /** POST /openvidu/api/recordings/stop **/
// 405
- restClient.rest(HttpMethod.POST, "/api/recordings/stop", body, HttpStatus.SC_METHOD_NOT_ALLOWED);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop", body, HttpStatus.SC_METHOD_NOT_ALLOWED);
// 404
- restClient.rest(HttpMethod.POST, "/api/recordings/stop/NOT_EXISTS", body, HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/NOT_EXISTS", body, HttpStatus.SC_NOT_FOUND);
// 200
- restClient.rest(HttpMethod.DELETE, "/api/recordings/CUSTOM_SESSION_ID", HttpStatus.SC_CONFLICT);
- restClient.rest(HttpMethod.POST, "/api/recordings/stop/CUSTOM_SESSION_ID", body, HttpStatus.SC_OK, true,
- "{'id':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':'STR','status':'STR'}");
- /** GET /api/recordings (after recording created) **/
- restClient.rest(HttpMethod.GET, "/api/recordings/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
- "{'id':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':'STR','status':'STR'}");
- restClient.rest(HttpMethod.GET, "/api/recordings", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("count", new Integer(1), "items", new JsonArray()));
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/recordings/CUSTOM_SESSION_ID", HttpStatus.SC_CONFLICT);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/CUSTOM_SESSION_ID", body, HttpStatus.SC_OK,
+ true, false, true,
+ "{'id':'STR','object':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':'STR','status':'STR'}");
+ /** GET /openvidu/api/recordings (after recording created) **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
+ false, true,
+ "{'id':'STR','object':'STR','sessionId':'STR','name':'STR','outputMode':'STR','recordingLayout':'STR','hasAudio':false,'hasVideo':false,'resolution':'STR','createdAt':0,'size':0,'duration':0,'url':'STR','status':'STR'}");
+ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings", null, HttpStatus.SC_OK, true, true, false,
+ "{'count':1,'items':[]}");
user.getEventManager().waitUntilEventReaches("recordingStopped", 2);
- /** DELETE /api/recordings **/
- restClient.rest(HttpMethod.DELETE, "/api/recordings", HttpStatus.SC_METHOD_NOT_ALLOWED);
- restClient.rest(HttpMethod.DELETE, "/api/recordings/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
- restClient.rest(HttpMethod.DELETE, "/api/recordings/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
+ /** DELETE /openvidu/api/recordings **/
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/recordings", HttpStatus.SC_METHOD_NOT_ALLOWED);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/recordings/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/recordings/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
- // GET /api/recordings should return empty again
- restClient.rest(HttpMethod.GET, "/api/recordings", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("count", new Integer(0), "items", new JsonArray()));
+ // GET /openvidu/api/recordings should return empty again
+ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings", null, HttpStatus.SC_OK, true, true, true,
+ "{'count':0,'items':[]}");
- /** DELETE /api/sessions//stream/ **/
- restClient.rest(HttpMethod.DELETE, "/api/sessions/NOT_EXISTS/stream/NOT_EXISTS", HttpStatus.SC_BAD_REQUEST);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/CUSTOM_SESSION_ID/stream/NOT_EXISTS",
+ /** DELETE /openvidu/api/sessions//stream/ **/
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/NOT_EXISTS/stream/NOT_EXISTS",
+ HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/stream/NOT_EXISTS",
HttpStatus.SC_NOT_FOUND);
- res = restClient.rest(HttpMethod.GET, "/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
- "{'sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':2,'content'"
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
+ false, true,
+ "{'id':'STR','object':'STR','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','connections':{'numberOfElements':2,'content'"
+ ":[{'connectionId':'STR','createdAt':0,'location':'STR','platform':'STR','token':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':["
+ "{'createdAt':0,'streamId':'STR','mediaOptions':{'hasAudio':false,'audioActive':false,'hasVideo':false,'videoActive':false,'typeOfVideo':'STR','frameRate':0,"
+ "'videoDimensions':'STR','filter':{}}}],'subscribers':[{'createdAt':0,'streamId':'STR','publisher':'STR'}]},{'connectionId':'STR','createdAt':0,'location':'STR',"
+ "'platform':'STR','token':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':[{'createdAt':0,'streamId':'STR','mediaOptions':{'hasAudio':false,"
- + "'audioActive':false,'hasVideo':false,'videoActive':false,'typeOfVideo':'STR','frameRate':0,'videoDimensions':'STR','filter':{}}}],'subscribers':[{'createdAt':0,'streamId':'STR','publisher':'STR'}]}]},'recording':false}");
+ + "'audioActive':false,'hasVideo':false,'videoActive':false,'typeOfVideo':'STR','frameRate':0,'videoDimensions':'STR','filter':{}}}],'subscribers':[{'createdAt':0,'streamId':'STR','publisher':'STR'}]}]},'recording':false,'forcedVideoCodec':'STR','allowTranscoding':false}");
String streamId = res.get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0)
.getAsJsonObject().get("publishers").getAsJsonArray().get(0).getAsJsonObject().get("streamId")
.getAsString();
- restClient.rest(HttpMethod.DELETE, "/api/sessions/CUSTOM_SESSION_ID/stream/" + streamId,
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/stream/" + streamId,
HttpStatus.SC_NO_CONTENT);
final String connectionId = res.get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0)
.getAsJsonObject().get("connectionId").getAsString();
- /** POST /api/signal (ACTIVE SESSION) **/
+ /** POST /openvidu/api/signal (ACTIVE SESSION) **/
body = "{'session':'CUSTOM_SESSION_ID','to':['wrongConnectionId']}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No valid connectionId
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No valid
+ // connectionId
body = "{'session':'CUSTOM_SESSION_ID','to':['" + connectionId + "','wrongConnectionId']}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No valid connectionId
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_NOT_ACCEPTABLE); // No valid
+ // connectionId
body = "{'session':'CUSTOM_SESSION_ID'}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("signal", 2);
body = "{'session':'CUSTOM_SESSION_ID','to':[],'type':'server1','data':'SERVER EVENT!'}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("signal:server1", 2);
body = "{'session':'CUSTOM_SESSION_ID','to':['" + connectionId + "'],'type':'server2','data':'SERVER EVENT!'}";
- restClient.rest(HttpMethod.POST, "/api/signal", body, HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal", body, HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("signal:server2", 1);
Assert.assertEquals("", 1, user.getDriver()
.findElements(By.xpath("//*[text()='server - signal:server2 - SERVER EVENT!']")).size());
- /** DELETE /api/sessions//connection/ **/
- restClient.rest(HttpMethod.DELETE, "/api/sessions/NOT_EXISTS/connection/NOT_EXISTS", HttpStatus.SC_BAD_REQUEST);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/CUSTOM_SESSION_ID/connection/NOT_EXISTS",
+ /** DELETE /openvidu/api/sessions//connection/ **/
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/NOT_EXISTS/connection/NOT_EXISTS",
+ HttpStatus.SC_BAD_REQUEST);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/NOT_EXISTS",
HttpStatus.SC_NOT_FOUND);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId,
HttpStatus.SC_NO_CONTENT);
- /** DELETE /api/sessions **/
- restClient.rest(HttpMethod.DELETE, "/api/sessions", HttpStatus.SC_METHOD_NOT_ALLOWED);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
+ /** DELETE /openvidu/api/sessions **/
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions", HttpStatus.SC_METHOD_NOT_ALLOWED);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
- // GET /api/sessions should return empty again
- restClient.rest(HttpMethod.GET, "/api/sessions", null, HttpStatus.SC_OK, true,
- ImmutableMap.of("numberOfElements", new Integer(0), "content", new JsonArray()));
+ // GET /openvidu/api/sessions should return empty again
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", null, HttpStatus.SC_OK, true, true, true,
+ "{'numberOfElements':0,'content':[]}");
- /** GET /config **/
- restClient.rest(HttpMethod.GET, "/config", null, HttpStatus.SC_OK, true,
+ /**
+ * DELETE /openvidu/api/sessions//connection/ (unused
+ * token)
+ **/
+ body = "{'customSessionId': 'CUSTOM_SESSION_ID'}";
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK);
+ body = "{'type': 'WEBRTC', 'role': 'SUBSCRIBER'}";
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_PENDING_CONNECTION);
+ final String connectionIdA = res.get("id").getAsString();
+ final String tokenA = res.get("token").getAsString();
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body,
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_PENDING_CONNECTION);
+ final String connectionIdB = res.get("connectionId").getAsString();
+ final String tokenB = res.get("token").getAsString();
+
+ user.getDriver().findElement(By.id("one2one-btn")).click();
+
+ // Set token 1
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ tokenInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokenInput.clear();
+ tokenInput.sendKeys(tokenA);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ // Set token 2
+ user.getDriver().findElement(By.id("session-settings-btn-1")).click();
+ Thread.sleep(1000);
+ tokenInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokenInput.clear();
+ tokenInput.sendKeys(tokenB);
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ // Invalidate token
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionIdA,
+ HttpStatus.SC_NO_CONTENT);
+
+ // User should pop up invalid token
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .join-btn")).sendKeys(Keys.ENTER);
+ try {
+ user.getWaiter().until(ExpectedConditions.alertIsPresent());
+ Alert alert = user.getDriver().switchTo().alert();
+ Assert.assertTrue("Alert does not contain expected text",
+ alert.getText().contains("Token " + tokenA + "is not valid"));
+ alert.accept();
+ } catch (Exception e) {
+ Assert.fail("Alert exception");
+ }
+
+ Thread.sleep(500);
+ user.getEventManager().resetEventThread();
+
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).sendKeys(Keys.ENTER);
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+
+ // connectionId should be equal to the one brought by the token
+ Assert.assertEquals("Wrong connectionId", connectionIdB,
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_OK)
+ .get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject()
+ .get("connectionId").getAsString());
+
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
+
+ // GET /openvidu/api/sessions should return empty again
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", null, HttpStatus.SC_OK, true, true, true,
+ "{'numberOfElements':0,'content':[]}");
+
+ /** GET /openvidu/api/config **/
+ restClient.rest(HttpMethod.GET, "/openvidu/api/config", null, HttpStatus.SC_OK, true, false, true,
"{'VERSION':'STR','DOMAIN_OR_PUBLIC_IP':'STR','HTTPS_PORT':0,'OPENVIDU_PUBLICURL':'STR','OPENVIDU_CDR':false,'OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH':0,'OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH':0,"
- + "'OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH':0,'OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH':0,'OPENVIDU_SESSIONS_GARBAGE_INTERVAL':0,'OPENVIDU_SESSIONS_GARBAGE_THRESHOLD':0,"
+ + "'OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH':0,'OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH':0,'OPENVIDU_STREAMS_FORCED_VIDEO_CODEC':'STR','OPENVIDU_STREAMS_ALLOW_TRANSCODING':false,'OPENVIDU_SESSIONS_GARBAGE_INTERVAL':0,"
+ "'OPENVIDU_RECORDING':false,'OPENVIDU_RECORDING_VERSION':'STR','OPENVIDU_RECORDING_PATH':'STR','OPENVIDU_RECORDING_PUBLIC_ACCESS':false,'OPENVIDU_RECORDING_NOTIFICATION':'STR',"
- + "'OPENVIDU_RECORDING_CUSTOM_LAYOUT':'STR','OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT':0,'OPENVIDU_WEBHOOK':false,'OPENVIDU_WEBHOOK_ENDPOINT':'STR','OPENVIDU_WEBHOOK_HEADERS':[],"
+ + "'OPENVIDU_SESSIONS_GARBAGE_THRESHOLD':0,'OPENVIDU_RECORDING_CUSTOM_LAYOUT':'STR','OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT':0,'OPENVIDU_WEBHOOK':false,'OPENVIDU_WEBHOOK_ENDPOINT':'STR','OPENVIDU_WEBHOOK_HEADERS':[],"
+ "'OPENVIDU_WEBHOOK_EVENTS':[]}");
+
+ /** POST /openvidu/api/sessions (default transcoding parameters) **/
+
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'MANUAL', 'customSessionId': 'CUSTOM_SESSION_ID', 'defaultOutputMode': 'COMPOSED', 'defaultRecordingLayout': 'BEST_FIT'}";
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK);
+
+ // Check session info
+ Assert.assertEquals(VideoCodec.valueOf(res.get("forcedVideoCodec").getAsString()), defaultForcedVideoCodec);
+ Assert.assertEquals(res.get("allowTranscoding").getAsBoolean(), defaultAllowTranscoding);
+
+ // Check all sessions data
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", HttpStatus.SC_OK);
+ Assert.assertEquals(res.get("numberOfElements").getAsInt(), 1);
+ Assert.assertEquals(VideoCodec.valueOf(
+ res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()),
+ defaultForcedVideoCodec);
+ Assert.assertEquals(
+ res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(),
+ defaultAllowTranscoding);
+
+ // Remove session
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
+
+ /** POST /openvidu/api/sessions (define forceCodec and allowTranscoding) **/
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'MANUAL', 'customSessionId': 'CUSTOM_SESSION_ID', 'defaultOutputMode': 'COMPOSED', 'defaultRecordingLayout': 'BEST_FIT', 'forcedVideoCodec': 'H264', 'allowTranscoding': true}";
+ res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK);
+
+ Assert.assertEquals(VideoCodec.valueOf(res.get("forcedVideoCodec").getAsString()), VideoCodec.H264);
+ Assert.assertEquals(res.get("allowTranscoding").getAsBoolean(), true);
+
+ // Check all sessions data
+ res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", HttpStatus.SC_OK);
+ Assert.assertEquals(res.get("numberOfElements").getAsInt(), 1);
+ Assert.assertEquals(VideoCodec.valueOf(
+ res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()),
+ VideoCodec.H264);
+ Assert.assertEquals(
+ res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(),
+ true);
+
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
}
@Test
@DisplayName("Kurento reconnect test")
+ @Disabled
void kurentoReconnectTest() throws Exception {
isRecordingTest = true;
isKurentoRestartTest = true;
@@ -2950,7 +3335,8 @@ public class OpenViduTestAppE2eTest {
Assert.assertTrue("Recording duration exceeds valid value. Expected no more than 0.2 seconds, got "
+ differenceInDuration, differenceInDuration < 0.2);
- this.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8", true);
+ this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8",
+ true);
WebElement pubBtn = user.getDriver().findElements(By.cssSelector("#openvidu-instance-1 .pub-btn")).get(0);
pubBtn.click();
@@ -2982,7 +3368,7 @@ public class OpenViduTestAppE2eTest {
log.info("Webhook test");
CountDownLatch initLatch = new CountDownLatch(1);
- io.openvidu.test.browsers.utils.CustomWebhook.main(new String[0], initLatch);
+ io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch);
try {
@@ -3024,6 +3410,7 @@ public class OpenViduTestAppE2eTest {
event = CustomWebhook.waitForEvent("webrtcConnectionCreated", 2);
Assert.assertEquals("Wrong number of properties in event 'webrtcConnectionCreated'", 10 + 1,
event.keySet().size());
+ String connectionId1 = event.get("participantId").getAsString();
event = CustomWebhook.waitForEvent("recordingStatusChanged", 10);
Assert.assertEquals("Wrong number of properties in event 'recordingStatusChanged'", 11 + 1,
@@ -3065,11 +3452,53 @@ public class OpenViduTestAppE2eTest {
user.getDriver().findElement(By.id("add-user-btn")).click();
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
- CustomWebhook.waitForEvent("participantJoined", 2);
+ event = CustomWebhook.waitForEvent("participantJoined", 2);
CustomWebhook.waitForEvent("webrtcConnectionCreated", 2);
CustomWebhook.waitForEvent("webrtcConnectionCreated", 2);
CustomWebhook.waitForEvent("webrtcConnectionCreated", 2);
+ String connectionId2 = event.get("participantId").getAsString();
+
+ // signalSent from client
+ long timestamp = System.currentTimeMillis();
+ user.getDriver().findElement(By.cssSelector(("#openvidu-instance-0 .message-btn"))).click();
+ user.getEventManager().waitUntilEventReaches("signal:chat", 2);
+ event = CustomWebhook.waitForEvent("signalSent", 1);
+ Assert.assertEquals("Wrong number of properties in event 'signalSent'", 6 + 1, event.keySet().size());
+ Assert.assertEquals("Wrong sessionId in webhook event", "TestSession",
+ event.get("sessionId").getAsString());
+ Assert.assertTrue("Wrong timestamp in webhook event", event.get("timestamp").getAsLong() > timestamp);
+ Assert.assertEquals("Wrong from in webhook event", connectionId1, event.get("from").getAsString());
+ Assert.assertEquals("Wrong type in webhook event", "chat", event.get("type").getAsString());
+ Assert.assertTrue("Wrong data in webhook event", !event.get("data").getAsString().isEmpty());
+ Assert.assertEquals("Wrong event name in webhook event", "signalSent", event.get("event").getAsString());
+ JsonArray toArray = event.get("to").getAsJsonArray();
+ Assert.assertEquals("Wrong to array size", 2, toArray.size());
+ Assert.assertTrue("Wrong to array content in webhook event",
+ toArray.contains(JsonParser.parseString(connectionId1)));
+ Assert.assertTrue("Wrong to array content in webhook event",
+ toArray.contains(JsonParser.parseString(connectionId2)));
+
+ // signalSent from server
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/signal",
+ "{'session':'TestSession','type':'chat','to':['" + connectionId1 + "'],'data':'SERVER_DATA'}",
+ HttpStatus.SC_OK);
+ user.getEventManager().waitUntilEventReaches("signal:chat", 3);
+ event = CustomWebhook.waitForEvent("signalSent", 1);
+ Assert.assertEquals("Wrong number of properties in event 'signalSent'", 6 + 1, event.keySet().size());
+ Assert.assertEquals("Wrong sessionId in webhook event", "TestSession",
+ event.get("sessionId").getAsString());
+ Assert.assertTrue("Wrong timestamp in webhook event", event.get("timestamp").getAsLong() > timestamp);
+ Assert.assertTrue("Wrong from in webhook event", event.get("from").isJsonNull());
+ Assert.assertEquals("Wrong type in webhook event", "chat", event.get("type").getAsString());
+ Assert.assertEquals("Wrong data in webhook event", "SERVER_DATA", event.get("data").getAsString());
+ Assert.assertEquals("Wrong event name in webhook event", "signalSent", event.get("event").getAsString());
+ toArray = event.get("to").getAsJsonArray();
+ Assert.assertEquals("Wrong to array size", 1, toArray.size());
+ Assert.assertTrue("Wrong to array content in webhook event",
+ toArray.contains(JsonParser.parseString(connectionId1)));
+
user.getDriver().findElement(By.id("session-api-btn-0")).click();
Thread.sleep(1000);
user.getDriver().findElement(By.id("close-session-btn")).click();
@@ -3135,7 +3564,7 @@ public class OpenViduTestAppE2eTest {
log.info("IP camera test");
CountDownLatch initLatch = new CountDownLatch(1);
- io.openvidu.test.browsers.utils.CustomWebhook.main(new String[0], initLatch);
+ io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch);
try {
@@ -3151,26 +3580,29 @@ public class OpenViduTestAppE2eTest {
CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
// Wrong session [404]
- restClient.rest(HttpMethod.POST, "/api/sessions/WRONG_SESSION/connection", "{}", HttpStatus.SC_NOT_FOUND);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/WRONG_SESSION/connection", "{'type':'IPCAM'}",
+ HttpStatus.SC_NOT_FOUND);
// Init a session and publish IP camera AS FIRST PARTICIPANT
- restClient.rest(HttpMethod.POST, "/api/sessions", "{'customSessionId':'IP_CAM_SESSION'}", HttpStatus.SC_OK,
- true,
- "{'id':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','forcedVideoCodec':'STR','allowTranscoding':false,'connections':{'numberOfElements':0,'content':[]},'recording':true}");
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", "{'customSessionId':'IP_CAM_SESSION'}",
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_SESSION);
// No rtspUri [400]
- restClient.rest(HttpMethod.POST, "/api/sessions/IP_CAM_SESSION/connection", "{}",
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/IP_CAM_SESSION/connection", "{'type':'IPCAM'}",
HttpStatus.SC_BAD_REQUEST);
// Wrong rtspUri (invalid url format) [400]
- restClient.rest(HttpMethod.POST, "/api/sessions/IP_CAM_SESSION/connection", "{'rtspUri': 'NOT_A_URL'}",
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/IP_CAM_SESSION/connection",
+ "{'type':'IPCAM','rtspUri': 'NOT_A_URL'}", HttpStatus.SC_BAD_REQUEST);
+ // Wrong adaptativeBitrate [400]
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/IP_CAM_SESSION/connection",
+ "{'type':'IPCAM','rtspUri':'rtsp://dummyurl.com','adaptativeBitrate':123,}",
HttpStatus.SC_BAD_REQUEST);
// Publish IP camera. Dummy URL because no user will subscribe to it [200]
- String ipCamBody = "{'type':'IPCAM','rtspUri':'rtsp://dummyurl.com','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'data':'MY_IP_CAMERA'}";
- JsonObject response = restClient.rest(HttpMethod.POST, "/api/sessions/IP_CAM_SESSION/connection", ipCamBody,
- HttpStatus.SC_OK, true,
- "{'connectionId':'STR','createdAt':0,'location':'STR','platform':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}");
+ String ipCamBody = "{'type':'IPCAM','rtspUri':'rtsp://dummyurl.com','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':1000,'data':'MY_IP_CAMERA'}";
+ JsonObject response = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/IP_CAM_SESSION/connection",
+ ipCamBody, HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION);
CustomWebhook.waitForEvent("sessionCreated", 1);
CustomWebhook.waitForEvent("participantJoined", 1);
@@ -3178,7 +3610,8 @@ public class OpenViduTestAppE2eTest {
Assert.assertEquals("Wrong serverData property", "MY_IP_CAMERA", response.get("serverData").getAsString());
Assert.assertEquals("Wrong platform property", "IPCAM", response.get("platform").getAsString());
- Assert.assertEquals("Wrong role property", "PUBLISHER", response.get("role").getAsString());
+ Assert.assertEquals("Wrong role property", JsonNull.INSTANCE, response.get("role"));
+ Assert.assertEquals("Wrong type property", "IPCAM", response.get("type").getAsString());
Assert.assertEquals("Wrong number of publishers in IPCAM participant", 1,
response.get("publishers").getAsJsonArray().size());
@@ -3187,19 +3620,19 @@ public class OpenViduTestAppE2eTest {
Assert.assertEquals("Wrong rtspUri property", "rtsp://dummyurl.com",
ipCamPublisher.get("rtspUri").getAsString());
JsonObject mediaOptions = ipCamPublisher.get("mediaOptions").getAsJsonObject();
- Assert.assertEquals("Wrong number of properties in MediaOptions", 10, mediaOptions.size());
+ Assert.assertEquals("Wrong number of properties in MediaOptions", 11, mediaOptions.size());
Assert.assertTrue("Wrong adaptativeBitrate property", mediaOptions.get("adaptativeBitrate").getAsBoolean());
Assert.assertTrue("Wrong onlyPlayWithSubscribers property",
mediaOptions.get("onlyPlayWithSubscribers").getAsBoolean());
// Can't delete the stream [405]
restClient.rest(HttpMethod.DELETE,
- "/api/sessions/IP_CAM_SESSION/stream/" + ipCamPublisher.get("streamId").getAsString(),
+ "/openvidu/api/sessions/IP_CAM_SESSION/stream/" + ipCamPublisher.get("streamId").getAsString(),
HttpStatus.SC_METHOD_NOT_ALLOWED);
// Can delete the connection [204]
restClient.rest(HttpMethod.DELETE,
- "/api/sessions/IP_CAM_SESSION/connection/" + response.get("connectionId").getAsString(),
+ "/openvidu/api/sessions/IP_CAM_SESSION/connection/" + response.get("connectionId").getAsString(),
HttpStatus.SC_NO_CONTENT);
response = CustomWebhook.waitForEvent("webrtcConnectionDestroyed", 1);
@@ -3237,37 +3670,37 @@ public class OpenViduTestAppE2eTest {
CustomWebhook.waitForEvent("webrtcConnectionCreated", 1);
// Composed recording to get an MP4 file AUDIO + VIDEO
- restClient.rest(HttpMethod.POST, "/api/recordings/start",
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
"{'session':'TestSession','name':'audioVideo','hasAudio':true,'hasVideo':true}", HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("recordingStarted", 1); // Started
CustomWebhook.waitForEvent("recordingStatusChanged", 1);
Thread.sleep(4000);
- restClient.rest(HttpMethod.POST, "/api/recordings/stop/TestSession", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/TestSession", HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("recordingStopped", 1);
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Stopped
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Ready
// Composed recording to get an MP4 file ONLY VIDEO
- restClient.rest(HttpMethod.POST, "/api/recordings/start",
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
"{'session':'TestSession','name':'videoOnly','hasAudio':false,'hasVideo':true}", HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("recordingStarted", 1); // Started
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Started
Thread.sleep(4000);
- restClient.rest(HttpMethod.POST, "/api/recordings/stop/TestSession-1", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/TestSession-1", HttpStatus.SC_OK);
user.getEventManager().waitUntilEventReaches("recordingStopped", 1);
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Stopped
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Ready
// Publish the MP4 file as an IPCAM
- String recPath = restClient.rest(HttpMethod.GET, "/config", HttpStatus.SC_OK).get("OPENVIDU_RECORDING_PATH")
- .getAsString();
+ String recPath = restClient.rest(HttpMethod.GET, "/openvidu/api/config", HttpStatus.SC_OK)
+ .get("OPENVIDU_RECORDING_PATH").getAsString();
recPath = recPath.endsWith("/") ? recPath : (recPath + "/");
String fullRecordingPath = "file://" + recPath + "TestSession/audioVideo.mp4";
ipCamBody = "{'type':'IPCAM','rtspUri':'" + fullRecordingPath
- + "','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'data':'MY_IP_CAMERA'}";
+ + "','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':1000,'data':'MY_IP_CAMERA'}";
- restClient.rest(HttpMethod.POST, "/api/sessions/TestSession/connection", ipCamBody, HttpStatus.SC_OK, true,
- "{'connectionId':'STR','createdAt':0,'location':'STR','platform':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}");
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/TestSession/connection", ipCamBody,
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION);
user.getEventManager().waitUntilEventReaches("connectionCreated", 2);
user.getEventManager().waitUntilEventReaches("streamCreated", 2);
@@ -3293,9 +3726,8 @@ public class OpenViduTestAppE2eTest {
user.getWaiter().until(ExpectedConditions.numberOfElementsToBe(By.tagName("video"), 1));
// Publish again the IPCAM
- response = restClient.rest(HttpMethod.POST, "/api/sessions/TestSession/connection", ipCamBody,
- HttpStatus.SC_OK, true,
- "{'connectionId':'STR','createdAt':0,'location':'STR','platform':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}");
+ response = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/TestSession/connection", ipCamBody,
+ HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION);
user.getEventManager().waitUntilEventReaches("connectionCreated", 3);
user.getEventManager().waitUntilEventReaches("streamCreated", 3);
user.getEventManager().waitUntilEventReaches("streamPlaying", 3);
@@ -3313,27 +3745,27 @@ public class OpenViduTestAppE2eTest {
CustomWebhook.waitForEvent("webrtcConnectionDestroyed", 1);
CustomWebhook.waitForEvent("participantLeft", 1);
- restClient.rest(HttpMethod.GET, "/api/sessions/TestSession", null, HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/TestSession", null, HttpStatus.SC_OK);
// Test IPCAM individual recording (IPCAM audio+video, recording audio+video)
- response = restClient.rest(HttpMethod.POST, "/api/recordings/start",
+ response = restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
"{'session':'TestSession','outputMode':'INDIVIDUAL','hasAudio':true,'hasVideo':true}",
HttpStatus.SC_OK);
String recId = response.get("id").getAsString();
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Started
Thread.sleep(2000);
- restClient.rest(HttpMethod.POST, "/api/recordings/stop/" + recId, HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/" + recId, HttpStatus.SC_OK);
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Stopped
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Ready
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(recId);
- this.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
+ this.recordingUtils.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
// Test IPCAM individual recording (IPCAM video only, recording audio and video)
// Disconnect audio+video IPCAM
- restClient.rest(HttpMethod.DELETE, "/api/sessions/TestSession/connection/" + connectionId,
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/TestSession/connection/" + connectionId,
HttpStatus.SC_NO_CONTENT);
// Session is closed (create new session)
@@ -3341,12 +3773,13 @@ public class OpenViduTestAppE2eTest {
CustomWebhook.waitForEvent("participantLeft", 1);
CustomWebhook.waitForEvent("sessionDestroyed", 1);
- restClient.rest(HttpMethod.POST, "/api/sessions", "{'customSessionId':'TestSession'}", HttpStatus.SC_OK);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", "{'customSessionId':'TestSession'}",
+ HttpStatus.SC_OK);
// Publish video only IPCAM
fullRecordingPath = "file://" + recPath + "TestSession-1/videoOnly.mp4";
- ipCamBody = "{'rtspUri':'" + fullRecordingPath + "'}";
- response = restClient.rest(HttpMethod.POST, "/api/sessions/TestSession/connection", ipCamBody,
+ ipCamBody = "{'type':'IPCAM','rtspUri':'" + fullRecordingPath + "'}";
+ response = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/TestSession/connection", ipCamBody,
HttpStatus.SC_OK);
CustomWebhook.waitForEvent("sessionCreated", 1);
CustomWebhook.waitForEvent("participantJoined", 1);
@@ -3354,21 +3787,21 @@ public class OpenViduTestAppE2eTest {
// Record audio and video
// TODO: THIS SHOULD WORK
-// response = restClient.rest(HttpMethod.POST, "/api/recordings/start",
+// response = restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
// "{'session':'TestSession','outputMode':'INDIVIDUAL','hasAudio':true,'hasVideo':true}",
// HttpStatus.SC_OK);
// recId = response.get("id").getAsString();
// CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Started
//
// Thread.sleep(2000);
-// restClient.rest(HttpMethod.POST, "/api/recordings/stop/TestSession-2", HttpStatus.SC_OK);
+// restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/TestSession-2", HttpStatus.SC_OK);
// CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Stopped
// CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Ready
//
// recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(recId);
// this.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
- restClient.rest(HttpMethod.DELETE, "/api/sessions/TestSession", HttpStatus.SC_NO_CONTENT);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/TestSession", HttpStatus.SC_NO_CONTENT);
CustomWebhook.waitForEvent("webrtcConnectionDestroyed", 1);
CustomWebhook.waitForEvent("participantLeft", 1);
@@ -3379,281 +3812,502 @@ public class OpenViduTestAppE2eTest {
}
}
- private void listEmptyRecordings() {
- // List existing recordings (empty)
- user.getDriver().findElement(By.id("list-recording-btn")).click();
- user.getWaiter()
- .until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "Recording list []"));
- }
+ @Test
+ @DisplayName("OpenVidu SDK fetch test")
+ void openviduSdkFetchTest() throws Exception {
+ isRecordingTest = true;
- private ExpectedCondition waitForVideoDuration(WebElement element, int durationInSeconds) {
- return new ExpectedCondition() {
- @Override
- public Boolean apply(WebDriver input) {
- return element.getAttribute("duration").matches(
- durationInSeconds - 1 + "\\.[5-9][0-9]{0,5}|" + durationInSeconds + "\\.[0-5][0-9]{0,5}");
- }
- };
- }
+ setupBrowser("chrome");
- private boolean checkVideoAverageRgbGreen(Map rgb) {
- // GREEN color: {r < 15, g > 130, b <15}
- return (rgb.get("r") < 15) && (rgb.get("g") > 130) && (rgb.get("b") < 15);
- }
+ log.info("OpenVidu SDK fetch test");
- private boolean checkVideoAverageRgbGray(Map rgb) {
- // GRAY color: {r < 50, g < 50, b < 50} and the absolute difference between them
- // not greater than 2
- return (rgb.get("r") < 50) && (rgb.get("g") < 50) && (rgb.get("b") < 50)
- && (Math.abs(rgb.get("r") - rgb.get("g")) <= 2) && (Math.abs(rgb.get("r") - rgb.get("b")) <= 2)
- && (Math.abs(rgb.get("b") - rgb.get("g")) <= 2);
- }
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
- private void gracefullyLeaveParticipants(int numberOfParticipants) throws Exception {
- int accumulatedConnectionDestroyed = 0;
- for (int j = 1; j <= numberOfParticipants; j++) {
- user.getDriver().findElement(By.id("remove-user-btn")).sendKeys(Keys.ENTER);
- user.getEventManager().waitUntilEventReaches("sessionDisconnected", j);
- accumulatedConnectionDestroyed = (j != numberOfParticipants)
- ? (accumulatedConnectionDestroyed + numberOfParticipants - j)
- : (accumulatedConnectionDestroyed);
- user.getEventManager().waitUntilEventReaches("connectionDestroyed", accumulatedConnectionDestroyed);
- }
- }
+ Session session = OV.createSession();
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
- private String getBase64Screenshot(MyUser user) throws Exception {
- String screenshotBase64 = ((TakesScreenshot) user.getDriver()).getScreenshotAs(BASE64);
- return "data:image/png;base64," + screenshotBase64;
- }
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", "{'customSessionId':'REST_SESSION'}",
+ HttpStatus.SC_OK);
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
- private boolean recordedFileFine(File file, Recording recording) throws IOException {
- this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(),
- recording.getResolution(), "aac", "h264", true);
+ Connection connection = session.createConnection();
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
- boolean isFine = false;
- Picture frame;
+ // OpenVidu CE does not support Session#updateConnection method
try {
- // Get a frame at 75% duration and check that it has the expected color
- frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
- BufferedImage image = AWTUtil.toBufferedImage(frame);
- Map colorMap = this.averageColor(image);
-
- String realResolution = image.getWidth() + "x" + image.getHeight();
- Assert.assertEquals(
- "Resolution (" + recording.getResolution()
- + ") of recording entity is not equal to real video resolution (" + realResolution + ")",
- recording.getResolution(), realResolution);
-
- log.info("Recording map color: {}", colorMap.toString());
- log.info("Recording frame below");
- System.out.println(bufferedImageToBase64PngString(image));
- isFine = this.checkVideoAverageRgbGreen(colorMap);
- } catch (IOException | JCodecException e) {
- log.warn("Error getting frame from video recording: {}", e.getMessage());
- isFine = false;
- }
- return isFine;
- }
-
- private String bufferedImageToBase64PngString(BufferedImage image) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- String imageString = null;
- try {
- ImageIO.write(image, "png", bos);
- byte[] imageBytes = bos.toByteArray();
- imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
- bos.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- return imageString;
- }
-
- private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
- String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
-
- // Should be only 2 files: zip and metadata
- File folder = new File(recPath);
- Assert.assertEquals("There are more than 2 files (ZIP and metadata) inside individual recording folder "
- + recPath + ": " + Arrays.toString(folder.listFiles()), 2, folder.listFiles().length);
-
- File file1 = new File(recPath + recording.getName() + ".zip");
- File file2 = new File(recPath + ".recording." + recording.getId());
-
- Assert.assertTrue("File " + file1.getAbsolutePath() + " does not exist or is empty",
- file1.exists() && file1.length() > 0);
- Assert.assertTrue("File " + file2.getAbsolutePath() + " does not exist or is empty",
- file2.exists() && file2.length() > 0);
-
- List unzippedWebmFiles = new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
-
- Assert.assertEquals("Expecting " + numberOfVideoFiles + " videos inside ZIP file but "
- + unzippedWebmFiles.size() + " found: " + unzippedWebmFiles.toString(), numberOfVideoFiles,
- unzippedWebmFiles.size());
-
- File jsonSyncFile = new File(recPath + recording.getName() + ".json");
- Assert.assertTrue("JSON sync file " + jsonSyncFile.getAbsolutePath() + "does not exist or is empty",
- jsonSyncFile.exists() && jsonSyncFile.length() > 0);
-
- JsonObject jsonSyncMetadata;
- try {
- Gson gson = new Gson();
- JsonReader reader = new JsonReader(new FileReader(jsonSyncFile));
- jsonSyncMetadata = gson.fromJson(reader, JsonObject.class);
+ session.updateConnection(connection.getConnectionId(), new ConnectionProperties.Builder().build());
+ Assert.fail("Expected exception was not thrown by OpenVidu Java Client");
+ } catch (OpenViduHttpException e) {
+ Assert.assertEquals("Wrong OpenViduException status", HttpStatus.SC_METHOD_NOT_ALLOWED, e.getStatus());
} catch (Exception e) {
- log.error("Cannot read JSON sync metadata file from {}. Error: {}", jsonSyncFile.getAbsolutePath(),
- e.getMessage());
- Assert.fail("Cannot read JSON sync metadata file from " + jsonSyncFile.getAbsolutePath());
- return;
+ Assert.fail("Wrong exception type thrown by OpenVidu Java Client");
}
- long totalFileSize = 0;
- JsonArray syncArray = jsonSyncMetadata.get("files").getAsJsonArray();
- for (File webmFile : unzippedWebmFiles) {
- totalFileSize += webmFile.length();
+ restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", "{'session':'REST_SESSION'}", HttpStatus.SC_OK);
+ Assert.assertFalse("Fetch should be true", session.fetch());
+ Assert.assertTrue("Fetch should be false", OV.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
- Assert.assertTrue("WEBM file " + webmFile.getAbsolutePath() + " does not exist or is empty",
- webmFile.exists() && webmFile.length() > 0);
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/REST_SESSION", HttpStatus.SC_NO_CONTENT);
+ Assert.assertFalse("Java fetch should be true", session.fetch());
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
- double durationInSeconds = 0;
- boolean found = false;
- for (int i = 0; i < syncArray.size(); i++) {
- JsonObject j = syncArray.get(i).getAsJsonObject();
- if (webmFile.getName().contains(j.get("streamId").getAsString())) {
- durationInSeconds = (double) (j.get("endTimeOffset").getAsDouble()
- - j.get("startTimeOffset").getAsDouble()) / 1000;
- found = true;
- break;
- }
- }
+ // Set token and join session
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+ WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokeInput.clear();
+ tokeInput.sendKeys(connection.getToken());
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.className("join-btn")).click();
- Assert.assertTrue("Couldn't find in JSON sync object information for webm file " + webmFile.getName(),
- found);
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 1);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 1);
- log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(),
- durationInSeconds);
- this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds,
- recording.getResolution(), audioDecoder, videoDecoder, checkAudio);
- webmFile.delete();
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ checkNodeFetchChanged(false, true);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(true, false);
+
+ // Modify connection properties
+ user.getDriver().findElement(By.id("record-checkbox")).click();
+ user.getDriver().findElement(By.id("token-role-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-MODERATOR")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("connection-data-field")).sendKeys("MY_SERVER_DATA");
+
+ // Create Connection with openvidu-node-client
+ long timestamp = System.currentTimeMillis();
+ final String successMessage = "Connection created: ";
+ user.getDriver().findElement(By.id("crate-connection-api-btn")).click();
+ user.getWaiter()
+ .until(ExpectedConditions.attributeContains(By.id("api-response-text-area"), "value", successMessage));
+ String value = user.getDriver().findElement(By.id("api-response-text-area")).getAttribute("value");
+ // Check openvidu-node-client Connection properties
+ JsonObject connectionJson = JsonParser
+ .parseString(value.substring(value.lastIndexOf(successMessage) + successMessage.length()))
+ .getAsJsonObject();
+ JsonObject connectionProperties = connectionJson.get("connectionProperties").getAsJsonObject();
+ String connectionId = connectionJson.get("connectionId").getAsString();
+ Assert.assertEquals("Wrong status Connection property", "pending", connectionJson.get("status").getAsString());
+ Assert.assertTrue("Wrong timestamp Connection property",
+ connectionJson.get("createdAt").getAsLong() > timestamp);
+ Assert.assertTrue("Wrong activeAt Connection property", connectionJson.get("activeAt").isJsonNull());
+ Assert.assertTrue("Wrong location Connection property", connectionJson.get("location").isJsonNull());
+ Assert.assertTrue("Wrong platform Connection property", connectionJson.get("platform").isJsonNull());
+ Assert.assertTrue("Wrong clientData Connection property", connectionJson.get("clientData").isJsonNull());
+ Assert.assertTrue("Wrong publishers Connection property",
+ connectionJson.get("publishers").getAsJsonArray().size() == 0);
+ Assert.assertTrue("Wrong subscribers Connection property",
+ connectionJson.get("subscribers").getAsJsonArray().size() == 0);
+ Assert.assertTrue("Wrong token Connection property",
+ connectionJson.get("token").getAsString().contains(session.getSessionId()));
+ Assert.assertEquals("Wrong number of keys in connectionProperties", 9, connectionProperties.keySet().size());
+ Assert.assertEquals("Wrong type property", ConnectionType.WEBRTC.name(),
+ connectionProperties.get("type").getAsString());
+ Assert.assertEquals("Wrong data property", "MY_SERVER_DATA", connectionProperties.get("data").getAsString());
+ Assert.assertTrue("Wrong record property", connectionProperties.get("record").getAsBoolean()); // Is true in CE
+ Assert.assertEquals("Wrong role property", OpenViduRole.MODERATOR.name(),
+ connectionProperties.get("role").getAsString());
+ Assert.assertTrue("Wrong kurentoOptions property", connectionProperties.get("kurentoOptions").isJsonNull());
+ Assert.assertTrue("Wrong rtspUri property", connectionProperties.get("rtspUri").isJsonNull());
+ Assert.assertTrue("Wrong adaptativeBitrate property",
+ connectionProperties.get("adaptativeBitrate").isJsonNull());
+ Assert.assertTrue("Wrong onlyPlayWithSubscribers property",
+ connectionProperties.get("onlyPlayWithSubscribers").isJsonNull());
+ Assert.assertTrue("Wrong networkCache property", connectionProperties.get("networkCache").isJsonNull());
+
+ Assert.assertTrue("Java fetch should be true", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(false, false);
+ checkNodeFetchChanged(true, false);
+
+ // Delete Connection with openvidu-node-client
+ user.getDriver().findElement(By.id("connection-id-field")).clear();
+ user.getDriver().findElement(By.id("connection-id-field")).sendKeys(connectionId);
+ user.getDriver().findElement(By.id("force-disconnect-api-btn")).click();
+ user.getWaiter()
+ .until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", "User disconnected"));
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ checkNodeFetchChanged(false, false);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(false, false);
+
+ // RECORD
+ user.getDriver().findElement(By.id("rec-properties-btn")).click();
+ user.getDriver().findElement(By.id("rec-hasvideo-checkbox")).click();
+ user.getDriver().findElement(By.id("rec-outputmode-select")).click();
+ Thread.sleep(500);
+ user.getDriver().findElement(By.id("option-INDIVIDUAL")).click();
+ Thread.sleep(500);
+
+ user.getDriver().findElement(By.id("start-recording-btn")).click();
+ user.getEventManager().waitUntilEventReaches("recordingStarted", 1);
+
+ // Node SDK should return false as the recording has been started with it
+ checkNodeFetchChanged(false, false);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(true, false);
+ // Java SDK should return true as it doesn't know about the recording yet
+ Assert.assertTrue("Java fetch should be true", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+
+ OV.stopRecording(session.getSessionId());
+ user.getEventManager().waitUntilEventReaches("recordingStopped", 1);
+ // Java SDK should return false as the recording has been stopped with it
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ // Node SDK should return true as it doesn't know about the recording stooped
+ checkNodeFetchChanged(false, true);
+ checkNodeFetchChanged(false, false);
+ checkNodeFetchChanged(true, false);
+
+ // NEW SUBSCRIBER
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ user.getDriver().findElement(By.id("session-name-input-1")).clear();
+ user.getDriver().findElement(By.id("session-name-input-1")).sendKeys(session.getSessionId());
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .publish-checkbox")).click();
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(false, false);
+
+ // MODIFY STREAM
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .pub-video-btn")).click();
+ user.getEventManager().waitUntilEventReaches("streamPropertyChanged", 2);
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+ Assert.assertTrue("Java fetch should be true", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(false, true);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(false, false);
+
+ // REMOVE STREAM
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .sub-btn")).click();
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+ Assert.assertTrue("Java fetch should be true", OV.fetch());
+ Assert.assertFalse("Java fetch should be false", session.fetch());
+ checkNodeFetchChanged(true, true);
+ checkNodeFetchChanged(true, false);
+ checkNodeFetchChanged(false, false);
+
+ // REMOVE USER
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .leave-btn")).click();
+ user.getEventManager().waitUntilEventReaches("connectionDestroyed", 1);
+ user.getDriver().findElement(By.id("session-api-btn-0")).click();
+ Thread.sleep(1000);
+ Assert.assertTrue("Java fetch should be true", session.fetch());
+ Assert.assertFalse("Java fetch should be false", OV.fetch());
+ checkNodeFetchChanged(false, true);
+ checkNodeFetchChanged(false, false);
+ checkNodeFetchChanged(true, false);
+ }
+
+ @Test
+ @DisplayName("Force codec default config")
+ void forceDefaultCodec() throws Exception {
+ log.info("Force codec default config");
+ setupBrowser("chrome");
+ this.forceCodecGenericE2eTest();
+ }
+
+ @Test
+ @DisplayName("Force valid codec - Not Allow Transcoding")
+ void forceValidCodecNotAllowTranscodingTest() throws Exception {
+ log.info("Force codec Chrome - Force VP8 - Not Allow Transcoding");
+ setupBrowser("chrome");
+ this.forceCodecGenericE2eTest(VideoCodec.VP8, false);
+ this.user.getDriver().close();
+
+ log.info("Force codec Chrome - Force H264 - Not Allow Transcoding");
+ setupBrowser("chrome");
+ this.forceCodecGenericE2eTest(VideoCodec.H264, false);
+ this.user.getDriver().close();
+ }
+
+ @Test
+ @DisplayName("Force valid codec - Allow Transcoding")
+ void forceValidCodecAllowTranscodingTest() throws Exception {
+ log.info("Force codec Chrome - Force VP8 - Allow Transcoding");
+ setupBrowser("chrome");
+ this.forceCodecGenericE2eTest(VideoCodec.VP8, true);
+ this.user.getDriver().close();
+
+ log.info("Force codec Chrome - Force H264 - Allow Transcoding");
+ setupBrowser("chrome");
+ this.forceCodecGenericE2eTest(VideoCodec.H264, true);
+ this.user.getDriver().close();
+ }
+
+ @Test
+ @DisplayName("Force not valid codec - Not Allow Transcoding")
+ void forceCodecNotValidCodecNotAllowTranscoding() throws Exception {
+ // Start firefox with OpenH264 disabled to check not supported codecs
+ log.info("Force codec Firefox - Force H264 - Allow Transcoding - Disabled H264 in Firefox");
+ setupBrowser("firefoxDisabledOpenH264");
+ this.forceNotSupportedCodec(VideoCodec.H264, false);
+ }
+
+ @Test
+ @DisplayName("Force not valid codec - Allow Transcoding")
+ void forceCodecNotValidCodecAllowTranscoding() throws Exception {
+ // Start firefox with OpenH264 disabled to check not supported codecs
+ setupBrowser("firefoxDisabledOpenH264");
+ log.info("Force codec Firefox - Force H264 - Allow Transcoding - Disabled H264 in Firefox");
+ this.forceNotSupportedCodec(VideoCodec.H264, true);
+ }
+
+ private void checkNodeFetchChanged(boolean global, boolean hasChanged) {
+ user.getDriver().findElement(By.id(global ? "list-sessions-btn" : "get-session-btn")).click();
+ user.getWaiter().until(new NodeFetchHasChanged(hasChanged));
+ }
+
+ private class NodeFetchHasChanged implements ExpectedCondition {
+
+ private boolean hasChanged;
+
+ public NodeFetchHasChanged(boolean hasChanged) {
+ this.hasChanged = hasChanged;
}
- Assert.assertEquals("Size of recording entity (" + recording.getSessionId()
- + ") is not equal to real file size (" + totalFileSize + ")", recording.getSize(), totalFileSize);
-
- jsonSyncFile.delete();
- }
-
- private void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration, String resolution,
- String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
- // Check tracks, duration, resolution, framerate and decoders
- MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath());
-
- if (hasVideo) {
- if (checkAudio) {
- if (hasAudio) {
- Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have audio",
- metadata.hasAudio() && metadata.hasVideo());
- Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
- } else {
- Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have video",
- metadata.hasVideo());
- Assert.assertFalse(metadata.hasAudio());
- }
- }
- if (resolution != null) {
- Assert.assertEquals(resolution, metadata.getVideoWidth() + "x" + metadata.getVideoHeight());
- }
- Assert.assertTrue(metadata.getVideoDecoder().toLowerCase().contains(videoDecoder));
- } else if (hasAudio && checkAudio) {
- Assert.assertTrue(metadata.hasAudio());
- Assert.assertFalse(metadata.hasVideo());
- Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
- } else {
- Assert.fail("Cannot check a file witho no audio and no video");
+ @Override
+ public Boolean apply(WebDriver driver) {
+ return driver.findElement(By.id("api-response-text-area")).getAttribute("value")
+ .endsWith("Changes: " + hasChanged);
}
- // Check duration with 1 decimal precision
- DecimalFormat df = new DecimalFormat("#0.0");
- df.setRoundingMode(RoundingMode.UP);
- log.info("Duration of {} according to ffmpeg: {} s", file.getName(), metadata.getDuration());
- log.info("Duration of {} according to 'duration' property: {} s", file.getName(), duration);
- log.info("Difference in s duration: {}", Math.abs(metadata.getDuration() - duration));
- final double difference = 10;
- Assert.assertTrue(
- "Difference between recording entity duration (" + duration + ") and real video duration ("
- + metadata.getDuration() + ") is greater than " + difference + " in file " + file.getName(),
- Math.abs((metadata.getDuration() - duration)) < difference);
}
- private boolean thumbnailIsFine(File file) {
- boolean isFine = false;
- BufferedImage image = null;
- try {
- image = ImageIO.read(file);
- } catch (IOException e) {
- log.error(e.getMessage());
- return false;
- }
- log.info("Recording thumbnail dimensions: {}x{}", image.getWidth(), image.getHeight());
- Map colorMap = this.averageColor(image);
- log.info("Thumbnail map color: {}", colorMap.toString());
- isFine = this.checkVideoAverageRgbGreen(colorMap);
- return isFine;
+ /**
+ * Test default config of forced codec and allowTranscoding
+ */
+ private void forceCodecGenericE2eTest() throws Exception {
+ forceCodecGenericE2eTest(null, null);
}
- private Map averageColor(BufferedImage bi) {
- int x0 = 0;
- int y0 = 0;
- int w = bi.getWidth();
- int h = bi.getHeight();
- int x1 = x0 + w;
- int y1 = y0 + h;
- long sumr = 0, sumg = 0, sumb = 0;
- for (int x = x0; x < x1; x++) {
- for (int y = y0; y < y1; y++) {
- Color pixel = new Color(bi.getRGB(x, y));
- sumr += pixel.getRed();
- sumg += pixel.getGreen();
- sumb += pixel.getBlue();
- }
- }
- int num = w * h;
- Map colorMap = new HashMap<>();
- colorMap.put("r", (long) (sumr / num));
- colorMap.put("g", (long) (sumg / num));
- colorMap.put("b", (long) (sumb / num));
- return colorMap;
- }
+ /**
+ * Test to force specified codec and allowTranscoding
+ *
+ * @param codec codec to force. If null, default value in openvidu
+ * config will be used.
+ * @param allowTranscoding If true, allow transcoding. If null, default value in
+ * openvidu config will be used.
+ */
+ private void forceCodecGenericE2eTest(VideoCodec codec, Boolean allowTranscoding) throws Exception {
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+ String sessionName = "CUSTOM_SESSION_" + ((codec != null) ? codec.name() : "DEFAULT_FORCE_CODEC");
- private void startKms() {
- log.info("Starting KMS");
- commandLine.executeCommand("/usr/bin/kurento-media-server &>> /kms.log &");
- }
+ // Configure Session to force Codec
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ WebElement sessionName1 = user.getDriver().findElement(By.id("session-name-input-0"));
+ sessionName1.clear();
+ sessionName1.sendKeys(sessionName);
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
- private void stopKms() {
- log.info("Stopping KMS");
- commandLine.executeCommand("kill -9 $(pidof kurento-media-server)");
- }
-
- private void restartKms() {
- this.stopKms();
- try {
+ if (codec != null) {
+ user.getDriver().findElement(By.id("forced-video-codec-select")).click();
Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ user.getDriver().findElement(By.id("option-" + codec.name())).click();
}
- this.startKms();
+ if (allowTranscoding != null && allowTranscoding) {
+ user.getDriver().findElement(By.id("allow-transcoding-checkbox")).click();
+ }
+
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ // Join Session
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ WebElement sessionName2 = user.getDriver().findElement(By.id("session-name-input-1"));
+ sessionName2.clear();
+ sessionName2.sendKeys(sessionName);
+
+ user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER));
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
+
+ // Load properties from session object of node-client
+ user.getDriver().findElement(By.id("session-info-btn-0")).click();
+ JsonObject res = JsonParser
+ .parseString(user.getDriver().findElement(By.id("session-text-area")).getAttribute("value"))
+ .getAsJsonObject();
+ VideoCodec sessionCodecNodeClient = VideoCodec
+ .valueOf(res.get("properties").getAsJsonObject().get("forcedVideoCodec").getAsString());
+ boolean sessionAllowTranscodingNodeClient = res.get("properties").getAsJsonObject().get("allowTranscoding")
+ .getAsBoolean();
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 4 videos but found " + numberOfVideos, 4, numberOfVideos);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+
+ // Assert Selected Codec in node-client session object
+ if (codec != null) {
+ // If specified codec, assert selected codec
+ Assert.assertEquals(sessionCodecNodeClient, codec);
+ } else {
+ // If not specified, assert default codec
+ Assert.assertEquals(sessionCodecNodeClient, defaultForcedVideoCodec);
+ }
+
+ // Assert Selected allow transcoding in node-client session object
+ if (allowTranscoding != null) {
+ // If specified allowTranscoding, assert selected
+ Assert.assertEquals(sessionAllowTranscodingNodeClient, allowTranscoding);
+ } else {
+ // If not specified, assert default allowTranscoding
+ Assert.assertEquals(sessionAllowTranscodingNodeClient, defaultAllowTranscoding);
+ }
+
+ // Check browser codecs
+ VideoCodec codecToCheck = (codec != null) ? codec : defaultForcedVideoCodec;
+ List statsButtons = user.getDriver().findElements(By.className("stats-button"));
+ for (WebElement statButton : statsButtons) {
+ statButton.click();
+ Thread.sleep(1000);
+ String videoCodecUsed = user.getDriver().findElement(By.id("video-codec-used")).getText();
+ Assert.assertEquals(videoCodecUsed, "video/" + codecToCheck);
+ user.getDriver().findElement(By.id("close-dialog-btn")).click();
+ }
+
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/" + sessionName, HttpStatus.SC_NO_CONTENT);
}
- private void checkDockerContainerRunning(String imageName, int amount) {
- int number = Integer.parseInt(commandLine.executeCommand("docker ps | grep " + imageName + " | wc -l"));
- Assert.assertEquals("Wrong number of Docker containers for image " + imageName + " running", amount, number);
+ /**
+ * Force codec not allowed by opened browser
+ *
+ * @throws Exception
+ */
+ private void forceNotSupportedCodec(VideoCodec codec, boolean allowTranscoding) throws Exception {
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
+
+ String sessionName = "CUSTOM_SESSION_CODEC_NOT_SUPPORTED";
+
+ // Configure Session to force Codec
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ WebElement sessionNameElem = user.getDriver().findElement(By.id("session-name-input-0"));
+ sessionNameElem.clear();
+ sessionNameElem.sendKeys(sessionName);
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.id("forced-video-codec-select")).click();
+ Thread.sleep(1000);
+ user.getDriver().findElement(By.id("option-" + codec.name())).click();
+
+ if (allowTranscoding) {
+ user.getDriver().findElement(By.id("allow-transcoding-checkbox")).click();
+ }
+
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ if (allowTranscoding) {
+ // If transcoding is enabled everything should work fine
+
+ // Join another user
+ user.getDriver().findElement(By.id("add-user-btn")).click();
+ WebElement sessionName2 = user.getDriver().findElement(By.id("session-name-input-1"));
+ sessionName2.clear();
+ sessionName2.sendKeys(sessionName);
+
+ List joinButtons = user.getDriver().findElements(By.className("join-btn"));
+ for (WebElement el : joinButtons) {
+ Thread.sleep(5000);
+ el.sendKeys(Keys.ENTER);
+ }
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 2);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 4);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 4);
+
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 4 videos but found " + numberOfVideos, 4, numberOfVideos);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+
+ } else {
+ // If transcoding not allowed it should return an alert with error
+ user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER));
+ user.getWaiter().until(ExpectedConditions.alertIsPresent());
+ Alert alert = user.getDriver().switchTo().alert();
+ Assert.assertTrue("Alert does not contain expected text",
+ alert.getText().contains("Error forcing codec: '" + codec.name() + "'"));
+ alert.accept();
+ }
+
+ restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/" + sessionName, HttpStatus.SC_NO_CONTENT);
+ Thread.sleep(1000);
}
- private void removeAllRecordingContiners() {
- commandLine.executeCommand("docker ps -a | awk '{ print $1,$2 }' | grep " + RECORDING_IMAGE
- + " | awk '{print $1 }' | xargs -I {} docker rm -f {}");
+ private void assertTranscodingSessionProperties(Session sessionDefaultCodec, Session sessionH264AllowTranscoding,
+ Session sessionVP9AllowTranscoding) {
+ // Check session with default transcoding params
+ Assert.assertEquals("Wrong default forcedVideoCodec", defaultForcedVideoCodec,
+ sessionDefaultCodec.getProperties().forcedVideoCodec());
+ Assert.assertEquals("Wrong default allowTranscoding", defaultAllowTranscoding,
+ sessionDefaultCodec.getProperties().isTranscodingAllowed());
+
+ // Check session which use H264 and allow transcoding
+ Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.H264,
+ sessionH264AllowTranscoding.getProperties().forcedVideoCodec());
+ Assert.assertEquals("Wrong default allowTranscoding", true,
+ sessionH264AllowTranscoding.getProperties().isTranscodingAllowed());
+
+ // Check session which use VP9 and allow transcoding
+ Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.VP9,
+ sessionVP9AllowTranscoding.getProperties().forcedVideoCodec());
+ Assert.assertEquals("Wrong default allowTranscoding", true,
+ sessionVP9AllowTranscoding.getProperties().isTranscodingAllowed());
}
}
diff --git a/openvidu-testapp/docker/Dockerfile b/openvidu-testapp/docker/Dockerfile
index 849344d3..da002e91 100644
--- a/openvidu-testapp/docker/Dockerfile
+++ b/openvidu-testapp/docker/Dockerfile
@@ -1,5 +1,5 @@
FROM ubuntu:16.04
-MAINTAINER openvidu@gmail.com
+MAINTAINER info@openvidu.io
# Install Kurento Media Server (KMS)
RUN echo "deb [arch=amd64] http://ubuntu.openvidu.io/6.7.2 xenial kms6" | tee /etc/apt/sources.list.d/kurento.list \
diff --git a/openvidu-testapp/package.json b/openvidu-testapp/package.json
index bf2e5bbe..4b450a9e 100644
--- a/openvidu-testapp/package.json
+++ b/openvidu-testapp/package.json
@@ -15,8 +15,8 @@
"colormap": "2.3.1",
"core-js": "3.4.7",
"hammerjs": "2.0.8",
- "openvidu-browser": "2.15.0",
- "openvidu-node-client": "2.11.0",
+ "openvidu-browser": "2.16.0",
+ "openvidu-node-client": "2.16.0",
"rxjs": "6.5.3",
"zone.js": "0.10.2"
},
@@ -44,5 +44,5 @@
"start": "ng serve",
"test": "ng test"
},
- "version": "2.15.0"
+ "version": "2.16.0"
}
\ No newline at end of file
diff --git a/openvidu-testapp/src/app/app.module.ts b/openvidu-testapp/src/app/app.module.ts
index 419f888b..29dadb34 100644
--- a/openvidu-testapp/src/app/app.module.ts
+++ b/openvidu-testapp/src/app/app.module.ts
@@ -31,6 +31,7 @@ import { OpenviduRestService } from './services/openvidu-rest.service';
import { OpenviduParamsService } from './services/openvidu-params.service';
import { TestFeedService } from './services/test-feed.service';
import { MuteSubscribersService } from './services/mute-subscribers.service';
+import {SessionInfoDialogComponent} from "./components/dialogs/session-info-dialog/session-info-dialog.component";
@NgModule({
declarations: [
@@ -49,6 +50,8 @@ import { MuteSubscribersService } from './services/mute-subscribers.service';
PublisherPropertiesDialogComponent,
ScenarioPropertiesDialogComponent,
FilterDialogComponent,
+ ShowCodecDialogComponent,
+ SessionInfoDialogComponent,
UsersTableComponent,
TableVideoComponent,
ShowCodecDialogComponent
@@ -77,7 +80,8 @@ import { MuteSubscribersService } from './services/mute-subscribers.service';
PublisherPropertiesDialogComponent,
ScenarioPropertiesDialogComponent,
FilterDialogComponent,
- ShowCodecDialogComponent
+ ShowCodecDialogComponent,
+ SessionInfoDialogComponent
],
bootstrap: [AppComponent]
})
diff --git a/openvidu-testapp/src/app/components/dialogs/filter-dialog/filter-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/filter-dialog/filter-dialog.component.html
index bd7a5f8d..23fa0dcd 100644
--- a/openvidu-testapp/src/app/components/dialogs/filter-dialog/filter-dialog.component.html
+++ b/openvidu-testapp/src/app/components/dialogs/filter-dialog/filter-dialog.component.html
@@ -5,19 +5,19 @@
Apply filter
Apply
-
+
-
+
Exec filter method
Exec
-
+
-
+
@@ -34,7 +34,7 @@
Filter events
-
+
Add listener to filter event
diff --git a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.css b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.css
index 7df97a20..5ff30ddb 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.css
+++ b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.css
@@ -31,6 +31,7 @@ mat-dialog-content button {
.inner-text-input {
margin-left: 9px;
+ width: 30%;
}
#rec-properties-btn {
diff --git a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.html
index cc50ca9d..ddd9bf27 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.html
+++ b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.html
@@ -6,26 +6,55 @@
Fetch
Fetch all
Close this session
-
-
-
-
-
- Force
- disconnect
- Force
- unpublish
+
+ Connections/Streams
+
+
+
+
+
+
+
+ Record
+
+
+
+
+ {{ enumerator }}
+
+
+
+
+
+
+
+
+ Create connection
+ Update
+ connection
+ Disconnect
+ Unpublish
+
+
+
Recordings
List recordings
Start recording
-
- {{recPropertiesIcon}}
+
+
+ {{recPropertiesIcon}}
diff --git a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.ts
index f1acab0e..d5890f95 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.ts
+++ b/openvidu-testapp/src/app/components/dialogs/session-api-dialog/session-api-dialog.component.ts
@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
-import { OpenVidu as OpenViduAPI, Session as SessionAPI, Recording, RecordingProperties, RecordingLayout } from 'openvidu-node-client';
+import { OpenVidu as OpenViduAPI, Session as SessionAPI, Recording, RecordingProperties, RecordingLayout, ConnectionProperties, OpenViduRole } from 'openvidu-node-client';
@Component({
selector: 'app-session-api-dialog',
@@ -14,16 +14,24 @@ export class SessionApiDialogComponent {
session: SessionAPI;
sessionId: string;
recordingId: string;
- resourceId: string;
+ connectionId: string;
+ streamId: string;
response: string;
recordingProperties: RecordingProperties;
recMode = Recording.OutputMode;
recLayouts = RecordingLayout;
+ openviduRoles = OpenViduRole;
customLayout = '';
recPropertiesIcon = 'add_circle';
showRecProperties = false;
+ connectionProperties: ConnectionProperties = {
+ record: true,
+ role: OpenViduRole.PUBLISHER,
+ data: ''
+ };
+
constructor(public dialogRef: MatDialogRef,
@Inject(MAT_DIALOG_DATA) public data) {
this.OV = data.openVidu;
@@ -50,7 +58,18 @@ export class SessionApiDialogComponent {
startRecording() {
console.log('Starting recording');
- this.OV.startRecording(this.sessionId, this.recordingProperties)
+ const finalRecordingProperties = {
+ name: this.recordingProperties.name,
+ outputMode: this.recordingProperties.outputMode,
+ recordingLayout: this.recordingProperties.recordingLayout,
+ customLayout: this.recordingProperties.customLayout,
+ resolution: this.recordingProperties.resolution,
+ hasAudio: this.recordingProperties.hasAudio,
+ hasVideo: this.recordingProperties.hasVideo,
+ shmSize: this.recordingProperties.shmSize,
+ mediaNode: !this.recordingProperties.mediaNode.id ? undefined : this.recordingProperties.mediaNode
+ }
+ this.OV.startRecording(this.sessionId, finalRecordingProperties)
.then(recording => {
this.response = 'Recording started [' + recording.id + ']';
})
@@ -145,7 +164,7 @@ export class SessionApiDialogComponent {
forceDisconnect() {
console.log('Forcing disconnect');
- this.session.forceDisconnect(this.resourceId)
+ this.session.forceDisconnect(this.connectionId)
.then(() => {
this.response = 'User disconnected';
})
@@ -156,7 +175,7 @@ export class SessionApiDialogComponent {
forceUnpublish() {
console.log('Forcing unpublish');
- this.session.forceUnpublish(this.resourceId)
+ this.session.forceUnpublish(this.streamId)
.then(() => {
this.response = 'Stream unpublished';
})
@@ -165,6 +184,32 @@ export class SessionApiDialogComponent {
});
}
+ createConnection() {
+ console.log('Creating connection');
+ this.session.createConnection(this.connectionProperties)
+ .then(connection => {
+ this.response = 'Connection created: ' + JSON.stringify(connection);
+ })
+ .catch(error => {
+ this.response = 'Error [' + error.message + ']';
+ });
+ }
+
+ updateConnection() {
+ console.log('Updating connection');
+ this.session.updateConnection(this.connectionId, this.connectionProperties)
+ .then(modifiedConnection => {
+ this.response = 'Connection updated: ' + JSON.stringify({
+ role: modifiedConnection.connectionProperties.role,
+ record: modifiedConnection.connectionProperties.record,
+ data: modifiedConnection.connectionProperties.data
+ });
+ })
+ .catch(error => {
+ this.response = 'Error [' + error.message + ']';
+ });
+ }
+
enumToArray(enumerator: any) {
return Object.keys(enumerator);
}
diff --git a/openvidu-testapp/src/app/components/dialogs/session-info-dialog/session-info-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/session-info-dialog/session-info-dialog.component.ts
new file mode 100644
index 00000000..8c166ea7
--- /dev/null
+++ b/openvidu-testapp/src/app/components/dialogs/session-info-dialog/session-info-dialog.component.ts
@@ -0,0 +1,38 @@
+import {Component, Inject} from "@angular/core";
+import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
+import {Session as SessionAPI} from "openvidu-node-client";
+
+@Component({
+ selector: 'app-session-info-dialog',
+ template: `
+
+
+
+
+
+
+ CLOSE
+
+ `,
+ styles: [`
+ #app-session-info-dialog-container {
+ text-align: center
+ }
+ #response-text-area {
+ width: 100%;
+ }
+ `]
+})
+export class SessionInfoDialogComponent {
+
+ sessionAPIContent: string;
+
+ constructor(public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data) {
+ const sessionApi = data.sessionAPI;
+ delete sessionApi.ov;
+ this.sessionAPIContent = JSON.stringify(sessionApi, null, 4);
+ }
+
+
+}
diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css
index 782565c8..d151819b 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css
+++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css
@@ -39,5 +39,9 @@ mat-radio-button:first-child {
}
#allow-transcoding-div {
- margin-bottom: 10px;
-}
\ No newline at end of file
+ margin-bottom: 10px;
+}
+
+#record-div {
+ padding-bottom: 20px;
+}
diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html
index e9eb8de6..6b06c0fa 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html
+++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html
@@ -27,16 +27,29 @@
-
+
- {{ enumerator }}
+ {{ enumerator }}
+ [(ngModel)]="sessionProperties.defaultCustomLayout" id="default-custom-layout-input">
+
+
+ Allow Transcoding
+
+
+
+
+ {{ enumerator }}
+
+
@@ -131,6 +156,6 @@
CANCEL
SAVE
+ [mat-dialog-close]="{sessionProperties: sessionProperties, turnConf: turnConf, manualTurnConf: manualTurnConf, customToken: customToken, forcePublishing: forcePublishing, connectionProperties: generateConnectionProperties()}">SAVE
diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts
index f06f3201..964d4f89 100644
--- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts
+++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts
@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
-import { SessionProperties, MediaMode, Recording, RecordingMode, RecordingLayout, TokenOptions, VideoCodec } from 'openvidu-node-client';
+import { SessionProperties, MediaMode, Recording, RecordingMode, RecordingLayout, ConnectionProperties, VideoCodec } from 'openvidu-node-client';
@Component({
selector: 'app-session-properties-dialog',
@@ -14,7 +14,9 @@ export class SessionPropertiesDialogComponent {
turnConf: string;
manualTurnConf: RTCIceServer = { urls: [] };
customToken: string;
- tokenOptions: TokenOptions;
+ forcePublishing: boolean = false;
+ connectionProperties: ConnectionProperties;
+ forceVideoCodec = VideoCodec;
filterName = 'GStreamerFilter';
filters: string[] = [];
@@ -30,17 +32,18 @@ export class SessionPropertiesDialogComponent {
this.sessionProperties = data.sessionProperties;
this.turnConf = data.turnConf;
this.manualTurnConf = data.manualTurnConf;
- this.tokenOptions = data.tokenOptions;
this.customToken = data.customToken;
+ this.forcePublishing = data.forcePublishing;
+ this.connectionProperties = data.connectionProperties;
}
enumToArray(enumerator: any) {
return Object.keys(enumerator);
}
- generateTokenOptions(): TokenOptions {
- this.tokenOptions.kurentoOptions.allowedFilters = this.filters;
- return this.tokenOptions;
+ generateConnectionProperties(): ConnectionProperties {
+ this.connectionProperties.kurentoOptions.allowedFilters = this.filters;
+ return this.connectionProperties;
}
}
diff --git a/openvidu-testapp/src/app/components/dialogs/show-codec-dialog/show-codec-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/show-codec-dialog/show-codec-dialog.component.ts
index c5b9678a..80b5f7a8 100644
--- a/openvidu-testapp/src/app/components/dialogs/show-codec-dialog/show-codec-dialog.component.ts
+++ b/openvidu-testapp/src/app/components/dialogs/show-codec-dialog/show-codec-dialog.component.ts
@@ -2,26 +2,25 @@ import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
@Component({
- selector: 'app-codec-used-dialog',
- template: `
+ selector: 'app-codec-used-dialog',
+ template: `
Used Codec: {{usedVideoCodec}}
CLOSE
-
`,
- styles: [`
+ styles: [`
#app-codec-dialog-container {
text-align: center
}
`]
})
export class ShowCodecDialogComponent {
-
- usedVideoCodec;
- constructor(public dialogRef: MatDialogRef,
- @Inject(MAT_DIALOG_DATA) public data) {
- this.usedVideoCodec = data.usedVideoCodec;
- }
-}
\ No newline at end of file
+ usedVideoCodec;
+
+ constructor(public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data) {
+ this.usedVideoCodec = data.usedVideoCodec;
+ }
+}
diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html
index 8014c7ea..7bef7a03 100644
--- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html
+++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html
@@ -18,26 +18,29 @@
-
+
settings
cloud_circle
-
+
notifications
+
+ info
+
-
JOIN
- Subscribe
+ Subscribe
Publish
@@ -48,26 +51,32 @@
Send
- Audio
- Video
+ Audio
+
+ Video
+
Enter active
- Audio
- Video
+ Audio
+ Video
-
+
Video
@@ -75,15 +84,16 @@
Screen
- Subscribe
+ Subscribe
to remote
-
+
settings
{{sessionName}}
+
+ linked_camera
+
chat
@@ -133,8 +147,8 @@
-
+
diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts
index d5a4b454..dc8b0d32 100644
--- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts
+++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts
@@ -4,9 +4,9 @@ import {
} from '@angular/core';
import {
- OpenVidu, Session, Subscriber, Publisher, Event, VideoInsertMode, StreamEvent, ConnectionEvent,
+ OpenVidu, Session, Subscriber, Publisher, Event, StreamEvent, ConnectionEvent,
SessionDisconnectedEvent, SignalEvent, RecordingEvent,
- PublisherSpeakingEvent, PublisherProperties, StreamPropertyChangedEvent, OpenViduError
+ PublisherSpeakingEvent, PublisherProperties, StreamPropertyChangedEvent, ConnectionPropertyChangedEvent, OpenViduError, NetworkQualityLevelChangedEvent
} from 'openvidu-browser';
import {
OpenVidu as OpenViduAPI,
@@ -15,11 +15,11 @@ import {
MediaMode,
RecordingMode,
RecordingLayout,
- TokenOptions,
+ Connection,
+ ConnectionProperties,
OpenViduRole,
RecordingProperties,
Recording,
- VideoCodec
} from 'openvidu-node-client';
import { MatDialog, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
import { ExtensionDialogComponent } from '../dialogs/extension-dialog/extension-dialog.component';
@@ -28,6 +28,8 @@ import { EventsDialogComponent } from '../dialogs/events-dialog/events-dialog.co
import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog/session-properties-dialog.component';
import { SessionApiDialogComponent } from '../dialogs/session-api-dialog/session-api-dialog.component';
import { PublisherPropertiesDialogComponent } from '../dialogs/publisher-properties-dialog/publisher-properties-dialog.component';
+import { SessionInfoDialogComponent } from "../dialogs/session-info-dialog/session-info-dialog.component";
+import {ShowCodecDialogComponent} from "../dialogs/show-codec-dialog/show-codec-dialog.component";
export interface SessionConf {
@@ -117,6 +119,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
streamCreated: true,
streamDestroyed: true,
streamPropertyChanged: true,
+ connectionPropertyChanged: true,
+ networkQualityLevelChanged: true,
recordingStarted: true,
recordingStopped: true,
signal: true,
@@ -130,8 +134,10 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
turnConf = 'auto';
manualTurnConf: RTCIceServer = { urls: [] };
customToken: string;
- tokenOptions: TokenOptions = {
+ forcePublishing: boolean;
+ connectionProperties: ConnectionProperties = {
role: OpenViduRole.PUBLISHER,
+ record: true,
kurentoOptions: {
videoMaxRecvBandwidth: 1000,
videoMinRecvBandwidth: 300,
@@ -143,6 +149,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
events: OpenViduEvent[] = [];
+ republishPossible: boolean = false;
openviduError: any;
constructor(
@@ -189,22 +196,21 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.clientData = 'TestClient';
}
- joinSession(): void {
-
+ async joinSession(): Promise {
if (this.session) {
this.leaveSession();
}
-
+ const sessionId = !!this.customToken ? this.getSessionIdFromToken(this.customToken) : this.sessionName;
+ await this.initializeNodeClient(sessionId);
if (!!this.customToken) {
this.joinSessionShared(this.customToken);
} else {
- this.getToken().then(token => {
- this.joinSessionShared(token);
- });
+ const connection: Connection = await this.createConnection();
+ this.joinSessionShared(connection.token);
}
}
- private joinSessionShared(token): void {
+ private joinSessionShared(token: string): void {
this.OV = new OpenVidu();
@@ -223,6 +229,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
streamCreated: false,
streamDestroyed: false,
streamPropertyChanged: false,
+ connectionPropertyChanged: false,
+ networkQualityLevelChanged: false,
recordingStarted: false,
recordingStopped: false,
signal: false,
@@ -236,7 +244,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
.then(() => {
this.changeDetector.detectChanges();
- if (this.publishTo && this.session.capabilities.publish) {
+ if (this.publishTo && this.session.capabilities.publish || this.forcePublishing) {
// this.asyncInitPublisher();
this.syncInitPublisher();
}
@@ -372,6 +380,24 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
}
}
+ if (this.sessionEvents.connectionPropertyChanged !== oldValues.connectionPropertyChanged || firstTime) {
+ this.session.off('connectionPropertyChanged');
+ if (this.sessionEvents.connectionPropertyChanged) {
+ this.session.on('connectionPropertyChanged', (event: ConnectionPropertyChangedEvent) => {
+ this.updateEventList('connectionPropertyChanged', event.changedProperty + ' [' + event.newValue + ']', event);
+ });
+ }
+ }
+
+ if (this.sessionEvents.networkQualityLevelChanged !== oldValues.networkQualityLevelChanged || firstTime) {
+ this.session.off('networkQualityLevelChanged');
+ if (this.sessionEvents.networkQualityLevelChanged) {
+ this.session.on('networkQualityLevelChanged', (event: NetworkQualityLevelChangedEvent) => {
+ this.updateEventList('networkQualityLevelChanged', event.connection.connectionId + ' [new:' + event.newValue + ',old:' + event.oldValue + ']', event);
+ });
+ }
+ }
+
if (this.sessionEvents.connectionCreated !== oldValues.connectionCreated || firstTime) {
this.session.off('connectionCreated');
if (this.sessionEvents.connectionCreated) {
@@ -495,9 +521,19 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.publisher.subscribeToRemote();
}
- this.session.publish(this.publisher).catch((error: OpenViduError) => {
+ this.session.publish(this.publisher).then(() => {
+ this.republishPossible = false;
+ }).catch((error: OpenViduError) => {
console.error(error);
+ if (!error.name) {
+ alert(error);
+ } else {
+ alert(error.name + ": " + error.message);
+ }
+
+ this.republishPossible = true;
this.session.unpublish(this.publisher);
+ delete this.publisher;
});
}
@@ -560,7 +596,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
turnConf: this.turnConf,
manualTurnConf: this.manualTurnConf,
customToken: this.customToken,
- tokenOptions: this.tokenOptions
+ forcePublishing: this.forcePublishing,
+ connectionProperties: this.connectionProperties,
},
width: '450px'
});
@@ -574,7 +611,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.turnConf = result.turnConf;
this.manualTurnConf = result.manualTurnConf;
this.customToken = result.customToken;
- this.tokenOptions = result.tokenOptions;
+ this.forcePublishing = result.forcePublishing;
+ this.connectionProperties = result.connectionProperties;
}
document.getElementById('session-settings-btn-' + this.index).classList.remove('cdk-program-focused');
});
@@ -594,7 +632,10 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
customLayout: this.sessionProperties.defaultCustomLayout,
resolution: '1920x1080',
hasAudio: true,
- hasVideo: true
+ hasVideo: true,
+ mediaNode: {
+ id: ''
+ }
}
},
width: '425px',
@@ -619,6 +660,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
streamCreated: this.sessionEvents.streamCreated,
streamDestroyed: this.sessionEvents.streamDestroyed,
streamPropertyChanged: this.sessionEvents.streamPropertyChanged,
+ connectionPropertyChanged: this.sessionEvents.connectionPropertyChanged,
+ networkQualityLevelChanged: this.sessionEvents.networkQualityLevelChanged,
recordingStarted: this.sessionEvents.recordingStarted,
recordingStopped: this.sessionEvents.recordingStopped,
signal: this.sessionEvents.signal,
@@ -651,6 +694,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
streamCreated: result.streamCreated,
streamDestroyed: result.streamDestroyed,
streamPropertyChanged: result.streamPropertyChanged,
+ connectionPropertyChanged: result.connectionPropertyChanged,
+ networkQualityLevelChanged: result.networkQualityLevelChanged,
recordingStarted: result.recordingStarted,
recordingStopped: result.recordingStopped,
signal: result.signal,
@@ -679,16 +724,23 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
});
}
- getToken(): Promise {
+ openSessionInfo() {
+ this.dialog.open(SessionInfoDialogComponent, {
+ data: {
+ sessionAPI: this.sessionAPI
+ },
+ width: '450px'
+ });
+ }
+
+ async initializeNodeClient(sessionId: string): Promise {
this.OV_NodeClient = new OpenViduAPI(this.openviduUrl, this.openviduSecret);
- if (!this.sessionProperties.customSessionId) {
- this.sessionProperties.customSessionId = this.sessionName;
- }
- return this.OV_NodeClient.createSession(this.sessionProperties)
- .then(session_NodeClient => {
- this.sessionAPI = session_NodeClient;
- return session_NodeClient.generateToken({ role: this.tokenOptions.role, kurentoOptions: this.tokenOptions.kurentoOptions });
- });
+ this.sessionProperties.customSessionId = sessionId;
+ this.sessionAPI = await this.OV_NodeClient.createSession(this.sessionProperties);
+ }
+
+ async createConnection(): Promise {
+ return this.sessionAPI.createConnection(this.connectionProperties);
}
updateEventFromChild(event: OpenViduEvent) {
@@ -717,4 +769,19 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.publisherProperties.videoSource !== 'screen');
}
+ republishAfterError() {
+ this.syncInitPublisher();
+ }
+
+ private getSessionIdFromToken(token: string): string {
+ const queryParams = decodeURI(token.split('?')[1])
+ .split('&')
+ .map(param => param.split('='))
+ .reduce((values, [key, value]) => {
+ values[key] = value
+ return values
+ }, {});
+ return queryParams['sessionId'];
+ }
+
}
diff --git a/openvidu-testapp/src/app/components/test-apirest/test-apirest.component.ts b/openvidu-testapp/src/app/components/test-apirest/test-apirest.component.ts
index b1db644c..d68deb9d 100644
--- a/openvidu-testapp/src/app/components/test-apirest/test-apirest.component.ts
+++ b/openvidu-testapp/src/app/components/test-apirest/test-apirest.component.ts
@@ -91,7 +91,7 @@ export class TestApirestComponent implements OnInit, OnDestroy {
getToken() {
const sessionId = this.data[this.selectedRadioIndex][0];
- this.openviduRestService.getToken(this.openviduUrl, this.openviduSecret, sessionId, this.selectedRole, this.serverData)
+ this.openviduRestService.getToken(sessionId, this.selectedRole, this.serverData)
.then((token) => {
this.updateData();
})
diff --git a/openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.ts b/openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.ts
index 62b48932..8de48df9 100644
--- a/openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.ts
+++ b/openvidu-testapp/src/app/components/test-scenarios/test-scenarios.component.ts
@@ -84,6 +84,8 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
textAreaValue = '';
isFocusedOnReport = false;
+ private API_PATH = 'openvidu/api';
+
constructor(
private openviduParamsService: OpenviduParamsService,
private testFeedService: TestFeedService,
@@ -368,7 +370,7 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
private updateRemoteStreamsInfo() {
let headers = new HttpHeaders();
headers = headers.append('Authorization', 'Basic ' + btoa('OPENVIDUAPP:' + this.openviduSecret));
- this.http.get(this.openviduUrl + 'api/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
+ this.http.get(this.openviduUrl + this.API_PATH + '/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
sessionInfo => {
this.report.streamsOut.content.forEach(report => {
@@ -445,7 +447,7 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
/*addReportForStreamConcurrent(event: StreamManagerWrapper) {
let headers = new HttpHeaders();
headers = headers.append('Authorization', 'Basic ' + btoa('OPENVIDUAPP:' + this.openviduSecret));
- this.http.get(this.openviduUrl + 'api/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
+ this.http.get(this.openviduUrl + this.API_PATH + '/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
sessionInfo => {
event.streamManager.stream.getSelectedIceCandidate()
diff --git a/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.ts b/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.ts
index 6db82d6c..bdb486ed 100644
--- a/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.ts
+++ b/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.ts
@@ -106,15 +106,15 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
if (key !== 'ee' && key !== 'openvidu') {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
- // Duplicate reference found
+ // Duplicate reference found
try {
- // If this value does not reference a parent
+ // If this value does not reference a parent
return JSON.parse(JSON.stringify(value));
} catch (error) {
return;
}
}
- // Store value in our collection
+ // Store value in our collection
cache.push(value);
}
return value;
diff --git a/openvidu-testapp/src/app/components/video/video.component.html b/openvidu-testapp/src/app/components/video/video.component.html
index 4f7af0ab..0a9830f8 100644
--- a/openvidu-testapp/src/app/components/video/video.component.html
+++ b/openvidu-testapp/src/app/components/video/video.component.html
@@ -6,7 +6,7 @@
notifications
- info
+ info
@@ -45,7 +45,7 @@
notifications
- info
+ info
@@ -79,4 +79,4 @@
-
\ No newline at end of file
+
diff --git a/openvidu-testapp/src/app/components/video/video.component.ts b/openvidu-testapp/src/app/components/video/video.component.ts
index e5577b1f..2d968c38 100644
--- a/openvidu-testapp/src/app/components/video/video.component.ts
+++ b/openvidu-testapp/src/app/components/video/video.component.ts
@@ -1,5 +1,6 @@
import { Component, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';
+import { ShowCodecDialogComponent } from '../dialogs/show-codec-dialog/show-codec-dialog.component';
import {
StreamManager,
@@ -67,694 +68,693 @@ export class VideoComponent implements OnInit, OnDestroy {
pubSubAudioIcon = 'mic';
recordIcon = 'fiber_manual_record';
pauseRecordIcon = '';
-
+
// Stats
usedVideoCodec: string;
-
- constructor(private dialog: MatDialog, private muteSubscribersService: MuteSubscribersService
- ) { }
- ngOnInit() {
+ constructor(private dialog: MatDialog, private muteSubscribersService: MuteSubscribersService) { }
- if (this.streamManager.remote) {
- // Init subscriber events
- this.eventCollection = {
- videoElementCreated: true,
- videoElementDestroyed: true,
- streamPlaying: true,
- streamAudioVolumeChange: false,
- streamPropertyChanged: false
- };
- this.updateSubscriberEvents({
- videoElementCreated: false,
- videoElementDestroyed: false,
- streamPlaying: false,
- streamAudioVolumeChange: true,
- streamPropertyChanged: true
- });
+ ngOnInit() {
- } else {
- // Init publisher events
- this.eventCollection = {
- videoElementCreated: true,
- videoElementDestroyed: true,
- streamPlaying: true,
- streamAudioVolumeChange: false,
- accessAllowed: true,
- accessDenied: true,
- accessDialogOpened: true,
- accessDialogClosed: true,
- streamCreated: true,
- streamDestroyed: true,
- streamPropertyChanged: false
- };
- this.updatePublisherEvents(
- this.streamManager,
- {
- videoElementCreated: false,
- videoElementDestroyed: false,
- streamPlaying: false,
- streamAudioVolumeChange: true,
- accessAllowed: false,
- accessDenied: false,
- accessDialogOpened: false,
- accessDialogClosed: false,
- streamCreated: false,
- streamDestroyed: false,
- streamPropertyChanged: true
- });
- this.sendAudio = this.streamManager.stream.hasAudio;
- this.sendVideo = this.streamManager.stream.hasVideo;
- this.audioMuted = !this.properties.publishAudio;
- this.videoMuted = !this.properties.publishVideo;
- this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
- this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
- this.optionsVideo = this.streamManager.stream.typeOfVideo;
- }
+ if (this.streamManager.remote) {
+ // Init subscriber events
+ this.eventCollection = {
+ videoElementCreated: true,
+ videoElementDestroyed: true,
+ streamPlaying: true,
+ streamAudioVolumeChange: false,
+ streamPropertyChanged: false
+ };
+ this.updateSubscriberEvents({
+ videoElementCreated: false,
+ videoElementDestroyed: false,
+ streamPlaying: false,
+ streamAudioVolumeChange: true,
+ streamPropertyChanged: true
+ });
- this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(muteOrUnmute => {
- if (this.streamManager.remote) {
- this.streamManager.videos.forEach(v => {
- v.video.muted = muteOrUnmute;
- });
- }
- });
- }
+ } else {
+ // Init publisher events
+ this.eventCollection = {
+ videoElementCreated: true,
+ videoElementDestroyed: true,
+ streamPlaying: true,
+ streamAudioVolumeChange: false,
+ accessAllowed: true,
+ accessDenied: true,
+ accessDialogOpened: true,
+ accessDialogClosed: true,
+ streamCreated: true,
+ streamDestroyed: true,
+ streamPropertyChanged: false
+ };
+ this.updatePublisherEvents(
+ this.streamManager,
+ {
+ videoElementCreated: false,
+ videoElementDestroyed: false,
+ streamPlaying: false,
+ streamAudioVolumeChange: true,
+ accessAllowed: false,
+ accessDenied: false,
+ accessDialogOpened: false,
+ accessDialogClosed: false,
+ streamCreated: false,
+ streamDestroyed: false,
+ streamPropertyChanged: true
+ });
+ this.sendAudio = this.streamManager.stream.hasAudio;
+ this.sendVideo = this.streamManager.stream.hasVideo;
+ this.audioMuted = !this.properties.publishAudio;
+ this.videoMuted = !this.properties.publishVideo;
+ this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
+ this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
+ this.optionsVideo = this.streamManager.stream.typeOfVideo;
+ }
- ngOnDestroy() {
- if (!!this.recorder) {
- this.recorder.clean();
- }
- if (!!this.muteSubscribersSubscription) { this.muteSubscribersSubscription.unsubscribe(); }
- }
+ this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(muteOrUnmute => {
+ if (this.streamManager.remote) {
+ this.streamManager.videos.forEach(v => {
+ v.video.muted = muteOrUnmute;
+ });
+ }
+ });
+ }
- subUnsub() {
- const subscriber: Subscriber = this.streamManager;
- if (this.subbed) {
- this.streamManager.stream.session.unsubscribe(subscriber);
- this.restartRecorder();
+ ngOnDestroy() {
+ if (!!this.recorder) {
+ this.recorder.clean();
+ }
+ if (!!this.muteSubscribersSubscription) { this.muteSubscribersSubscription.unsubscribe(); }
+ }
- this.pubSubVideoIcon = '';
- this.pubSubAudioIcon = '';
- this.recordIcon = '';
- this.pauseRecordIcon = '';
- this.pubSubIcon = 'play_arrow';
- this.subbedVideo = false;
- this.subbedAudio = false;
- } else {
- const oldValues = {
- videoElementCreated: this.eventCollection.videoElementCreated,
- videoElementDestroyed: this.eventCollection.videoElementDestroyed,
- streamPlaying: this.eventCollection.streamPlaying,
- streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
- streamPropertyChanged: this.eventCollection.streamPropertyChanged
- };
- this.streamManager = this.streamManager.stream.session.subscribe(subscriber.stream, undefined);
- this.reSubbed.emit(this.streamManager);
+ subUnsub() {
+ const subscriber: Subscriber = this.streamManager;
+ if (this.subbed) {
+ this.streamManager.stream.session.unsubscribe(subscriber);
+ this.restartRecorder();
- this.pubSubVideoIcon = 'videocam';
- this.pubSubAudioIcon = 'mic';
- this.recordIcon = 'fiber_manual_record';
- this.pauseRecordIcon = '';
- this.pubSubIcon = 'stop';
- this.subbedVideo = true;
- this.subbedAudio = true;
+ this.pubSubVideoIcon = '';
+ this.pubSubAudioIcon = '';
+ this.recordIcon = '';
+ this.pauseRecordIcon = '';
+ this.pubSubIcon = 'play_arrow';
+ this.subbedVideo = false;
+ this.subbedAudio = false;
+ } else {
+ const oldValues = {
+ videoElementCreated: this.eventCollection.videoElementCreated,
+ videoElementDestroyed: this.eventCollection.videoElementDestroyed,
+ streamPlaying: this.eventCollection.streamPlaying,
+ streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
+ streamPropertyChanged: this.eventCollection.streamPropertyChanged
+ };
+ this.streamManager = this.streamManager.stream.session.subscribe(subscriber.stream, undefined);
+ this.reSubbed.emit(this.streamManager);
- this.updateSubscriberEvents(oldValues);
- }
- this.subbed = !this.subbed;
- }
+ this.pubSubVideoIcon = 'videocam';
+ this.pubSubAudioIcon = 'mic';
+ this.recordIcon = 'fiber_manual_record';
+ this.pauseRecordIcon = '';
+ this.pubSubIcon = 'stop';
+ this.subbedVideo = true;
+ this.subbedAudio = true;
- subUnsubVideo(connectionId: string) {
- const subscriber: Subscriber = this.streamManager;
- this.subbedVideo = !this.subbedVideo;
- subscriber.subscribeToVideo(this.subbedVideo);
- this.pubSubVideoIcon = this.subbedVideo ? 'videocam' : 'videocam_off';
- }
+ this.updateSubscriberEvents(oldValues);
+ }
+ this.subbed = !this.subbed;
+ }
- subUnsubAudio(connectionId: string) {
- const subscriber: Subscriber = this.streamManager;
- this.subbedAudio = !this.subbedAudio;
- subscriber.subscribeToAudio(this.subbedAudio);
- this.pubSubAudioIcon = this.subbedAudio ? 'mic' : 'mic_off';
- }
+ subUnsubVideo(connectionId: string) {
+ const subscriber: Subscriber = this.streamManager;
+ this.subbedVideo = !this.subbedVideo;
+ subscriber.subscribeToVideo(this.subbedVideo);
+ this.pubSubVideoIcon = this.subbedVideo ? 'videocam' : 'videocam_off';
+ }
- pubUnpub() {
- const publisher: Publisher = this.streamManager;
- if (this.unpublished) {
- this.streamManager.stream.session.publish(publisher)
- .then(() => {
- console.log(publisher);
- })
- .catch(e => {
- console.error(e);
- });
- } else {
- this.streamManager.stream.session.unpublish(publisher);
- }
- this.unpublished = !this.unpublished;
- this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop';
- }
+ subUnsubAudio(connectionId: string) {
+ const subscriber: Subscriber = this.streamManager;
+ this.subbedAudio = !this.subbedAudio;
+ subscriber.subscribeToAudio(this.subbedAudio);
+ this.pubSubAudioIcon = this.subbedAudio ? 'mic' : 'mic_off';
+ }
- pubUnpubVideo() {
- const publisher: Publisher = this.streamManager;
- this.videoMuted = !this.videoMuted;
- publisher.publishVideo(!this.videoMuted);
- this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
- }
+ pubUnpub() {
+ const publisher: Publisher = this.streamManager;
+ if (this.unpublished) {
+ this.streamManager.stream.session.publish(publisher)
+ .then(() => {
+ console.log(publisher);
+ })
+ .catch(e => {
+ console.error(e);
+ });
+ } else {
+ this.streamManager.stream.session.unpublish(publisher);
+ }
+ this.unpublished = !this.unpublished;
+ this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop';
+ }
- pubUnpubAudio() {
- const publisher: Publisher = this.streamManager;
- this.audioMuted = !this.audioMuted;
- publisher.publishAudio(!this.audioMuted);
- this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
- }
+ pubUnpubVideo() {
+ const publisher: Publisher = this.streamManager;
+ this.videoMuted = !this.videoMuted;
+ publisher.publishVideo(!this.videoMuted);
+ this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
+ }
- changePub() {
- let screenChange;
- if (!this.publisherChanged) {
- if (this.sendAudio && !this.sendVideo) {
- this.sendAudioChange = false;
- this.sendVideoChange = true;
- screenChange = false;
- } else if (!this.sendAudio && this.sendVideo) {
- this.sendAudioChange = true;
- this.sendVideoChange = false;
- } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'CAMERA') {
- this.sendAudioChange = false;
- this.sendVideoChange = true;
- screenChange = true;
- } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'SCREEN') {
- this.sendAudioChange = false;
- this.sendVideoChange = true;
- screenChange = false;
- }
- } else {
- this.sendAudioChange = this.sendAudio;
- this.sendVideoChange = this.sendVideo;
- screenChange = this.optionsVideo === 'SCREEN' ? true : false;
- }
+ pubUnpubAudio() {
+ const publisher: Publisher = this.streamManager;
+ this.audioMuted = !this.audioMuted;
+ publisher.publishAudio(!this.audioMuted);
+ this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
+ }
- this.unpublished = false;
+ changePub() {
+ let screenChange;
+ if (!this.publisherChanged) {
+ if (this.sendAudio && !this.sendVideo) {
+ this.sendAudioChange = false;
+ this.sendVideoChange = true;
+ screenChange = false;
+ } else if (!this.sendAudio && this.sendVideo) {
+ this.sendAudioChange = true;
+ this.sendVideoChange = false;
+ } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'CAMERA') {
+ this.sendAudioChange = false;
+ this.sendVideoChange = true;
+ screenChange = true;
+ } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'SCREEN') {
+ this.sendAudioChange = false;
+ this.sendVideoChange = true;
+ screenChange = false;
+ }
+ } else {
+ this.sendAudioChange = this.sendAudio;
+ this.sendVideoChange = this.sendVideo;
+ screenChange = this.optionsVideo === 'SCREEN' ? true : false;
+ }
- const otherPublisher = this.OV.initPublisher(
- undefined,
- {
- audioSource: this.sendAudioChange ? undefined : false,
- videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false,
- publishAudio: !this.audioMuted,
- publishVideo: !this.videoMuted,
- resolution: '640x480',
- frameRate: 30,
- insertMode: VideoInsertMode.APPEND
- },
- (err) => {
- if (err) {
- console.warn(err);
- if (err.name === 'SCREEN_EXTENSION_NOT_INSTALLED') {
- this.dialog.open(ExtensionDialogComponent, {
- data: { url: err.message },
- disableClose: true,
- width: '250px'
- });
- }
- }
- });
- this.updatePublisherEvents(otherPublisher, {
- videoElementCreated: !this.eventCollection.videoElementCreated,
- videoElementDestroyed: !this.eventCollection.videoElementDestroyed,
- streamPlaying: !this.eventCollection.streamPlaying,
- streamAudioVolumeChange: !this.eventCollection.streamAudioVolumeChange,
- accessAllowed: !this.eventCollection.accessAllowed,
- accessDenied: !this.eventCollection.accessDenied,
- accessDialogOpened: !this.eventCollection.accessDialogOpened,
- accessDialogClosed: !this.eventCollection.accessDialogClosed,
- streamCreated: !this.eventCollection.streamCreated,
- streamDestroyed: !this.eventCollection.streamDestroyed,
- streamPropertyChanged: !this.eventCollection.streamPropertyChanged,
- });
+ this.unpublished = false;
- const oldPublisher = this.streamManager;
- if (oldPublisher.isSubscribedToRemote) {
- otherPublisher.subscribeToRemote(true);
- }
+ const otherPublisher = this.OV.initPublisher(
+ undefined,
+ {
+ audioSource: this.sendAudioChange ? undefined : false,
+ videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false,
+ publishAudio: !this.audioMuted,
+ publishVideo: !this.videoMuted,
+ resolution: '640x480',
+ frameRate: 30,
+ insertMode: VideoInsertMode.APPEND
+ },
+ (err) => {
+ if (err) {
+ console.warn(err);
+ if (err.name === 'SCREEN_EXTENSION_NOT_INSTALLED') {
+ this.dialog.open(ExtensionDialogComponent, {
+ data: { url: err.message },
+ disableClose: true,
+ width: '250px'
+ });
+ }
+ }
+ });
+ this.updatePublisherEvents(otherPublisher, {
+ videoElementCreated: !this.eventCollection.videoElementCreated,
+ videoElementDestroyed: !this.eventCollection.videoElementDestroyed,
+ streamPlaying: !this.eventCollection.streamPlaying,
+ streamAudioVolumeChange: !this.eventCollection.streamAudioVolumeChange,
+ accessAllowed: !this.eventCollection.accessAllowed,
+ accessDenied: !this.eventCollection.accessDenied,
+ accessDialogOpened: !this.eventCollection.accessDialogOpened,
+ accessDialogClosed: !this.eventCollection.accessDialogClosed,
+ streamCreated: !this.eventCollection.streamCreated,
+ streamDestroyed: !this.eventCollection.streamDestroyed,
+ streamPropertyChanged: !this.eventCollection.streamPropertyChanged,
+ });
- otherPublisher.once('accessAllowed', () => {
- if (!this.unpublished) {
- this.streamManager.stream.session.unpublish(oldPublisher);
- }
- this.streamManager.stream.session.publish(otherPublisher).then(() => {
- this.streamManager = otherPublisher;
- });
- });
+ const oldPublisher = this.streamManager;
+ if (oldPublisher.isSubscribedToRemote) {
+ otherPublisher.subscribeToRemote(true);
+ }
- this.publisherChanged = !this.publisherChanged;
- }
+ otherPublisher.once('accessAllowed', () => {
+ if (!this.unpublished) {
+ this.streamManager.stream.session.unpublish(oldPublisher);
+ }
+ this.streamManager.stream.session.publish(otherPublisher).then(() => {
+ this.streamManager = otherPublisher;
+ });
+ });
- updateSubscriberEvents(oldValues) {
- const sub: Subscriber = this.streamManager;
+ this.publisherChanged = !this.publisherChanged;
+ }
- if (this.eventCollection.videoElementCreated) {
- if (!oldValues.videoElementCreated) {
- sub.on('videoElementCreated', (event: VideoElementEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'videoElementCreated',
- eventContent: event.element.id,
- event
- });
- });
- }
- } else {
- sub.off('videoElementCreated');
- }
+ updateSubscriberEvents(oldValues) {
+ const sub: Subscriber = this.streamManager;
- if (this.eventCollection.videoElementDestroyed) {
- if (!oldValues.videoElementDestroyed) {
- sub.on('videoElementDestroyed', (event: VideoElementEvent) => {
- this.showButtons = false;
- this.updateEventListInParent.emit({
- eventName: 'videoElementDestroyed',
- eventContent: event.element.id,
- event
- });
- });
- }
- } else {
- sub.off('videoElementDestroyed');
- }
+ if (this.eventCollection.videoElementCreated) {
+ if (!oldValues.videoElementCreated) {
+ sub.on('videoElementCreated', (event: VideoElementEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'videoElementCreated',
+ eventContent: event.element.id,
+ event
+ });
+ });
+ }
+ } else {
+ sub.off('videoElementCreated');
+ }
- if (this.eventCollection.streamPlaying) {
- if (!oldValues.streamPlaying) {
- sub.on('streamPlaying', (event: StreamManagerEvent) => {
- if (!sub.stream.hasVideo) {
- this.videoClasses = 'grey-background';
- this.videoPoster = 'assets/images/volume.png';
- } else {
- this.videoClasses = '';
- this.videoPoster = '';
- }
- this.showButtons = true;
- this.updateEventListInParent.emit({
- eventName: 'streamPlaying',
- eventContent: this.streamManager.stream.streamId,
- event
- });
- });
- }
- } else {
- sub.off('streamPlaying');
- }
+ if (this.eventCollection.videoElementDestroyed) {
+ if (!oldValues.videoElementDestroyed) {
+ sub.on('videoElementDestroyed', (event: VideoElementEvent) => {
+ this.showButtons = false;
+ this.updateEventListInParent.emit({
+ eventName: 'videoElementDestroyed',
+ eventContent: event.element.id,
+ event
+ });
+ });
+ }
+ } else {
+ sub.off('videoElementDestroyed');
+ }
- if (this.eventCollection.streamAudioVolumeChange) {
- if (!oldValues.streamAudioVolumeChange) {
- sub.on('streamAudioVolumeChange', (event: StreamManagerEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'streamAudioVolumeChange',
- eventContent: event.value['newValue'],
- event
- });
- });
- }
- } else {
- sub.off('streamAudioVolumeChange');
- }
+ if (this.eventCollection.streamPlaying) {
+ if (!oldValues.streamPlaying) {
+ sub.on('streamPlaying', (event: StreamManagerEvent) => {
+ if (!sub.stream.hasVideo) {
+ this.videoClasses = 'grey-background';
+ this.videoPoster = 'assets/images/volume.png';
+ } else {
+ this.videoClasses = '';
+ this.videoPoster = '';
+ }
+ this.showButtons = true;
+ this.updateEventListInParent.emit({
+ eventName: 'streamPlaying',
+ eventContent: this.streamManager.stream.streamId,
+ event
+ });
+ });
+ }
+ } else {
+ sub.off('streamPlaying');
+ }
- if (this.eventCollection.streamPropertyChanged) {
- if (!oldValues.streamPropertyChanged) {
- sub.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
- const newValue = event.changedProperty === 'videoDimensions' ?
- JSON.stringify(event.newValue) : event.newValue.toString();
- this.updateEventListInParent.emit({
- eventName: 'streamPropertyChanged',
- eventContent: event.changedProperty + ' [' + newValue + ']',
- event
- });
- });
- }
- } else {
- sub.off('streamPropertyChanged');
- }
- }
+ if (this.eventCollection.streamAudioVolumeChange) {
+ if (!oldValues.streamAudioVolumeChange) {
+ sub.on('streamAudioVolumeChange', (event: StreamManagerEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'streamAudioVolumeChange',
+ eventContent: event.value['newValue'],
+ event
+ });
+ });
+ }
+ } else {
+ sub.off('streamAudioVolumeChange');
+ }
- updatePublisherEvents(pub: Publisher, oldValues: any) {
- if (this.eventCollection.videoElementCreated) {
- if (!oldValues.videoElementCreated) {
- pub.on('videoElementCreated', (event: VideoElementEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'videoElementCreated',
- eventContent: event.element.id,
- event
- });
- });
- }
- } else {
- pub.off('videoElementCreated');
- }
+ if (this.eventCollection.streamPropertyChanged) {
+ if (!oldValues.streamPropertyChanged) {
+ sub.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
+ const newValue = event.changedProperty === 'videoDimensions' ?
+ JSON.stringify(event.newValue) : event.newValue.toString();
+ this.updateEventListInParent.emit({
+ eventName: 'streamPropertyChanged',
+ eventContent: event.changedProperty + ' [' + newValue + ']',
+ event
+ });
+ });
+ }
+ } else {
+ sub.off('streamPropertyChanged');
+ }
+ }
- if (this.eventCollection.accessAllowed) {
- if (!oldValues.accessAllowed) {
- pub.on('accessAllowed', () => {
- this.updateEventListInParent.emit({
- eventName: 'accessAllowed',
- eventContent: '',
- event: {
- type: 'accessAllowed',
- target: pub,
- cancelable: false,
- hasBeenPrevented: false,
- isDefaultPrevented: () => false,
- preventDefault: () => { },
- callDefaultBehavior: () => { }
- }
- });
- });
- }
- } else {
- pub.off('accessAllowed');
- }
+ updatePublisherEvents(pub: Publisher, oldValues: any) {
+ if (this.eventCollection.videoElementCreated) {
+ if (!oldValues.videoElementCreated) {
+ pub.on('videoElementCreated', (event: VideoElementEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'videoElementCreated',
+ eventContent: event.element.id,
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('videoElementCreated');
+ }
- if (this.eventCollection.accessDenied) {
- if (!oldValues.accessDenied) {
- pub.on('accessDenied', (error) => {
- this.updateEventListInParent.emit({
- eventName: 'accessDenied',
- eventContent: JSON.stringify(error),
- event: {
- type: 'accessDenied',
- target: pub,
- cancelable: false,
- hasBeenPrevented: false,
- isDefaultPrevented: () => false,
- preventDefault: () => { },
- callDefaultBehavior: () => { }
- }
- });
- });
- }
- } else {
- pub.off('accessDenied');
- }
+ if (this.eventCollection.accessAllowed) {
+ if (!oldValues.accessAllowed) {
+ pub.on('accessAllowed', () => {
+ this.updateEventListInParent.emit({
+ eventName: 'accessAllowed',
+ eventContent: '',
+ event: {
+ type: 'accessAllowed',
+ target: pub,
+ cancelable: false,
+ hasBeenPrevented: false,
+ isDefaultPrevented: () => false,
+ preventDefault: () => { },
+ callDefaultBehavior: () => { }
+ }
+ });
+ });
+ }
+ } else {
+ pub.off('accessAllowed');
+ }
- if (this.eventCollection.accessDialogOpened) {
- if (!oldValues.accessDialogOpened) {
- pub.on('accessDialogOpened', (e) => {
- this.updateEventListInParent.emit({
- eventName: 'accessDialogOpened',
- eventContent: '',
- event: {
- type: 'accessDialogOpened',
- target: pub,
- cancelable: false,
- hasBeenPrevented: false,
- isDefaultPrevented: () => false,
- preventDefault: () => { },
- callDefaultBehavior: () => { }
- }
- });
- });
- }
- } else {
- pub.off('accessDialogOpened');
- }
+ if (this.eventCollection.accessDenied) {
+ if (!oldValues.accessDenied) {
+ pub.on('accessDenied', (error) => {
+ this.updateEventListInParent.emit({
+ eventName: 'accessDenied',
+ eventContent: JSON.stringify(error),
+ event: {
+ type: 'accessDenied',
+ target: pub,
+ cancelable: false,
+ hasBeenPrevented: false,
+ isDefaultPrevented: () => false,
+ preventDefault: () => { },
+ callDefaultBehavior: () => { }
+ }
+ });
+ });
+ }
+ } else {
+ pub.off('accessDenied');
+ }
- if (this.eventCollection.accessDialogClosed) {
- if (!oldValues.accessDialogClosed) {
- pub.on('accessDialogClosed', (e) => {
- this.updateEventListInParent.emit({
- eventName: 'accessDialogClosed',
- eventContent: '',
- event: {
- type: 'accessDialogClosed',
- target: pub,
- cancelable: false,
- hasBeenPrevented: false,
- isDefaultPrevented: () => false,
- preventDefault: () => { },
- callDefaultBehavior: () => { }
- }
- });
- });
- }
- } else {
- pub.off('accessDialogClosed');
- }
+ if (this.eventCollection.accessDialogOpened) {
+ if (!oldValues.accessDialogOpened) {
+ pub.on('accessDialogOpened', (e) => {
+ this.updateEventListInParent.emit({
+ eventName: 'accessDialogOpened',
+ eventContent: '',
+ event: {
+ type: 'accessDialogOpened',
+ target: pub,
+ cancelable: false,
+ hasBeenPrevented: false,
+ isDefaultPrevented: () => false,
+ preventDefault: () => { },
+ callDefaultBehavior: () => { }
+ }
+ });
+ });
+ }
+ } else {
+ pub.off('accessDialogOpened');
+ }
- if (this.eventCollection.streamCreated) {
- if (!oldValues.streamCreated) {
- pub.on('streamCreated', (event: StreamEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'streamCreated',
- eventContent: event.stream.streamId,
- event
- });
- });
- }
- } else {
- pub.off('streamCreated');
- }
+ if (this.eventCollection.accessDialogClosed) {
+ if (!oldValues.accessDialogClosed) {
+ pub.on('accessDialogClosed', (e) => {
+ this.updateEventListInParent.emit({
+ eventName: 'accessDialogClosed',
+ eventContent: '',
+ event: {
+ type: 'accessDialogClosed',
+ target: pub,
+ cancelable: false,
+ hasBeenPrevented: false,
+ isDefaultPrevented: () => false,
+ preventDefault: () => { },
+ callDefaultBehavior: () => { }
+ }
+ });
+ });
+ }
+ } else {
+ pub.off('accessDialogClosed');
+ }
- if (this.eventCollection.streamDestroyed) {
- if (!oldValues.streamDestroyed) {
- pub.on('streamDestroyed', (event: StreamEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'streamDestroyed',
- eventContent: event.stream.streamId,
- event
- });
- if (event.reason.indexOf('forceUnpublish') !== -1) {
- this.unpublished = !this.unpublished;
- this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop';
- }
- });
- }
- } else {
- pub.off('streamDestroyed');
- }
+ if (this.eventCollection.streamCreated) {
+ if (!oldValues.streamCreated) {
+ pub.on('streamCreated', (event: StreamEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'streamCreated',
+ eventContent: event.stream.streamId,
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('streamCreated');
+ }
- if (this.eventCollection.streamPropertyChanged) {
- if (!oldValues.streamPropertyChanged) {
- pub.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
- const newValue = event.changedProperty === 'videoDimensions' ?
- JSON.stringify(event.newValue) : event.newValue.toString();
- this.updateEventListInParent.emit({
- eventName: 'streamPropertyChanged',
- eventContent: event.changedProperty + ' [' + newValue + ']',
- event
- });
- });
- }
- } else {
- pub.off('streamPropertyChanged');
- }
+ if (this.eventCollection.streamDestroyed) {
+ if (!oldValues.streamDestroyed) {
+ pub.on('streamDestroyed', (event: StreamEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'streamDestroyed',
+ eventContent: event.stream.streamId,
+ event
+ });
+ if (event.reason.indexOf('forceUnpublish') !== -1) {
+ this.unpublished = !this.unpublished;
+ this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop';
+ }
+ });
+ }
+ } else {
+ pub.off('streamDestroyed');
+ }
- if (this.eventCollection.videoElementDestroyed) {
- if (!oldValues.videoElementDestroyed) {
- pub.on('videoElementDestroyed', (event: VideoElementEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'videoElementDestroyed',
- eventContent: '(Publisher)',
- event
- });
- });
- }
- } else {
- pub.off('videoElementDestroyed');
- }
+ if (this.eventCollection.streamPropertyChanged) {
+ if (!oldValues.streamPropertyChanged) {
+ pub.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
+ const newValue = event.changedProperty === 'videoDimensions' ?
+ JSON.stringify(event.newValue) : event.newValue.toString();
+ this.updateEventListInParent.emit({
+ eventName: 'streamPropertyChanged',
+ eventContent: event.changedProperty + ' [' + newValue + ']',
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('streamPropertyChanged');
+ }
- if (this.eventCollection.streamPlaying) {
- if (!oldValues.streamPlaying) {
- pub.on('streamPlaying', (event: StreamManagerEvent) => {
- if (!pub.stream.hasVideo) {
- this.videoClasses = 'grey-background';
- this.videoPoster = 'assets/images/volume.png';
- } else {
- this.videoClasses = '';
- this.videoPoster = '';
- }
- this.showButtons = true;
- this.updateEventListInParent.emit({
- eventName: 'streamPlaying',
- eventContent: pub.stream.streamId,
- event
- });
- });
- }
- } else {
- pub.off('streamPlaying');
- }
+ if (this.eventCollection.videoElementDestroyed) {
+ if (!oldValues.videoElementDestroyed) {
+ pub.on('videoElementDestroyed', (event: VideoElementEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'videoElementDestroyed',
+ eventContent: '(Publisher)',
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('videoElementDestroyed');
+ }
- if (this.eventCollection.streamAudioVolumeChange) {
- if (!oldValues.streamAudioVolumeChange) {
- pub.on('streamAudioVolumeChange', (event: StreamManagerEvent) => {
- this.updateEventListInParent.emit({
- eventName: 'streamAudioVolumeChange',
- eventContent: event.value['newValue'],
- event
- });
- });
- }
- } else {
- pub.off('streamAudioVolumeChange');
- }
- }
+ if (this.eventCollection.streamPlaying) {
+ if (!oldValues.streamPlaying) {
+ pub.on('streamPlaying', (event: StreamManagerEvent) => {
+ if (!pub.stream.hasVideo) {
+ this.videoClasses = 'grey-background';
+ this.videoPoster = 'assets/images/volume.png';
+ } else {
+ this.videoClasses = '';
+ this.videoPoster = '';
+ }
+ this.showButtons = true;
+ this.updateEventListInParent.emit({
+ eventName: 'streamPlaying',
+ eventContent: pub.stream.streamId,
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('streamPlaying');
+ }
- openSubscriberEventsDialog() {
- const oldValues = {
- videoElementCreated: this.eventCollection.videoElementCreated,
- videoElementDestroyed: this.eventCollection.videoElementDestroyed,
- streamPlaying: this.eventCollection.streamPlaying,
- streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
- streamPropertyChanged: this.eventCollection.streamPropertyChanged
- };
- const dialogRef = this.dialog.open(EventsDialogComponent, {
- data: {
- eventCollection: this.eventCollection,
- target: 'Subscriber'
- },
- width: '295px',
- autoFocus: false,
- disableClose: true
- });
- dialogRef.afterClosed().subscribe((result) => {
- this.updateSubscriberEvents(oldValues);
- });
- }
+ if (this.eventCollection.streamAudioVolumeChange) {
+ if (!oldValues.streamAudioVolumeChange) {
+ pub.on('streamAudioVolumeChange', (event: StreamManagerEvent) => {
+ this.updateEventListInParent.emit({
+ eventName: 'streamAudioVolumeChange',
+ eventContent: event.value['newValue'],
+ event
+ });
+ });
+ }
+ } else {
+ pub.off('streamAudioVolumeChange');
+ }
+ }
- openPublisherEventsDialog() {
- const oldValues = {
- videoElementCreated: this.eventCollection.videoElementCreated,
- videoElementDestroyed: this.eventCollection.videoElementDestroyed,
- streamPlaying: this.eventCollection.streamPlaying,
- streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
- accessAllowed: this.eventCollection.accessAllowed,
- accessDenied: this.eventCollection.accessDenied,
- accessDialogOpened: this.eventCollection.accessDialogOpened,
- accessDialogClosed: this.eventCollection.accessDialogClosed,
- streamCreated: this.eventCollection.streamCreated,
- streamDestroyed: this.eventCollection.streamDestroyed,
- streamPropertyChanged: this.eventCollection.streamPropertyChanged
- };
- const dialogRef = this.dialog.open(EventsDialogComponent, {
- data: {
- eventCollection: this.eventCollection,
- target: 'Publisher'
- },
- width: '295px',
- autoFocus: false,
- disableClose: true
- });
- dialogRef.afterClosed().subscribe((result) => {
- this.updatePublisherEvents(this.streamManager, oldValues);
- });
- }
+ openSubscriberEventsDialog() {
+ const oldValues = {
+ videoElementCreated: this.eventCollection.videoElementCreated,
+ videoElementDestroyed: this.eventCollection.videoElementDestroyed,
+ streamPlaying: this.eventCollection.streamPlaying,
+ streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
+ streamPropertyChanged: this.eventCollection.streamPropertyChanged
+ };
+ const dialogRef = this.dialog.open(EventsDialogComponent, {
+ data: {
+ eventCollection: this.eventCollection,
+ target: 'Subscriber'
+ },
+ width: '295px',
+ autoFocus: false,
+ disableClose: true
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ this.updateSubscriberEvents(oldValues);
+ });
+ }
- record(): void {
- if (!this.recording) {
- this.recorder = this.OV.initLocalRecorder(this.streamManager.stream);
- this.recorder.record();
- this.recording = true;
- this.recordIcon = 'stop';
- this.pauseRecordIcon = 'pause';
- } else {
- this.recorder.stop()
- .then(() => {
- let dialogRef: MatDialogRef;
- dialogRef = this.dialog.open(LocalRecordingDialogComponent, {
- disableClose: true,
- data: {
- recorder: this.recorder
- }
- });
- dialogRef.componentInstance.myReference = dialogRef;
+ openPublisherEventsDialog() {
+ const oldValues = {
+ videoElementCreated: this.eventCollection.videoElementCreated,
+ videoElementDestroyed: this.eventCollection.videoElementDestroyed,
+ streamPlaying: this.eventCollection.streamPlaying,
+ streamAudioVolumeChange: this.eventCollection.streamAudioVolumeChange,
+ accessAllowed: this.eventCollection.accessAllowed,
+ accessDenied: this.eventCollection.accessDenied,
+ accessDialogOpened: this.eventCollection.accessDialogOpened,
+ accessDialogClosed: this.eventCollection.accessDialogClosed,
+ streamCreated: this.eventCollection.streamCreated,
+ streamDestroyed: this.eventCollection.streamDestroyed,
+ streamPropertyChanged: this.eventCollection.streamPropertyChanged
+ };
+ const dialogRef = this.dialog.open(EventsDialogComponent, {
+ data: {
+ eventCollection: this.eventCollection,
+ target: 'Publisher'
+ },
+ width: '295px',
+ autoFocus: false,
+ disableClose: true
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ this.updatePublisherEvents(this.streamManager, oldValues);
+ });
+ }
- dialogRef.afterOpen().subscribe(() => {
- this.muteSubscribersService.updateMuted(true);
- this.recorder.preview('recorder-preview').controls = true;
- });
- dialogRef.afterClosed().subscribe(() => {
- this.muteSubscribersService.updateMuted(false);
- this.restartRecorder();
- });
- })
- .catch((error) => {
- console.error('Error stopping LocalRecorder: ' + error);
- });
- }
- }
+ record(): void {
+ if (!this.recording) {
+ this.recorder = this.OV.initLocalRecorder(this.streamManager.stream);
+ this.recorder.record();
+ this.recording = true;
+ this.recordIcon = 'stop';
+ this.pauseRecordIcon = 'pause';
+ } else {
+ this.recorder.stop()
+ .then(() => {
+ let dialogRef: MatDialogRef;
+ dialogRef = this.dialog.open(LocalRecordingDialogComponent, {
+ disableClose: true,
+ data: {
+ recorder: this.recorder
+ }
+ });
+ dialogRef.componentInstance.myReference = dialogRef;
- pauseRecord(): void {
- if (!this.recordingPaused) {
- this.recorder.pause();
- this.pauseRecordIcon = 'play_arrow';
- } else {
- this.recorder.resume();
- this.pauseRecordIcon = 'pause';
- }
- this.recordingPaused = !this.recordingPaused;
- }
+ dialogRef.afterOpen().subscribe(() => {
+ this.muteSubscribersService.updateMuted(true);
+ this.recorder.preview('recorder-preview').controls = true;
+ });
+ dialogRef.afterClosed().subscribe(() => {
+ this.muteSubscribersService.updateMuted(false);
+ this.restartRecorder();
+ });
+ })
+ .catch((error) => {
+ console.error('Error stopping LocalRecorder: ' + error);
+ });
+ }
+ }
- private restartRecorder() {
- this.recording = false;
- this.recordingPaused = false;
- this.recordIcon = 'fiber_manual_record';
- this.pauseRecordIcon = '';
- if (!!this.recorder) {
- this.recorder.clean();
- }
- }
+ pauseRecord(): void {
+ if (!this.recordingPaused) {
+ this.recorder.pause();
+ this.pauseRecordIcon = 'play_arrow';
+ } else {
+ this.recorder.resume();
+ this.pauseRecordIcon = 'pause';
+ }
+ this.recordingPaused = !this.recordingPaused;
+ }
- forceUnpublish() {
- this.OV.session.forceUnpublish(this.streamManager.stream);
- }
+ private restartRecorder() {
+ this.recording = false;
+ this.recordingPaused = false;
+ this.recordIcon = 'fiber_manual_record';
+ this.pauseRecordIcon = '';
+ if (!!this.recorder) {
+ this.recorder.clean();
+ }
+ }
- forceDisconnect() {
- this.OV.session.forceDisconnect(this.streamManager.stream.connection);
- }
+ forceUnpublish() {
+ this.OV.session.forceUnpublish(this.streamManager.stream);
+ }
- filterConfig() {
- this.dialog.open(FilterDialogComponent, {
- data: {
- session: this.streamManager.stream.session,
- stream: this.streamManager.stream,
- filterEventHandler: this.emitFilterEventToParent.bind(this)
- },
- disableClose: true,
- width: '450px'
- });
- }
+ forceDisconnect() {
+ this.OV.session.forceDisconnect(this.streamManager.stream.connection);
+ }
- emitFilterEventToParent(event: FilterEvent) {
- this.updateEventListInParent.emit({
- eventName: 'filterEvent',
- eventContent: JSON.stringify(event.data),
- event
- });
- }
-
- async showCodecUsed() {
- let stats = await this.streamManager.stream.getWebRtcPeer().pc.getStats(null);
- let codecIdIndex = null;
- // Search codec Index
- stats.forEach(report => {
- if (!this.streamManager.remote && report.id.includes("RTCOutboundRTPVideoStream")) {
+ filterConfig() {
+ this.dialog.open(FilterDialogComponent, {
+ data: {
+ session: this.streamManager.stream.session,
+ stream: this.streamManager.stream,
+ filterEventHandler: this.emitFilterEventToParent.bind(this)
+ },
+ disableClose: true,
+ width: '450px'
+ });
+ }
+
+ emitFilterEventToParent(event: FilterEvent) {
+ this.updateEventListInParent.emit({
+ eventName: 'filterEvent',
+ eventContent: JSON.stringify(event.data),
+ event
+ });
+ }
+
+ async showCodecUsed() {
+ let stats = await this.streamManager.stream.getWebRtcPeer().pc.getStats(null);
+ let codecIdIndex = null;
+ // Search codec Index
+ stats.forEach(report => {
+ console.log(report);
+ if (!this.streamManager.remote && report.id.includes("RTCOutboundRTPVideoStream")) {
codecIdIndex = report.codecId;
-
- } else if (this.streamManager.remote && report.id.includes("RTCInboundRTPVideoStream")) {
+
+ } else if (this.streamManager.remote && report.id.includes("RTCInboundRTPVideoStream")) {
codecIdIndex = report.codecId;
- }
- })
- // Search codec Info
- stats.forEach(report => {
- if (report.id === codecIdIndex) {
+ }
+ })
+ // Search codec Info
+ stats.forEach(report => {
+ if (report.id === codecIdIndex) {
this.usedVideoCodec = report.mimeType;
- }
- })
- this.dialog.open(ShowCodecDialogComponent, {
- data: {
- usedVideoCodec: this.usedVideoCodec
- },
- width: '450px'
- });
- }
-
+ }
+ })
+ this.dialog.open(ShowCodecDialogComponent, {
+ data: {
+ usedVideoCodec: this.usedVideoCodec
+ },
+ width: '450px'
+ });
+ }
}
diff --git a/openvidu-testapp/src/app/services/openvidu-rest.service.ts b/openvidu-testapp/src/app/services/openvidu-rest.service.ts
index 50bf7d54..12668741 100644
--- a/openvidu-testapp/src/app/services/openvidu-rest.service.ts
+++ b/openvidu-testapp/src/app/services/openvidu-rest.service.ts
@@ -29,7 +29,7 @@ export class OpenviduRestService {
});
}
- getToken(openviduURL: string, openviduSecret: string, sessionId: string, role: string, serverData: string): Promise {
+ getToken(sessionId: string, role: string, serverData: string): Promise {
const session: SessionAPI = this.sessionIdSession.get(sessionId);
const OVRole: OpenViduRoleAPI = OpenViduRoleAPI[role];
diff --git a/pom.xml b/pom.xml
index 14bcd715..837e4e5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,39 +43,40 @@
6.15.0
- 2.2.4.RELEASE
- 4.12
- 5.6.0
- 1.6.0
+ 2.3.5.RELEASE
+ 4.13.1
+ 5.7.0
+ 1.7.0
3.141.59
- 2.23.4
- 2.0.5
+ 3.6.0
+ 2.0.9
2.2
- 4.5.11
- 3.1.0
- 3.10
- 3.1.5
+ 4.5.13
+ 3.1.2
+ 3.11
+ 3.2.5
1.7.30
2.8.6
1.4.9
+ 0.2.5
- 4.0.0
+ 4.2.2
- 2.15.1
+ 2.16.0
1.1.0
1.1.0
3.0.0-M3
- 1.2
+ 1.3
3.2.1
- 3.0.0-M4
+ 3.0.0-M5
1.6
1.6.8
- 1.6.0
- 3.1.1
+ 3.0.0
+ 3.2.0
- 1.8
- 1.8
+ 11
+ 11
https://github.com/OpenVidu/openvidu
git@github.com:OpenVidu/openvidu.git
@@ -91,11 +92,11 @@
UTF-8
- 1.8
- 1.8
+ 11
+ 11
-
${maven.compiler.target}
${maven.compiler.source}
@@ -121,7 +122,7 @@
-
org.apache.maven.plugins
maven-enforcer-plugin