UNPKG

@novo-learning/novo-sdk

Version:

SDK for the Novolanguage Speech Analysis API

148 lines 5.32 kB
"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