@novo-learning/novo-sdk
Version:
SDK for the Novolanguage Speech Analysis API
148 lines • 5.32 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectionMonitor = void 0;
const connection_quality_change_event_1 = require("../events/connection-quality-change.event");
const connection_state_change_event_1 = require("../events/connection-state-change.event");
const timeout_promise_1 = require("../utils/timeout.promise");
const CLOSED_BY_CLIENT_EVENT_CODE = 4000;
const CHECK_CONNECTION_INTERVAL = 20000;
const PING_TIMEOUT = 5000;
const LOW_CONNECTION_QUALITY_LIMIT = 1000;
const HIGH_CONNECTION_QUALITY_LIMIT = 200;
class ConnectionMonitor {
constructor(eventBus, messageQueue, websocket) {
this.eventBus = eventBus;
this.messageQueue = messageQueue;
this.websocket = websocket;
this.internalConnQuality = 'medium';
this.internalState = 'initial';
this.websocket.onopen = () => {
this.state = 'open';
void this.checkConnection(true);
};
this.websocket.onclose = (event) => {
if (this.state === 'open' && event.code !== CLOSED_BY_CLIENT_EVENT_CODE) {
console.info('Websocket closed');
if (event.preventDefault) {
event.preventDefault();
}
this.lostConnection({
reason: 'Websocket closed',
closeEvent: {
reason: event.reason,
code: event.code,
},
});
}
this.state = 'closed';
};
this.websocket.onerror = (error) => {
if (this.state === 'open') {
console.error('Websocket error:', JSON.stringify(error));
this.lostConnection({
error,
reason: 'Websocket error',
});
}
};
}
get connectionQuality() {
return this.internalConnQuality;
}
set connectionQuality(quality) {
if (this.internalConnQuality === quality) {
return;
}
this.eventBus.dispatch(connection_quality_change_event_1.ConnectionQualityChangedEvent.type, {
quality,
previous: this.internalConnQuality,
});
this.internalConnQuality = quality;
}
get state() {
return this.internalState;
}
set state(state) {
if (this.state === state) {
return;
}
this.eventBus.dispatch(connection_state_change_event_1.ConnectionStateChangedEvent.type, {
previous: this.internalState,
state,
});
this.internalState = state;
}
async checkConnection(force = false) {
if (this.state !== 'open') {
return;
}
if (this.pollingPromise && !force) {
return;
}
if (this.pollingTimer) {
clearTimeout(this.pollingTimer);
}
const promise = this.ping();
this.pollingPromise = promise;
this.connectionQuality = await this.getConnectionQuality(promise);
if (this.state === 'open') {
const isAlive = await (0, timeout_promise_1.promiseTimeout)(PING_TIMEOUT, promise);
this.pollingPromise = undefined;
if (!isAlive) {
this.lostConnection({ reason: 'Connection check timed out' });
}
else {
this.pollingTimer = setTimeout(() => this.checkConnection(), CHECK_CONNECTION_INTERVAL);
}
}
}
lostConnection(detail) {
if (this.pollingTimer) {
clearTimeout(this.pollingTimer);
}
if (this.websocket && this.websocket.readyState <= WebSocket.OPEN) {
this.websocket.close(detail.closeEvent?.code);
}
if (this.state === 'closed') {
return;
}
this.eventBus.dispatch(connection_state_change_event_1.ConnectionStateChangedEvent.type, {
state: 'closed',
previous: this.state,
reason: detail.closeEvent?.reason ?? detail.reason,
code: detail.closeEvent?.code ?? -1,
});
this.state = 'closed';
}
getConnectionQuality(pingRequestPromise) {
return new Promise((resolve) => {
const before = new Date();
const timer = setTimeout(() => resolve('low'), LOW_CONNECTION_QUALITY_LIMIT);
pingRequestPromise
.then(() => {
clearTimeout(timer);
const duration = new Date().getTime() - before.getTime();
if (duration < HIGH_CONNECTION_QUALITY_LIMIT) {
resolve('high');
}
else if (duration > LOW_CONNECTION_QUALITY_LIMIT) {
resolve('medium');
}
})
.catch((_) => {
resolve('error');
});
});
}
ping() {
return this.messageQueue.request('ping');
}
close() {
this.lostConnection({
reason: 'Closed by client',
closeEvent: { code: CLOSED_BY_CLIENT_EVENT_CODE, reason: 'Connection closed by client' },
});
}
}
exports.ConnectionMonitor = ConnectionMonitor;
//# sourceMappingURL=monitor.js.map