@convergence/convergence
Version:
Convergence JavaScript Client
1,475 lines (1,450 loc) • 727 kB
JavaScript
/**!
Copyright © 2016-2020 - Convergence Labs, Inc.
@version 1.0.0-rc.12
@license Licensed under the terms of the GNU Lesser General Public License version 3 (GPLv3). See https://www.gnu.org/licenses/lgpl-3.0.html
*/
import { Subject, BehaviorSubject } from 'rxjs';
import { share, filter, map, tap } from 'rxjs/operators';
import * as protobuf from 'protobufjs/light';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
var LogLevel;
(function (LogLevel) {
LogLevel["TRACE"] = "trace";
LogLevel["DEBUG"] = "debug";
LogLevel["INFO"] = "info";
LogLevel["WARN"] = "warn";
LogLevel["ERROR"] = "error";
LogLevel["SILENT"] = "silent";
})(LogLevel || (LogLevel = {}));
class Immutable {
static make(source) {
if (typeof source === "object") {
Object.freeze(source);
Object.keys(source).forEach((val, idx, array) => {
Immutable.make(val);
});
}
}
static copy(source, updates) {
const result = {};
Object.keys(source).forEach((prop, idx, array) => {
result[prop] = updates[prop] !== undefined ? updates[prop] : source[prop];
});
Object.freeze(result);
return result;
}
static getOrDefault(value, defaultValue) {
return value === undefined ? defaultValue : value;
}
static update(current, update) {
return update !== undefined ? update : current;
}
}
class LogEvent {
constructor(timestamp, logger, level, message, error) {
this.timestamp = timestamp;
this.logger = logger;
this.level = level;
this.message = message;
this.error = error;
Immutable.make(this);
}
}
class TypeChecker {
static isArray(value) {
return Array.isArray(value);
}
static isMap(value) {
return value instanceof Map;
}
static isBoolean(value) {
return typeof value === "boolean";
}
static isDate(value) {
return value instanceof Date || value.constructor.name === "Date";
}
static isError(value) {
return value instanceof Error && typeof value.message !== "undefined";
}
static isFunction(value) {
return typeof value === "function";
}
static isNull(value) {
return value === null;
}
static isNumber(value) {
return typeof value === "number" && isFinite(value);
}
static isObject(value) {
return value && typeof value === "object" && value.constructor === Object;
}
static isString(value) {
return typeof value === "string" || value instanceof String;
}
static isSymbol(value) {
return typeof value === "symbol";
}
static isRegExp(value) {
return value && typeof value === "object" && value.constructor === RegExp;
}
static isUndefined(value) {
return typeof value === "undefined";
}
static isSet(value) {
return !TypeChecker.isNull(value) && !TypeChecker.isUndefined(value);
}
static isNotSet(value) {
return !TypeChecker.isSet(value);
}
static switch(value, matcher) {
if (TypeChecker.isUndefined(matcher) || TypeChecker.isNull(matcher)) {
throw new Error("matcher must be defined.");
}
if (TypeChecker.isArray(value) && !TypeChecker.isUndefined(matcher.array)) {
matcher.array(value);
}
else if (TypeChecker.isBoolean(value) && !TypeChecker.isUndefined(matcher.boolean)) {
matcher.boolean(value);
}
else if (TypeChecker.isError(value) && !TypeChecker.isUndefined(matcher.error)) {
matcher.error(value);
}
else if (TypeChecker.isDate(value) && !TypeChecker.isUndefined(matcher.date)) {
matcher.date(value);
}
else if (TypeChecker.isFunction(value) && !TypeChecker.isUndefined(matcher.function)) {
matcher.function(value);
}
else if (TypeChecker.isNull(value) && !TypeChecker.isUndefined(matcher.null)) {
matcher.null();
}
else if (TypeChecker.isNumber(value) && !TypeChecker.isUndefined(matcher.number)) {
matcher.number(value);
}
else if (TypeChecker.isObject(value) && !TypeChecker.isUndefined(matcher.object)) {
matcher.object(value);
}
else if (TypeChecker.isRegExp(value) && !TypeChecker.isUndefined(matcher.regexp)) {
matcher.regexp(value);
}
else if (TypeChecker.isString(value) && !TypeChecker.isUndefined(matcher.string)) {
matcher.string(value);
}
else if (TypeChecker.isSymbol(value) && !TypeChecker.isUndefined(matcher.symbol)) {
matcher.symbol(value);
}
else if (TypeChecker.isUndefined(value) && !TypeChecker.isUndefined(matcher.undefined)) {
matcher.undefined();
}
else {
const customRule = TypeChecker.isArray(matcher.custom) ?
matcher.custom.find(rule => {
return TypeChecker.isFunction(rule.test) &&
TypeChecker.isFunction(rule.callback) &&
rule.test(value);
}) :
undefined;
if (!TypeChecker.isUndefined(customRule)) {
customRule.callback(value);
}
else if (!TypeChecker.isUndefined(matcher.default)) {
matcher.default(value);
}
}
}
}
const LogLevelPriority = {
SILENT: -1,
TRACE: 0,
DEBUG: 1,
INFO: 2,
WARN: 3,
ERROR: 4
};
class Logger {
constructor(id, level, logWriters) {
this._id = id;
this._logWriters = logWriters;
this.setLevel(level);
}
getId() {
return this._id;
}
getLevel() {
return this._level;
}
setLevel(logLevel) {
this._level = logLevel;
this._levelPriority = LogLevelPriority[this._level.toUpperCase()];
}
trace(message) {
if (LogLevelPriority.TRACE >= this._levelPriority) {
const event = new LogEvent(new Date(), this._id, LogLevel.TRACE, this._resolveLogMessage(message));
this._log(event);
}
}
debug(message) {
if (LogLevelPriority.DEBUG >= this._levelPriority) {
const event = new LogEvent(new Date(), this._id, LogLevel.DEBUG, this._resolveLogMessage(message));
this._log(event);
}
}
info(message) {
if (LogLevelPriority.INFO >= this._levelPriority) {
const event = new LogEvent(new Date(), this._id, LogLevel.INFO, this._resolveLogMessage(message));
this._log(event);
}
}
warn(message) {
if (LogLevelPriority.WARN >= this._levelPriority) {
const event = new LogEvent(new Date(), this._id, LogLevel.WARN, this._resolveLogMessage(message));
this._log(event);
}
}
error(message, e) {
if (LogLevelPriority.ERROR >= this._levelPriority) {
const event = new LogEvent(new Date(), this._id, LogLevel.ERROR, this._resolveLogMessage(message), e);
this._log(event);
}
}
_log(event) {
if (this._levelPriority !== LogLevelPriority.SILENT) {
this._logWriters.forEach(writer => writer.writeLog(event));
}
}
_resolveLogMessage(message) {
if (TypeChecker.isFunction(message)) {
return message();
}
else {
return message;
}
}
}
function mapObjectValues(obj, mapFunc) {
return Object.keys(obj).reduce((newObj, value) => {
newObj[value] = mapFunc(obj[value]);
return newObj;
}, {});
}
function objectForEach(obj, callback) {
return Object.keys(obj).forEach(key => {
callback(key, obj[key]);
});
}
function deepClone(from) {
const type = typeof from;
if (from === null || from === undefined || type === "string" || type === "number" || type === "boolean") {
return from;
}
if (from instanceof Date) {
return new Date(from.getTime());
}
if (Array.isArray(from)) {
return from.map(e => deepClone(e));
}
if (from instanceof Map) {
const result = new Map();
from.forEach((v, k) => {
result.set(k, deepClone(v));
});
return result;
}
if (from instanceof Set) {
const result = new Set();
from.forEach(v => result.add(deepClone(v)));
return result;
}
if (from.constructor === Object) {
const result = {};
Object.keys(from).forEach(key => {
result[key] = deepClone(from[key]);
});
return result;
}
const name = from.constructor.name || from.constructor.toString();
throw new Error("Can not clone unknown type: " + name);
}
class Validation {
static isSet(value) {
return !Validation.isNotSet(value);
}
static isNotSet(value) {
return value === undefined || value === null;
}
static nonEmptyString(value) {
return typeof value === "string" && value.length > 0;
}
static assertNonEmptyString(value, name) {
if (name === undefined) {
name = "value";
}
if (!Validation.nonEmptyString(value)) {
throw new Error(name + " must be a non-empty string: " + typeof value);
}
}
static assertString(value, name) {
if (typeof value !== "string") {
throw new Error(`${Validation.getValueName(name)} must be a string: ${typeof value}`);
}
}
static assertValidStringIndex(index, str, inclusiveEnd, name) {
Validation.assertValidIndex(index, 0, inclusiveEnd ? str.length + 1 : str.length, name);
}
static assertNumber(value, name) {
if (typeof value !== "number") {
throw new Error(`${Validation.getValueName(name)} must be a number: ${typeof value}`);
}
}
static assertArray(value, name) {
if (!Array.isArray(value)) {
throw new Error(`${Validation.getValueName(name)} must be an array: ${typeof value}`);
}
}
static assertNonEmptyArray(value, name) {
Validation.assertArray(value, name);
if (value.length === 0) {
throw new Error(`${Validation.getValueName(name)} must be a non-empty array: ${typeof value}`);
}
}
static assertValidArrayIndex(index, array, name) {
Validation.assertValidIndex(index, 0, array.length, name);
}
static assertValidIndex(index, lower, upper, name) {
Validation.assertNumber(index, name);
if (index < lower || index >= upper) {
name = Validation.getValueName(name);
throw new Error(`Index out of bounds. ${name} must be > 0 and <= ${upper}: ${index}`);
}
}
static assertBoolean(value, name) {
if (typeof value !== "boolean") {
throw new Error(`${Validation.getValueName(name)} must be a boolean but was: ${typeof value}`);
}
}
static assertDate(value, name) {
if (!TypeChecker.isDate(value)) {
throw new Error(`${Validation.getValueName(name)} must be a Date but was: ${value.constructor.name}`);
}
}
static getValueName(name, defaultValue) {
return name || defaultValue || "value";
}
}
class LoggingConfig {
constructor(config) {
this._loggers = new Map();
if (TypeChecker.isSet(config.loggers)) {
objectForEach(config.loggers, (id, loggerConfig) => {
if (!Validation.nonEmptyString(id)) {
throw new Error("A logger's id must be a non-empty string");
}
this._loggers.set(id, this._processLoggerConfig(loggerConfig));
});
}
this._loggers.set(LoggingConfig.ROOT_LOGGER_ID, this._processLoggerConfig(config.root));
}
resolveLoggerConfig(loggerId) {
let id = loggerId;
let logger = this._loggers.get(id);
while (logger === undefined && id !== "") {
const dot = id.lastIndexOf(".");
id = id.substring(0, dot);
logger = this._loggers.get(id);
}
return logger !== undefined ?
logger :
this._loggers.get(LoggingConfig.ROOT_LOGGER_ID);
}
_processLoggerConfig(config) {
if (TypeChecker.isString(config)) {
return { level: config };
}
else {
return config;
}
}
}
LoggingConfig.ROOT_LOGGER_ID = "";
class PatternLogWriter {
constructor(pattern) {
this._pattern = pattern || PatternLogWriter.DEFAULT_PATTERN;
}
_formatMessage(event) {
const level = this._pad(event.level.toUpperCase(), 5, " ", false);
const time = this._formatTime(event);
return `${level} ${time} ${event.message}`;
}
_formatTime(event) {
const t = event.timestamp;
return this._pad(t.getHours(), 2, "0") +
":" +
this._pad(t.getMinutes(), 2, "0") +
":" +
this._pad(t.getSeconds(), 2, "0") +
"." +
this._pad(t.getMilliseconds(), 3, "0");
}
_pad(value, minLen, char, left = true) {
let str = String(value);
while (str.length < minLen) {
str = left ? char + str : str + char;
}
return str;
}
}
PatternLogWriter.FIELDS = {
LOG_NAME: "%l",
MESSAGE: "%m",
DATE_TIME: "%t",
LOG_LEVEL: "%p"
};
PatternLogWriter.DEFAULT_PATTERN = "%t %p %m";
class ConsoleLogWriter extends PatternLogWriter {
constructor(pattern) {
super(pattern);
}
writeLog(event) {
switch (event.level) {
case LogLevel.TRACE:
console.debug(this._formatMessage(event));
break;
case LogLevel.DEBUG:
console.debug(this._formatMessage(event));
break;
case LogLevel.INFO:
console.info(this._formatMessage(event));
break;
case LogLevel.WARN:
console.warn(this._formatMessage(event));
break;
case LogLevel.ERROR:
if (event.error) {
console.error(this._formatMessage(event) + "\n", event.error);
}
else {
console.error(this._formatMessage(event));
}
break;
}
}
}
const DEFAULT_CONFIG = {
root: {
level: LogLevel.WARN
}
};
class ConvergenceLogging {
constructor(config) {
this.configure(config || {});
this._writer = new ConsoleLogWriter("");
}
configure(config) {
const defaulted = Object.assign(Object.assign({}, DEFAULT_CONFIG), config);
this._config = new LoggingConfig(defaulted);
this._loggers = new Map();
}
root() {
return this.logger();
}
logger(id) {
if (id === null || id === undefined) {
id = LoggingConfig.ROOT_LOGGER_ID;
}
if (!this._loggers.has(id)) {
const config = this._config.resolveLoggerConfig(id);
this._loggers.set(id, new Logger(id, config.level, [this._writer]));
}
return this._loggers.get(id);
}
}
const Logging = new ConvergenceLogging();
class HeartbeatHelper {
constructor(handler, pingInterval, pongTimeout) {
this._sendPing = () => {
this._handler.sendPing();
this._schedulePongTimeout();
};
this._onTimeout = () => {
this._logger.trace(() => "A pong timeout occurred");
this._handler.onTimeout();
};
this._handler = handler;
this._pingInterval = pingInterval;
this._pongTimeout = pongTimeout;
this._started = false;
this._logger = Logging.logger("heartbeat");
}
messageReceived() {
if (this._started) {
this._cancelPongTimeout();
this._restartPingTimeout();
}
}
start() {
if (this._handler == null) {
throw new Error("Can't start the HeartbeatManager unless the callback is set.");
}
this._logger.trace(() => "HeartbeatHelper started with Ping Interval " + this._pingInterval +
" and Pong Timeout " + this._pongTimeout);
this._started = true;
this.messageReceived();
}
stop() {
this._started = false;
this._stopPingTimer();
this._cancelPongTimeout();
this._logger.trace(() => "HeartbeatHelper stopped.");
}
get started() {
return this._started;
}
dispose() {
this.stop();
}
_schedulePongTimeout() {
this._cancelPongTimeout();
const timeoutMillis = this._pongTimeout * 1000;
this._timeoutFuture = setTimeout(this._onTimeout, timeoutMillis);
}
_cancelPongTimeout() {
if (this._timeoutFuture !== null) {
clearTimeout(this._timeoutFuture);
this._timeoutFuture = null;
}
}
_stopPingTimer() {
if (this._pingFuture !== null) {
clearTimeout(this._pingFuture);
this._pingFuture = null;
}
}
_restartPingTimeout() {
this._stopPingTimer();
const pingIntervalMillis = this._pingInterval * 1000;
this._pingFuture = setTimeout(this._sendPing, pingIntervalMillis);
}
}
class ConvergenceError extends Error {
constructor(message, code, details) {
super(message);
this._code = code;
this._details = details || {};
this.name = "ConvergenceError";
Object.setPrototypeOf(this, ConvergenceError.prototype);
}
get code() {
return this._code;
}
get details() {
return this._details;
}
}
class ConvergenceEventEmitter {
constructor() {
this._defaultSubject = new Subject();
this._observable = this._defaultSubject.asObservable().pipe(share());
this._listeners = new Map();
}
static _resolveEventKey(event) {
if (typeof event === "string") {
return event.toLowerCase();
}
else {
throw new Error("Event names must be strings: " + typeof event);
}
}
addListener(event, listener) {
if (typeof listener !== "function") {
throw new TypeError("Listeners must be functions");
}
event = ConvergenceEventEmitter._resolveEventKey(event);
let listeners = this._listeners.get(event);
if (listeners === undefined) {
listeners = [];
this._listeners.set(event, listeners);
}
else if (listeners.find(r => r.listener === listener) !== undefined) {
return this;
}
const subscription = this._observable
.pipe(filter((e) => e.name.toLowerCase() === event))
.subscribe((e) => {
listener(e);
});
listeners.push({ listener, subscription });
return this;
}
on(event, listener) {
return this.addListener(event, listener);
}
once(event, listener) {
const wrapper = (e) => {
this.removeListener(event, wrapper);
listener(e);
};
return this.addListener(event, wrapper);
}
removeAllListeners() {
Array
.from(this._listeners.keys())
.forEach(event => this.removeListeners(event));
return this;
}
removeListeners(event) {
event = ConvergenceEventEmitter._resolveEventKey(event);
if (this._listeners.has(event)) {
const registrations = this._listeners.get(event);
registrations.forEach(r => r.subscription.unsubscribe());
this._listeners.delete(event);
}
return this;
}
removeListener(event, listener) {
event = ConvergenceEventEmitter._resolveEventKey(event);
if (this._listeners.has(event)) {
const listeners = this._listeners.get(event);
const index = listeners.findIndex(r => r.listener === listener);
if (index !== -1) {
const r = listeners[index];
listeners.splice(index, 1);
r.subscription.unsubscribe();
}
if (listeners.length === 0) {
this._listeners.delete(event);
}
}
return this;
}
off(event, listener) {
return this.removeListener(event, listener);
}
events() {
return this._observable;
}
_emitFrom(observable) {
return observable.subscribe((value) => {
this._defaultSubject.next(value);
}, (error) => {
this._defaultSubject.error(error);
});
}
_emitEvent(value) {
if (Validation.isNotSet(value.name)) {
throw new ConvergenceError("An event must have a name.");
}
this._defaultSubject.next(value);
}
_completeEventStream() {
this._defaultSubject.complete();
}
}
class ConvergenceServerError extends Error {
constructor(m, code, details) {
super(m);
this._code = code;
this._details = details;
Object.setPrototypeOf(this, ConvergenceServerError.prototype);
}
get code() {
return this._code;
}
get details() {
return this._details;
}
}
class CancellationToken {
constructor() {
this._callback = null;
this._canceled = false;
}
static create() {
return new CancellationToken();
}
cancel() {
if (this._callback === null) {
throw new Error("The cancellation token must be bound before calling cancel.");
}
if (this._canceled) {
throw new Error("The cancellation token has already been cancelled.");
}
this._callback();
this._canceled = true;
}
isBound() {
return this._callback !== null;
}
_bind(callback) {
if (typeof callback !== "function") {
throw new Error("callback must be a function");
}
if (this._callback !== null) {
throw new Error("The cancellation token was already bound");
}
this._callback = callback;
}
}
class PagedData {
constructor(data, offset, totalResults) {
this.data = data;
this.offset = offset;
this.totalResults = totalResults;
Object.freeze(this);
Object.freeze(data);
}
}
class AbstractDeferred {
constructor() {
this._rejected = false;
this._resolved = false;
}
isPending() {
return !this._resolved && !this._rejected;
}
isRejected() {
return this._rejected;
}
isResolved() {
return this._resolved;
}
resolve(value) {
this._rejected = false;
this._resolved = true;
}
reject(error) {
this._rejected = true;
this._resolved = false;
}
resolveFromPromise(p) {
p.then((r) => this.resolve(r)).catch((e) => this.reject(e));
}
}
class Deferred extends AbstractDeferred {
constructor() {
super();
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
}
resolve(value) {
super.resolve(value);
this._resolve(value);
}
reject(error) {
super.reject(error);
this._reject(error);
}
promise() {
return this._promise;
}
}
class ConvergenceSocket extends ConvergenceEventEmitter {
constructor(url, webSocketClass, webSocketFactory) {
super();
this._connectionError = null;
this._logger = Logging.logger("socket");
let tmp = url;
tmp = tmp.replace(/https:/i, "wss:");
tmp = tmp.replace(/http:/i, "ws:");
this._url = tmp;
this._socket = null;
this._webSocketClass = webSocketClass !== null ? webSocketClass : WebSocket;
this._webSocketFactory = webSocketFactory !== null ? webSocketFactory : (u) => new this._webSocketClass(u);
}
static _detachFromSocket(socket) {
socket.onmessage = undefined;
socket.onopen = undefined;
socket.onerror = undefined;
socket.onclose = undefined;
}
get url() {
return this._url;
}
open() {
this._openDeferred = new Deferred();
if (this._socket && this._socket.readyState === this._webSocketClass.CONNECTING) {
throw new Error("Socket already in the process of opening.");
}
else if (this._socket && this._socket.readyState === this._webSocketClass.OPEN) {
throw new Error("Can not call connect on a socket that is already connected.");
}
else if (this._socket && this._socket.readyState === this._webSocketClass.CLOSING) {
throw new Error("Can not call connect on a socket that is in the process of closing.");
}
else {
this._socket = this._webSocketFactory(this._url);
this._socket.binaryType = "arraybuffer";
this._attachToSocket(this._socket);
}
return this._openDeferred.promise();
}
close() {
return this._doClose(true);
}
terminate(reason) {
return this._doClose(false, reason);
}
isOpen() {
return this._socket !== null && this._socket.readyState === this._webSocketClass.OPEN;
}
isConnecting() {
return this._socket !== null && this._socket.readyState === this._webSocketClass.CONNECTING;
}
isClosed() {
return this._socket === null || this._socket.readyState === this._webSocketClass.CLOSED;
}
send(message) {
if (!this.isOpen()) {
throw new Error("Can't send messages because the WebSocket is not open.");
}
this._socket.send(message);
}
_doClose(clean, reason) {
if (!this._socket || this._socket.readyState === this._webSocketClass.CLOSED) {
this._logger.trace("Attempted to close a WebSocket that is in the CLOSED state.");
return Promise.reject(new Error("Can not call close on a WebSocket in the CLOSED state."));
}
else if (this._socket.readyState === this._webSocketClass.CLOSING) {
this._logger.trace("Attempted to close a WebSocket that is in the CLOSING state.");
return Promise.reject(new Error("Can not call close on a WebSocket in the CLOSING state."));
}
else {
if (this._socket.readyState === this._webSocketClass.CONNECTING) {
this._logger.trace("Closing a connecting WebSocket.");
}
else {
this._logger.trace("Closing an open WebSocket.");
}
this._closeDeferred = new Deferred();
if (clean) {
this._logger.debug("Closing WebSocket normally.");
this._socket.close(1000, reason || "The client closed the connection");
}
else {
this._logger.debug("Closing WebSocket abnormally: " + reason);
this._socket.close(4006, reason);
}
if (this._openDeferred !== null) {
const tmp = this._openDeferred;
this._openDeferred = null;
tmp.reject(new Error("Web Socket connection closed while opening."));
}
return this._closeDeferred.promise();
}
}
_attachToSocket(socket) {
socket.onmessage = (evt) => {
try {
if (evt.data instanceof ArrayBuffer) {
const buffer = new Uint8Array(evt.data);
this._emitEvent({
name: ConvergenceSocket.Events.MESSAGE,
message: buffer
});
}
else {
throw new ConvergenceError("Convergence protocol does not accept text frames: " + evt.data);
}
}
catch (e) {
this._emitEvent({
name: ConvergenceSocket.Events.ERROR,
error: e
});
this._logger.error("Error handling web socket frame", e);
}
};
socket.onopen = (_) => {
if (this._openDeferred) {
this._logger.debug("WebSocket connection opened");
try {
this._openDeferred.resolve();
}
catch (e) {
this._logger.error("Error resolving WebSocket Open Promise.", e);
}
this._openDeferred = null;
}
else {
const event = {
name: ConvergenceSocket.Events.ERROR,
error: new ConvergenceError("Received onOpen event while in state: " + this._socket.readyState)
};
this._emitEvent(event);
}
};
socket.onerror = (evt) => {
this._logger.debug("WebSocket Error: " + evt.message);
if (this._socket === undefined || this._socket.readyState === this._webSocketClass.CONNECTING) {
try {
const event = {
name: ConvergenceSocket.Events.ERROR,
error: new ConvergenceError("WebSocket error: " + evt.message)
};
this._emitEvent(event);
}
catch (e) {
this._logger.error("Error handling WebSocket error.", e);
}
}
else {
this._connectionError = evt.error;
}
};
socket.onclose = (evt) => {
this._logger.trace(() => `WebSocket close event: {code: ${evt.code}, reason: "${evt.reason}"}`);
ConvergenceSocket._detachFromSocket(socket);
this._socket = null;
try {
if (this._openDeferred) {
this._logger.debug(() => `Web Socket connection failed: {code: ${evt.code}, reason: "${evt.reason}"}`);
this._openDeferred.reject(new Error(this._connectionError));
this._openDeferred = null;
}
if (this._closeDeferred) {
const event = {
name: ConvergenceSocket.Events.CLOSE,
reason: "close requested by client"
};
this._emitEvent(event);
this._closeDeferred.resolve();
}
else {
this._logger.debug(() => `Web Socket connection unexpectedly closed: {code: ${evt.code}, reason: "${evt.reason}"}`);
const event = {
name: ConvergenceSocket.Events.CLOSE,
reason: "unexpected Web Socket closure."
};
this._emitEvent(event);
}
}
catch (e) {
this._logger.error("Error handling web socket close event.", e);
}
};
}
}
ConvergenceSocket.Events = {
MESSAGE: "message",
ERROR: "error",
CLOSE: "close"
};
var nested = {
com: {
nested: {
convergencelabs: {
nested: {
convergence: {
nested: {
proto: {
nested: {
ConvergenceMessage: {
oneofs: {
body: {
oneof: [
"ok",
"error",
"ping",
"pong",
"connectionRequest",
"connectionResponse",
"serverTimeRequest",
"serverTimeResponse",
"openRealTimeModelRequest",
"openRealTimeModelResponse",
"closeRealTimeModelRequest",
"closeRealTimeModelResponse",
"createRealTimeModelRequest",
"createRealTimeModelResponse",
"deleteRealtimeModelRequest",
"deleteRealtimeModelResponse",
"forceCloseRealTimeModel",
"remoteClientOpenedModel",
"remoteClientClosedModel",
"modelAutoCreateConfigRequest",
"modelAutoCreateConfigResponse",
"remoteOperation",
"operationSubmission",
"operationAck",
"shareReference",
"setReference",
"clearReference",
"unshareReference",
"referenceShared",
"referenceSet",
"referenceCleared",
"referenceUnshared",
"modelsQueryRequest",
"modelsQueryResponse",
"getModelPermissionsRequest",
"getModelPermissionsResponse",
"setModelPermissionsRequest",
"setModelPermissionsResponse",
"modelPermissionsChanged",
"modelResyncRequest",
"modelResyncResponse",
"modelResyncClientComplete",
"modelResyncServerComplete",
"remoteClientResyncStarted",
"remoteClientResyncCompleted",
"modelOfflineSubscriptionChange",
"modelOfflineUpdated",
"historicalDataRequest",
"historicalDataResponse",
"historicalOperationsRequest",
"historicalOperationsResponse",
"modelGetVersionAtTimeRequest",
"modelGetVersionAtTimeResponse",
"identityCacheUpdate",
"usersGetRequest",
"userSearchRequest",
"userListResponse",
"userGroupsRequest",
"userGroupsResponse",
"userGroupsForUsersRequest",
"userGroupsForUsersResponse",
"activityParticipantsRequest",
"activityParticipantsResponse",
"activityCreateRequest",
"activityDeleteRequest",
"activityJoinRequest",
"activityJoinResponse",
"activityLeaveRequest",
"activitySessionJoined",
"activitySessionLeft",
"activityUpdateState",
"activityStateUpdated",
"activityDeleted",
"activityForceLeave",
"presenceSetState",
"presenceRemoveState",
"presenceClearState",
"presenceStateSet",
"presenceStateRemoved",
"presenceStateCleared",
"presenceAvailabilityChanged",
"presenceRequest",
"presenceResponse",
"presenceSubscribeRequest",
"presenceSubscribeResponse",
"presenceUnsubscribe",
"createChatRequest",
"createChatResponse",
"removeChatRequest",
"removeChatResponse",
"chatRemoved",
"getChatsRequest",
"getChatsResponse",
"chatsExistRequest",
"chatsExistResponse",
"getDirectChatsRequest",
"getDirectChatsResponse",
"getJoinedChatsRequest",
"getJoinedChatsResponse",
"chatsSearchRequest",
"chatsSearchResponse",
"joinChatRequest",
"joinChatResponse",
"userJoinedChat",
"leaveChatRequest",
"leaveChatResponse",
"userLeftChat",
"addUserToChatRequest",
"addUserToChatResponse",
"userAddedToChat",
"removeUserFromChatRequest",
"removeUserFromChatResponse",
"userRemovedFromChat",
"setChatNameRequest",
"setChatNameResponse",
"chatNameChanged",
"setChatTopicRequest",
"setChatTopicResponse",
"chatTopicChanged",
"markChatEventsSeenRequest",
"markChatEventsSeenResponse",
"chatEventsMarkedSeen",
"publishChatMessageRequest",
"publishChatMessageResponse",
"remoteChatMessage",
"getChatHistoryRequest",
"getChatHistoryResponse",
"resolvePermissionsForConnectedSessionRequest",
"resolvePermissionsForConnectedSessionResponse",
"addPermissionsRequest",
"removePermissionsRequest",
"setPermissionsRequest",
"getPermissionsRequest",
"getPermissionsResponse"
]
}
},
fields: {
requestId: {
type: "google.protobuf.Int32Value",
id: 1
},
responseId: {
type: "google.protobuf.Int32Value",
id: 2
},
ok: {
type: "core.OkResponse",
id: 3
},
error: {
type: "core.ErrorMessage",
id: 4
},
ping: {
type: "core.PingMessage",
id: 5
},
pong: {
type: "core.PongMessage",
id: 6
},
connectionRequest: {
type: "core.ConnectionRequestMessage",
id: 7
},
connectionResponse: {
type: "core.ConnectionResponseMessage",
id: 8
},
serverTimeRequest: {
type: "core.GetServerTimeRequestMessage",
id: 20
},
serverTimeResponse: {
type: "core.GetServerTimeResponseMessage",
id: 21
},
openRealTimeModelRequest: {
type: "model.OpenRealtimeModelRequestMessage",
id: 100
},
openRealTimeModelResponse: {
type: "model.OpenRealtimeModelResponseMessage",
id: 101
},
closeRealTimeModelRequest: {
type: "model.CloseRealtimeModelRequestMessage",
id: 102
},
closeRealTimeModelResponse: {
type: "model.CloseRealTimeModelResponseMessage",
id: 103
},
createRealTimeModelRequest: {
type: "model.CreateRealtimeModelRequestMessage",
id: 104
},
createRealTimeModelResponse: {
type: "model.CreateRealtimeModelResponseMessage",
id: 105
},
deleteRealtimeModelRequest: {
type: "model.DeleteRealtimeModelRequestMessage",
id: 106
},
deleteRealtimeModelResponse: {
type: "model.DeleteRealtimeModelResponseMessage",
id: 107
},
forceCloseRealTimeModel: {
type: "model.ModelForceCloseMessage",
id: 108
},
remoteClientOpenedModel: {
type: "model.RemoteClientOpenedMessage",
id: 109
},
remoteClientClosedModel: {
type: "model.RemoteClientClosedMessage",
id: 110
},
modelAutoCreateConfigRequest: {
type: "model.AutoCreateModelConfigRequestMessage",
id: 111
},
modelAutoCreateConfigResponse: {
type: "model.AutoCreateModelConfigResponseMessage",
id: 112
},
remoteOperation: {
type: "model.RemoteOperationMessage",
id: 113
},
operationSubmission: {
type: "model.OperationSubmissionMessage",
id: 114
},
operationAck: {
type: "model.OperationAcknowledgementMessage",
id: 115
},
shareReference: {
type: "model.ShareReferenceMessage",
id: 116
},
setReference: {
type: "model.SetReferenceMessage",
id: 117
},
clearReference: {
type: "model.ClearReferenceMessage",
id: 118
},
unshareReference: {
type: "model.UnshareReferenceMessage",
id: 119
},
referenceShared: {
type: "model.RemoteReferenceSharedMessage",
id: 120
},
referenceSet: {
type: "model.RemoteReferenceSetMessage",
id: 121
},
referenceCleared: {
type: "model.RemoteReferenceClearedMessage",
id: 122
},
referenceUnshared: {
type: "model.RemoteReferenceUnsharedMessage",
id: 123
},
modelsQueryRequest: {
type: "model.ModelsQueryRequestMessage",
id: 124
},
modelsQueryResponse: {
type: "model.ModelsQueryResponseMessage",
id: 125
},
getModelPermissionsRequest: {
type: "model.GetModelPermissionsRequestMessage",
id: 126
},
getModelPermissionsResponse: {
type: "model.GetModelPermissionsResponseMessage",
id: 127
},
setModelPermissionsRequest: {
type: "model.SetModelPermissionsRequestMessage",
id: 128
},
setModelPermissionsResponse: {
type: "model.SetModelPermissionsResponseMessage",
id: 129
},
modelPermissionsChanged: {
type: "model.ModelPermissionsChangedMessage",
id: 130
},
modelResyncRequest: {
type: "model.ModelResyncRequestMessage",
id: 131
},
modelResyncResponse: {
type: "model.ModelResyncResponseMessage",
id: 132
},
modelResyncClientComplete: {
type: "model.ModelResyncClientCompleteMessage",
id: 133
},
modelResyncServerComplete: {
type: "model.ModelResyncServerCompleteMessage",
id: 134
},
remoteClientResyncStarted: {
type: "model.RemoteClientResyncStartedMessage",
id: 135
},
remoteClientResyncCompleted: {
type: "model.RemoteClientResyncCompletedMessage",
id: 136
},
modelOfflineSubscriptionChange: {
type: "model.ModelOfflineSubscriptionChangeRequestMessage",
id: 137
},
modelOfflineUpdated: {
type: "model.OfflineModelUpdatedMessage",
id: 138
},
historicalDataRequest: {
type: "model.HistoricalDataRequestMessage",
id: 139
},
historicalDataResponse: {
type: "model.HistoricalDataResponseMessage",
id: 140
},
historicalOperationsRequest: {
type: "model.HistoricalOperationRequestMessage",
id: 141
},
historicalOperationsResponse: {
type: "model.HistoricalOperationsResponseMessage",
id: 142
},
modelGetVersionAtTimeRequest: {
type: "model.ModelGetVersionAtTimeRequestMessage",
id: 143
},
modelGetVersionAtTimeResponse: {
type: "model.ModelGetVersionAtTimeResponseMessage",
id: 144
},
identityCacheUpdate: {
type: "identity.IdentityCacheUpdateMessage",
id: 200
},
usersGetRequest: {
type: "identity.GetUsersRequestMessage",
id: 201
},
userSearchRequest: {
type: "identity.SearchUsersRequestMessage",
id: 202
},
userListResponse: {
type: "identity.UserListMessage",
id: 203
},
userGroupsRequest: {
type: "identity.UserGroupsRequestMessage",
id: 204
},
userGroupsResponse: {
type: "identity.UserGroupsResponseMessage",
id: 205
},
userGroupsForUsersRequest: {
type: "identity.UserGroupsForUsersRequestMessage",
id: 206
},
userGroupsForUsersResponse: {
type: "identity.UserGroupsForUsersResponseMessage",
id: 207
},
activityParticipantsRequest: {
type: "activity.ActivityParticipantsRequestMessage",
id: 300
},
activityParticipantsResponse: {
type: "activity.ActivityParticipantsResponseMessage",
id: 301
},
activityCreateRequest: {
type: "activity.ActivityCreateRequestMessage",
id: 302
},
activityDeleteRequest: {
type: "activity.ActivityDeleteRequestMessage",
id: 304
},
activityJoinRequest: {
type: "activity.ActivityJoinRequestMessage",
id: 306
},
activityJoinResponse: {
type: "activity.ActivityJoinResponseMessage",
id: 307
},
activityLeaveRequest: {
type: "activity.ActivityLeaveRequestMessage",
id: 308
},
activitySessionJoined: {
type: "activity.ActivitySessionJoinedMessage",
id: 310
},
activitySessionLeft: {
type: "activity.ActivitySessionLeftMessage",
id: 311
},
activityUpdateState: {
type: "activity.ActivityUpdateStateMessage",
id: 312
},
activityStateUpdated: {
type: "activity.ActivityStateUpdatedMessage",
id: 313
},
activityDeleted: {
type: "activity.ActivityDeletedMessage",
id: 314
},
activityForceLeave: {
type: "activity.ActivityForceLeaveMessage",
id: 315
},
presenceSetState: {
type: "presence.PresenceSetStateMessage",
id: 400
},
presenceRemoveState: {
type: "presence.PresenceRemoveStateMessage",
id: 401
},
presenceClearState: {
type: "presence.PresenceClearStateMessage",
id: 402
},
presenceStateSet: {
type: "presence.PresenceStateSetMessage",
id: 403
},
presenceStateRemoved: {
type: "presence.PresenceStateRemovedMessage",
id: 404
},
presenceStateCleared: {
type: "presence.PresenceStateClearedMessage",
id: 405
},
presenceAvailabilityChanged: {
type: "presence.PresenceAvailabilityChangedMessage",
id: 406
},
presenceRequest: {
type: "presence.PresenceRequestMessage",
id: 407
},
presenceResponse: {
type: "presence.PresenceResponseMessage",
id: 408
},
presenceSubscribeRequest: {
type: "presence.SubscribePresenceRequestMessage",
id: 409
},
presenceSubscribeResponse: {
type: "presence.SubscribePresenceResponseMessage",
id