@ohos-ports/genesys-cloud-streaming-client 20.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/cjs/alerting-leader.d.ts +29 -0
- package/dist/cjs/alerting-leader.js +163 -0
- package/dist/cjs/client.d.ts +84 -0
- package/dist/cjs/client.js +680 -0
- package/dist/cjs/connection-manager.d.ts +18 -0
- package/dist/cjs/connection-manager.js +120 -0
- package/dist/cjs/connection-transfer.d.ts +7 -0
- package/dist/cjs/connection-transfer.js +20 -0
- package/dist/cjs/http-client.d.ts +18 -0
- package/dist/cjs/http-client.js +230 -0
- package/dist/cjs/index.d.ts +10 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/messenger.d.ts +37 -0
- package/dist/cjs/messenger.js +54 -0
- package/dist/cjs/notifications.d.ts +94 -0
- package/dist/cjs/notifications.js +495 -0
- package/dist/cjs/ping.d.ts +21 -0
- package/dist/cjs/ping.js +60 -0
- package/dist/cjs/polyfills.d.ts +0 -0
- package/dist/cjs/polyfills.js +6 -0
- package/dist/cjs/server-monitor.d.ts +16 -0
- package/dist/cjs/server-monitor.js +44 -0
- package/dist/cjs/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/cjs/stanza-definitions/webrtc-signaling.js +113 -0
- package/dist/cjs/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/cjs/stanza-definitions/xep0051.js +28 -0
- package/dist/cjs/stats-formatter.d.ts +6 -0
- package/dist/cjs/stats-formatter.js +63 -0
- package/dist/cjs/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/cjs/types/genesys-cloud-media-session.js +353 -0
- package/dist/cjs/types/interfaces.d.ts +310 -0
- package/dist/cjs/types/interfaces.js +22 -0
- package/dist/cjs/types/libs.d.ts +32 -0
- package/dist/cjs/types/libs.js +1 -0
- package/dist/cjs/types/media-session.d.ts +76 -0
- package/dist/cjs/types/media-session.js +2 -0
- package/dist/cjs/types/named-agent.d.ts +13 -0
- package/dist/cjs/types/named-agent.js +2 -0
- package/dist/cjs/types/offline-error.d.ts +2 -0
- package/dist/cjs/types/offline-error.js +5 -0
- package/dist/cjs/types/sasl-error.d.ts +8 -0
- package/dist/cjs/types/sasl-error.js +12 -0
- package/dist/cjs/types/stanza-media-session.d.ts +56 -0
- package/dist/cjs/types/stanza-media-session.js +255 -0
- package/dist/cjs/types/streaming-client-extension.d.ts +14 -0
- package/dist/cjs/types/streaming-client-extension.js +2 -0
- package/dist/cjs/types/timeout-error.d.ts +3 -0
- package/dist/cjs/types/timeout-error.js +9 -0
- package/dist/cjs/types/user-cancelled-error.d.ts +2 -0
- package/dist/cjs/types/user-cancelled-error.js +5 -0
- package/dist/cjs/utils.d.ts +34 -0
- package/dist/cjs/utils.js +202 -0
- package/dist/cjs/webrtc.d.ts +126 -0
- package/dist/cjs/webrtc.js +891 -0
- package/dist/deploy-info.json +21 -0
- package/dist/es/alerting-leader.d.ts +29 -0
- package/dist/es/alerting-leader.js +177 -0
- package/dist/es/client.d.ts +84 -0
- package/dist/es/client.js +699 -0
- package/dist/es/connection-manager.d.ts +18 -0
- package/dist/es/connection-manager.js +124 -0
- package/dist/es/connection-transfer.d.ts +7 -0
- package/dist/es/connection-transfer.js +16 -0
- package/dist/es/http-client.d.ts +18 -0
- package/dist/es/http-client.js +194 -0
- package/dist/es/index.bundle.js +39629 -0
- package/dist/es/index.d.ts +10 -0
- package/dist/es/index.js +10 -0
- package/dist/es/messenger.d.ts +37 -0
- package/dist/es/messenger.js +62 -0
- package/dist/es/notifications.d.ts +94 -0
- package/dist/es/notifications.js +507 -0
- package/dist/es/ping.d.ts +21 -0
- package/dist/es/ping.js +68 -0
- package/dist/es/polyfills.d.ts +0 -0
- package/dist/es/polyfills.js +6 -0
- package/dist/es/server-monitor.d.ts +16 -0
- package/dist/es/server-monitor.js +41 -0
- package/dist/es/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/es/stanza-definitions/webrtc-signaling.js +110 -0
- package/dist/es/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/es/stanza-definitions/xep0051.js +25 -0
- package/dist/es/stats-formatter.d.ts +6 -0
- package/dist/es/stats-formatter.js +54 -0
- package/dist/es/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/es/types/genesys-cloud-media-session.js +379 -0
- package/dist/es/types/interfaces.d.ts +310 -0
- package/dist/es/types/interfaces.js +19 -0
- package/dist/es/types/libs.d.ts +32 -0
- package/dist/es/types/libs.js +1 -0
- package/dist/es/types/media-session.d.ts +76 -0
- package/dist/es/types/media-session.js +1 -0
- package/dist/es/types/named-agent.d.ts +13 -0
- package/dist/es/types/named-agent.js +1 -0
- package/dist/es/types/offline-error.d.ts +2 -0
- package/dist/es/types/offline-error.js +2 -0
- package/dist/es/types/sasl-error.d.ts +8 -0
- package/dist/es/types/sasl-error.js +9 -0
- package/dist/es/types/stanza-media-session.d.ts +56 -0
- package/dist/es/types/stanza-media-session.js +264 -0
- package/dist/es/types/streaming-client-extension.d.ts +14 -0
- package/dist/es/types/streaming-client-extension.js +1 -0
- package/dist/es/types/timeout-error.d.ts +3 -0
- package/dist/es/types/timeout-error.js +5 -0
- package/dist/es/types/user-cancelled-error.d.ts +2 -0
- package/dist/es/types/user-cancelled-error.js +2 -0
- package/dist/es/utils.d.ts +34 -0
- package/dist/es/utils.js +192 -0
- package/dist/es/webrtc.d.ts +126 -0
- package/dist/es/webrtc.js +917 -0
- package/dist/manifest.json +19 -0
- package/dist/npm/CHANGELOG.md +637 -0
- package/dist/npm/README.md +47 -0
- package/dist/npm/alerting-leader.d.ts +29 -0
- package/dist/npm/alerting-leader.js +163 -0
- package/dist/npm/client.d.ts +84 -0
- package/dist/npm/client.js +680 -0
- package/dist/npm/connection-manager.d.ts +18 -0
- package/dist/npm/connection-manager.js +120 -0
- package/dist/npm/connection-transfer.d.ts +7 -0
- package/dist/npm/connection-transfer.js +20 -0
- package/dist/npm/http-client.d.ts +18 -0
- package/dist/npm/http-client.js +230 -0
- package/dist/npm/index.d.ts +10 -0
- package/dist/npm/index.js +31 -0
- package/dist/npm/messenger.d.ts +37 -0
- package/dist/npm/messenger.js +54 -0
- package/dist/npm/module.js +10 -0
- package/dist/npm/notifications.d.ts +94 -0
- package/dist/npm/notifications.js +495 -0
- package/dist/npm/ping.d.ts +21 -0
- package/dist/npm/ping.js +60 -0
- package/dist/npm/polyfills.d.ts +0 -0
- package/dist/npm/polyfills.js +6 -0
- package/dist/npm/server-monitor.d.ts +16 -0
- package/dist/npm/server-monitor.js +44 -0
- package/dist/npm/stanza-definitions/webrtc-signaling.d.ts +29 -0
- package/dist/npm/stanza-definitions/webrtc-signaling.js +113 -0
- package/dist/npm/stanza-definitions/xep0051.d.ts +19 -0
- package/dist/npm/stanza-definitions/xep0051.js +28 -0
- package/dist/npm/stats-formatter.d.ts +6 -0
- package/dist/npm/stats-formatter.js +63 -0
- package/dist/npm/types/genesys-cloud-media-session.d.ts +64 -0
- package/dist/npm/types/genesys-cloud-media-session.js +353 -0
- package/dist/npm/types/interfaces.d.ts +310 -0
- package/dist/npm/types/interfaces.js +22 -0
- package/dist/npm/types/libs.d.ts +32 -0
- package/dist/npm/types/libs.js +1 -0
- package/dist/npm/types/media-session.d.ts +76 -0
- package/dist/npm/types/media-session.js +2 -0
- package/dist/npm/types/named-agent.d.ts +13 -0
- package/dist/npm/types/named-agent.js +2 -0
- package/dist/npm/types/offline-error.d.ts +2 -0
- package/dist/npm/types/offline-error.js +5 -0
- package/dist/npm/types/sasl-error.d.ts +8 -0
- package/dist/npm/types/sasl-error.js +12 -0
- package/dist/npm/types/stanza-media-session.d.ts +56 -0
- package/dist/npm/types/stanza-media-session.js +255 -0
- package/dist/npm/types/streaming-client-extension.d.ts +14 -0
- package/dist/npm/types/streaming-client-extension.js +2 -0
- package/dist/npm/types/timeout-error.d.ts +3 -0
- package/dist/npm/types/timeout-error.js +9 -0
- package/dist/npm/types/user-cancelled-error.d.ts +2 -0
- package/dist/npm/types/user-cancelled-error.js +5 -0
- package/dist/npm/utils.d.ts +34 -0
- package/dist/npm/utils.js +207 -0
- package/dist/npm/webrtc.d.ts +126 -0
- package/dist/npm/webrtc.js +893 -0
- package/dist/streaming-client.browser.js +2 -0
- package/dist/streaming-client.browser.js.LICENSE.txt +12 -0
- package/dist/v20/streaming-client.browser.js +2 -0
- package/dist/v20/streaming-client.browser.js.LICENSE.txt +12 -0
- package/dist/v20.1.0/streaming-client.browser.js +2 -0
- package/dist/v20.1.0/streaming-client.browser.js.LICENSE.txt +12 -0
- package/package.json +132 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Client = void 0;
|
|
7
|
+
const limiter_1 = require("limiter");
|
|
8
|
+
const genesys_cloud_client_logger_1 = require("genesys-cloud-client-logger");
|
|
9
|
+
require("./polyfills");
|
|
10
|
+
const alerting_leader_1 = require("./alerting-leader");
|
|
11
|
+
const notifications_1 = require("./notifications");
|
|
12
|
+
const webrtc_1 = require("./webrtc");
|
|
13
|
+
const ping_1 = require("./ping");
|
|
14
|
+
const server_monitor_1 = require("./server-monitor");
|
|
15
|
+
const utils_1 = require("./utils");
|
|
16
|
+
const http_client_1 = require("./http-client");
|
|
17
|
+
const interfaces_1 = require("./types/interfaces");
|
|
18
|
+
const events_1 = __importDefault(require("events"));
|
|
19
|
+
const connection_manager_1 = require("./connection-manager");
|
|
20
|
+
const exponential_backoff_1 = require("exponential-backoff");
|
|
21
|
+
const sasl_error_1 = __importDefault(require("./types/sasl-error"));
|
|
22
|
+
const timeout_error_1 = require("./types/timeout-error");
|
|
23
|
+
const messenger_1 = require("./messenger");
|
|
24
|
+
const connection_transfer_1 = require("./connection-transfer");
|
|
25
|
+
const user_cancelled_error_1 = __importDefault(require("./types/user-cancelled-error"));
|
|
26
|
+
const extensions = {
|
|
27
|
+
notifications: notifications_1.Notifications,
|
|
28
|
+
webrtcSessions: webrtc_1.WebrtcExtension,
|
|
29
|
+
messenger: messenger_1.MessengerExtension,
|
|
30
|
+
alertingLeader: alerting_leader_1.AlertingLeaderExtension
|
|
31
|
+
};
|
|
32
|
+
const STANZA_DISCONNECTED = 'stanzaDisconnected';
|
|
33
|
+
const NO_LONGER_SUBSCRIBED = 'notify:no_longer_subscribed';
|
|
34
|
+
const DUPLICATE_ID = 'notify:duplicate_id';
|
|
35
|
+
const MAX_CHANNEL_REUSES = 10;
|
|
36
|
+
const SESSION_STORE_KEY = 'sc_connectionData';
|
|
37
|
+
const BACKOFF_DECREASE_DELAY_MULTIPLIER = 5;
|
|
38
|
+
const INITIAL_DELAY = 2000;
|
|
39
|
+
class Client extends events_1.default {
|
|
40
|
+
constructor(options) {
|
|
41
|
+
super();
|
|
42
|
+
this.connected = false;
|
|
43
|
+
this.connecting = false;
|
|
44
|
+
this.hardReconnectRequired = true;
|
|
45
|
+
this.isGuest = false;
|
|
46
|
+
this.backgroundAssistantMode = false;
|
|
47
|
+
this.autoReconnect = true;
|
|
48
|
+
this.cancelConnectionAttempt = false;
|
|
49
|
+
this.extensions = [];
|
|
50
|
+
this.channelReuses = 0;
|
|
51
|
+
this.hasMadeInitialAttempt = false;
|
|
52
|
+
this.jidResource = '';
|
|
53
|
+
this.http = new http_client_1.HttpClient({ customHeaders: options.customHeaders });
|
|
54
|
+
this.reconnectOnNoLongerSubscribed = options.reconnectOnNoLongerSubscribed !== false;
|
|
55
|
+
this.useServerSidePings = options.useServerSidePings !== false;
|
|
56
|
+
this.config = {
|
|
57
|
+
host: options.host,
|
|
58
|
+
apiHost: options.apiHost || options.host.replace('wss://streaming.', ''),
|
|
59
|
+
authToken: options.authToken,
|
|
60
|
+
jwt: options.jwt,
|
|
61
|
+
jid: options.jid,
|
|
62
|
+
jidResource: options.jidResource,
|
|
63
|
+
userId: options.userId,
|
|
64
|
+
channelId: null,
|
|
65
|
+
appName: options.appName,
|
|
66
|
+
appVersion: options.appVersion,
|
|
67
|
+
appId: options.appId,
|
|
68
|
+
logLevel: options.logLevel,
|
|
69
|
+
customHeaders: options.customHeaders
|
|
70
|
+
};
|
|
71
|
+
this.backgroundAssistantMode = this.checkIsBackgroundAssistant();
|
|
72
|
+
this.isGuest = !this.backgroundAssistantMode && !options.authToken;
|
|
73
|
+
let loggerAccessToken = options.authToken || '';
|
|
74
|
+
let loggerUrl = `https://api.${this.config.apiHost}/api/v2/diagnostics/trace`;
|
|
75
|
+
if (this.backgroundAssistantMode) {
|
|
76
|
+
loggerAccessToken = options.jwt;
|
|
77
|
+
loggerUrl += '/backgroundassistant';
|
|
78
|
+
}
|
|
79
|
+
this.logger = new genesys_cloud_client_logger_1.Logger({
|
|
80
|
+
accessToken: loggerAccessToken,
|
|
81
|
+
url: loggerUrl,
|
|
82
|
+
uploadDebounceTime: 1000,
|
|
83
|
+
initializeServerLogging: !this.isGuest && !options.optOutOfWebrtcStatsTelemetry,
|
|
84
|
+
/* streaming-client logging info */
|
|
85
|
+
appVersion: Client.version,
|
|
86
|
+
appName: 'streaming-client',
|
|
87
|
+
logLevel: this.config.logLevel || 'info',
|
|
88
|
+
logger: options.logger || console,
|
|
89
|
+
formatters: options.logFormatters,
|
|
90
|
+
useUniqueLogUploader: options.useUniqueLogUploader,
|
|
91
|
+
/* secondary/parent app info */
|
|
92
|
+
originAppName: options.appName,
|
|
93
|
+
originAppVersion: options.appVersion,
|
|
94
|
+
originAppId: options.appId,
|
|
95
|
+
customHeaders: options.customHeaders
|
|
96
|
+
});
|
|
97
|
+
this.connectionManager = new connection_manager_1.ConnectionManager(this.logger, this.config);
|
|
98
|
+
Object.keys(extensions).forEach((extensionName) => {
|
|
99
|
+
const extension = new extensions[extensionName](this, options);
|
|
100
|
+
this.extensions.push(extension);
|
|
101
|
+
if (!extension.tokenBucket) {
|
|
102
|
+
// default rate limit
|
|
103
|
+
// 20 stanzas per 1000 ms,
|
|
104
|
+
// adding up to 25 stanzas over the course of the 1000ms
|
|
105
|
+
// starting with 20 stanzas
|
|
106
|
+
// = 45 stanzas max per 1000 ms
|
|
107
|
+
// = 70 stanzas max per 2000 ms
|
|
108
|
+
extension.tokenBucket = new limiter_1.TokenBucket(20, 25, 1000);
|
|
109
|
+
extension.tokenBucket.content = 25;
|
|
110
|
+
}
|
|
111
|
+
if (typeof extension.on === 'function') {
|
|
112
|
+
extension.on('send', this.handleSendEventFromExtension.bind(this, extension));
|
|
113
|
+
}
|
|
114
|
+
this[extensionName] = extension.expose;
|
|
115
|
+
this[`_${extensionName}`] = extension;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
handleSendEventFromExtension(extension, data, message = false) {
|
|
119
|
+
return extension.tokenBucket.removeTokens(1, () => {
|
|
120
|
+
const stanza = this.activeStanzaInstance;
|
|
121
|
+
if (!stanza) {
|
|
122
|
+
return this.logger.warn('cannot send message, no active stanza client', { data, message }, { skipServer: true });
|
|
123
|
+
}
|
|
124
|
+
if (message === true) {
|
|
125
|
+
return stanza.sendMessage(data);
|
|
126
|
+
}
|
|
127
|
+
return stanza.sendIQ(data);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
checkIsBackgroundAssistant() {
|
|
131
|
+
if (this.config.jwt) {
|
|
132
|
+
const jwt = (0, utils_1.parseJwt)(this.config.jwt);
|
|
133
|
+
return jwt && jwt.iss === 'urn:purecloud:screenrecording';
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
addInateEventHandlers(stanza) {
|
|
138
|
+
// make sure we don't stack event handlers. There should only ever be *at most* one handler
|
|
139
|
+
this.removeStanzaBoundEventHandlers();
|
|
140
|
+
this.boundStanzaDisconnect = this.handleStanzaDisconnectedEvent.bind(this, stanza);
|
|
141
|
+
this.boundStanzaNoLongerSubscribed = this.handleNoLongerSubscribed.bind(this, stanza);
|
|
142
|
+
this.boundStanzaDuplicateId = this.handleDuplicateId.bind(this, stanza);
|
|
143
|
+
this.on(STANZA_DISCONNECTED, this.boundStanzaDisconnect);
|
|
144
|
+
this.on(NO_LONGER_SUBSCRIBED, this.boundStanzaNoLongerSubscribed);
|
|
145
|
+
this.on(DUPLICATE_ID, this.boundStanzaDuplicateId);
|
|
146
|
+
this.extensions.forEach(extension => {
|
|
147
|
+
if (typeof extension.handleIq === 'function') {
|
|
148
|
+
stanza.on('iq', extension.handleIq.bind(extension));
|
|
149
|
+
}
|
|
150
|
+
if (typeof extension.handleMessage === 'function') {
|
|
151
|
+
stanza.on('message', extension.handleMessage.bind(extension));
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
removeStanzaBoundEventHandlers() {
|
|
156
|
+
if (this.boundStanzaDisconnect) {
|
|
157
|
+
this.off(STANZA_DISCONNECTED, this.boundStanzaDisconnect);
|
|
158
|
+
this.boundStanzaDisconnect = undefined;
|
|
159
|
+
}
|
|
160
|
+
if (this.boundStanzaNoLongerSubscribed) {
|
|
161
|
+
this.off(NO_LONGER_SUBSCRIBED, this.boundStanzaNoLongerSubscribed);
|
|
162
|
+
this.boundStanzaNoLongerSubscribed = undefined;
|
|
163
|
+
}
|
|
164
|
+
if (this.boundStanzaDuplicateId) {
|
|
165
|
+
this.off(DUPLICATE_ID, this.boundStanzaDuplicateId);
|
|
166
|
+
this.boundStanzaDuplicateId = undefined;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
proxyStanzaEvents(stanza) {
|
|
170
|
+
stanza.originalEmitter = stanza.emit;
|
|
171
|
+
stanza.emit = (eventName, ...args) => {
|
|
172
|
+
const hasListeners = stanza.originalEmitter(eventName, ...args);
|
|
173
|
+
// there are a few events that need to be handled specially. stanza emits a `connected` event
|
|
174
|
+
// which means the web socket connected but that doesn't mean it's not going to immediately close.
|
|
175
|
+
// For this reason, we are going to equate the `session:started` event as "connected" which
|
|
176
|
+
// essentially means the websocket connection is stable.
|
|
177
|
+
//
|
|
178
|
+
// we are also going to let streaming client control its own connected and disconnected state so
|
|
179
|
+
// we will emit those events separately "when we are ready".
|
|
180
|
+
// as per block comment, we'll ignore the connected event
|
|
181
|
+
if (eventName === 'connected') {
|
|
182
|
+
return hasListeners;
|
|
183
|
+
}
|
|
184
|
+
else if (eventName === 'disconnected') {
|
|
185
|
+
eventName = STANZA_DISCONNECTED;
|
|
186
|
+
}
|
|
187
|
+
return this.emit(eventName, ...args);
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async handleStanzaDisconnectedEvent(disconnectedInstance) {
|
|
191
|
+
var _a, _b;
|
|
192
|
+
this.logger.info('stanzaDisconnected event received', { stanzaInstanceId: disconnectedInstance.id, channelId: disconnectedInstance.channelId });
|
|
193
|
+
this.connected = false;
|
|
194
|
+
this.connecting = false;
|
|
195
|
+
(_a = disconnectedInstance.pinger) === null || _a === void 0 ? void 0 : _a.stop();
|
|
196
|
+
(_b = disconnectedInstance.serverMonitor) === null || _b === void 0 ? void 0 : _b.stop();
|
|
197
|
+
this.removeAllListeners(STANZA_DISCONNECTED);
|
|
198
|
+
this.removeAllListeners(NO_LONGER_SUBSCRIBED);
|
|
199
|
+
// unproxy events
|
|
200
|
+
if (disconnectedInstance.originalEmitter) {
|
|
201
|
+
disconnectedInstance.emit = disconnectedInstance.originalEmitter;
|
|
202
|
+
}
|
|
203
|
+
this.activeStanzaInstance = undefined;
|
|
204
|
+
this.emit('disconnected', { reconnecting: this.autoReconnect });
|
|
205
|
+
if (this.autoReconnect) {
|
|
206
|
+
return this.connect({ keepTryingOnFailure: true })
|
|
207
|
+
.catch(error => {
|
|
208
|
+
this.logger.error('Failed to auto reconnect', {
|
|
209
|
+
keepTryingOnFailure: true,
|
|
210
|
+
stanzaInstanceId: disconnectedInstance.id,
|
|
211
|
+
channelId: disconnectedInstance.channelId
|
|
212
|
+
});
|
|
213
|
+
this.emit('disconnected', {
|
|
214
|
+
error,
|
|
215
|
+
reconnecting: false
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
handleNoLongerSubscribed(stanzaInstance) {
|
|
221
|
+
var _a, _b;
|
|
222
|
+
this.logger.warn('noLongerSubscribed event received', { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
223
|
+
(_a = stanzaInstance.pinger) === null || _a === void 0 ? void 0 : _a.stop();
|
|
224
|
+
(_b = stanzaInstance.serverMonitor) === null || _b === void 0 ? void 0 : _b.stop();
|
|
225
|
+
this.hardReconnectRequired = true;
|
|
226
|
+
if (!this.reconnectOnNoLongerSubscribed) {
|
|
227
|
+
this.autoReconnect = false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
handleDuplicateId(stanzaInstance) {
|
|
231
|
+
var _a, _b;
|
|
232
|
+
this.logger.warn('duplicate_id event received, forcing hard reconnect', { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
233
|
+
(_a = stanzaInstance.pinger) === null || _a === void 0 ? void 0 : _a.stop();
|
|
234
|
+
(_b = stanzaInstance.serverMonitor) === null || _b === void 0 ? void 0 : _b.stop();
|
|
235
|
+
this.hardReconnectRequired = true;
|
|
236
|
+
}
|
|
237
|
+
async disconnect() {
|
|
238
|
+
this.logger.info('streamingClient.disconnect was called');
|
|
239
|
+
// Clear stored JID on client disconnect.
|
|
240
|
+
this.jidResource = '';
|
|
241
|
+
return (0, utils_1.timeoutPromise)(resolve => {
|
|
242
|
+
this.hardReconnectRequired = true;
|
|
243
|
+
this.autoReconnect = false;
|
|
244
|
+
this.cancelConnectionAttempt = true;
|
|
245
|
+
this.http.stopAllRetries();
|
|
246
|
+
const currentStanza = this.connectionManager.currentStanzaInstance;
|
|
247
|
+
if (currentStanza) {
|
|
248
|
+
return currentStanza.disconnect().then(resolve);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
resolve();
|
|
252
|
+
}
|
|
253
|
+
}, 5000, 'disconnecting streaming service');
|
|
254
|
+
}
|
|
255
|
+
getSessionStoreKey() {
|
|
256
|
+
const differentiator = this.config.appName || this.logger.clientId;
|
|
257
|
+
return `${SESSION_STORE_KEY}_${differentiator}`;
|
|
258
|
+
}
|
|
259
|
+
getConnectionData() {
|
|
260
|
+
const connectionDataStr = sessionStorage.getItem(this.getSessionStoreKey());
|
|
261
|
+
const defaultValue = {
|
|
262
|
+
currentDelayMs: 0
|
|
263
|
+
};
|
|
264
|
+
if (connectionDataStr) {
|
|
265
|
+
try {
|
|
266
|
+
return JSON.parse(connectionDataStr);
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
this.logger.warn('failed to parse streaming client connection data');
|
|
270
|
+
return defaultValue;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return defaultValue;
|
|
274
|
+
}
|
|
275
|
+
setConnectionData(data) {
|
|
276
|
+
sessionStorage.setItem(this.getSessionStoreKey(), JSON.stringify(data));
|
|
277
|
+
}
|
|
278
|
+
increaseBackoff() {
|
|
279
|
+
const connectionData = this.getConnectionData();
|
|
280
|
+
const currentDelay = Math.max(connectionData.currentDelayMs * 2, INITIAL_DELAY * 2);
|
|
281
|
+
const newConnectionData = {
|
|
282
|
+
currentDelayMs: currentDelay,
|
|
283
|
+
delayMsAfterNextReduction: currentDelay / 2,
|
|
284
|
+
nextDelayReductionTime: new Date().getTime() + (currentDelay * BACKOFF_DECREASE_DELAY_MULTIPLIER),
|
|
285
|
+
timeOfTotalReset: new Date().getTime() + 1000 * 60 * 60 // one hour in the future
|
|
286
|
+
};
|
|
287
|
+
this.setConnectionData(newConnectionData);
|
|
288
|
+
return newConnectionData;
|
|
289
|
+
}
|
|
290
|
+
decreaseBackoff(newAmountMs) {
|
|
291
|
+
const data = this.getConnectionData();
|
|
292
|
+
const msUntilNextReduction = newAmountMs * BACKOFF_DECREASE_DELAY_MULTIPLIER;
|
|
293
|
+
const newConnectionData = {
|
|
294
|
+
currentDelayMs: newAmountMs,
|
|
295
|
+
delayMsAfterNextReduction: newAmountMs / 2,
|
|
296
|
+
nextDelayReductionTime: new Date().getTime() + (msUntilNextReduction),
|
|
297
|
+
timeOfTotalReset: data.timeOfTotalReset
|
|
298
|
+
};
|
|
299
|
+
// if we are past the total reset time, do that instead
|
|
300
|
+
if (data.timeOfTotalReset && data.timeOfTotalReset < new Date().getTime() || newAmountMs < INITIAL_DELAY) {
|
|
301
|
+
this.logger.debug('decreaseBackoff() called, but timeOfTotalReset has elasped or next delay is below 2s. Resetting backoff');
|
|
302
|
+
return this.setConnectionData({
|
|
303
|
+
currentDelayMs: 0
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
this.setConnectionData(newConnectionData);
|
|
307
|
+
clearTimeout(this.backoffReductionTimer);
|
|
308
|
+
this.logger.debug('Setting timer for next backoff reduction since we haven\'t reached total reset', { msUntilReduction: msUntilNextReduction, delayMsAfterNextReduction: newConnectionData.delayMsAfterNextReduction });
|
|
309
|
+
this.backoffReductionTimer = setTimeout(() => this.decreaseBackoff(newConnectionData.delayMsAfterNextReduction), msUntilNextReduction);
|
|
310
|
+
}
|
|
311
|
+
getStartingDelay(connectionData, maxDelay) {
|
|
312
|
+
// we don't want the delay to ever be less than 2 seconds
|
|
313
|
+
const minDelay = Math.max(connectionData.currentDelayMs, INITIAL_DELAY);
|
|
314
|
+
if (connectionData.timeOfTotalReset && connectionData.timeOfTotalReset < new Date().getTime()) {
|
|
315
|
+
return INITIAL_DELAY;
|
|
316
|
+
}
|
|
317
|
+
return Math.min(minDelay, maxDelay);
|
|
318
|
+
}
|
|
319
|
+
async connect(connectOpts) {
|
|
320
|
+
var _a;
|
|
321
|
+
this.cancelConnectionAttempt = false;
|
|
322
|
+
if (this.connecting) {
|
|
323
|
+
const error = new Error('Already trying to connect streaming client');
|
|
324
|
+
return this.logger.warn(error);
|
|
325
|
+
}
|
|
326
|
+
this.connecting = true;
|
|
327
|
+
const maxDelay = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxDelayBetweenConnectionAttempts) || 90000;
|
|
328
|
+
let maxAttempts = (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.maxConnectionAttempts) || 1;
|
|
329
|
+
if (connectOpts === null || connectOpts === void 0 ? void 0 : connectOpts.keepTryingOnFailure) {
|
|
330
|
+
// this maintains the previous functionality
|
|
331
|
+
maxAttempts = Infinity;
|
|
332
|
+
}
|
|
333
|
+
clearTimeout(this.backoffReductionTimer);
|
|
334
|
+
const connectionData = this.getConnectionData();
|
|
335
|
+
const startingDelay = this.getStartingDelay(connectionData, maxDelay);
|
|
336
|
+
const delayFirstAttempt = this.hasMadeInitialAttempt;
|
|
337
|
+
this.hasMadeInitialAttempt = true;
|
|
338
|
+
if (connectionData.currentDelayMs) {
|
|
339
|
+
this.logger.debug('streamingClient.connect was called, but backoff is remembered', { currentDelayMs: connectionData.currentDelayMs, delayingThisAttempt: delayFirstAttempt, clientId: this.logger.clientId, appName: this.config.appName });
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
await (0, exponential_backoff_1.backOff)(async () => {
|
|
343
|
+
const connectionData = this.getConnectionData();
|
|
344
|
+
await this.makeConnectionAttempt();
|
|
345
|
+
if (connectionData.nextDelayReductionTime) {
|
|
346
|
+
const msUntilReduction = connectionData.nextDelayReductionTime - new Date().getTime();
|
|
347
|
+
this.logger.debug('Setting timer for next backoff reduction', { msUntilReduction, delayMsAfterNextReduction: connectionData.delayMsAfterNextReduction });
|
|
348
|
+
this.backoffReductionTimer = setTimeout(() => this.decreaseBackoff(connectionData.delayMsAfterNextReduction || 0), msUntilReduction);
|
|
349
|
+
}
|
|
350
|
+
}, {
|
|
351
|
+
jitter: 'none',
|
|
352
|
+
maxDelay,
|
|
353
|
+
numOfAttempts: maxAttempts,
|
|
354
|
+
startingDelay,
|
|
355
|
+
delayFirstAttempt,
|
|
356
|
+
retry: this.backoffConnectRetryHandler.bind(this, {
|
|
357
|
+
maxConnectionAttempts: maxAttempts
|
|
358
|
+
})
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
let errorForThrowing;
|
|
363
|
+
let errorForLogging = err;
|
|
364
|
+
// Check `cancelConnectionAttempt` instead of the error type in case a different error occurred
|
|
365
|
+
// around the same time that might mask the cancellation.
|
|
366
|
+
if (this.cancelConnectionAttempt) {
|
|
367
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.userCancelled, 'Streaming client connection cancelled', err);
|
|
368
|
+
errorForLogging = errorForThrowing;
|
|
369
|
+
}
|
|
370
|
+
else if (!err) {
|
|
371
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.generic, 'Streaming client connection attempted and received an undefined error');
|
|
372
|
+
errorForLogging = errorForThrowing;
|
|
373
|
+
}
|
|
374
|
+
else if (err.name === 'AxiosError') {
|
|
375
|
+
const axiosError = err;
|
|
376
|
+
const config = axiosError.config || { url: undefined, method: undefined };
|
|
377
|
+
// sanitized error for logging
|
|
378
|
+
errorForLogging = {
|
|
379
|
+
config: {
|
|
380
|
+
url: config.url,
|
|
381
|
+
method: config.method
|
|
382
|
+
},
|
|
383
|
+
status: (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
384
|
+
code: axiosError.code,
|
|
385
|
+
name: axiosError.name,
|
|
386
|
+
message: axiosError.message
|
|
387
|
+
};
|
|
388
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.generic, 'Failed to connect streaming client due to network error', err);
|
|
389
|
+
if (this.networkErrorNeedsAuth(err)) {
|
|
390
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.invalid_token, 'Failed to connect streaming client due to invalid token', err);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (err instanceof sasl_error_1.default) {
|
|
394
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.invalid_token, 'Failed to connect streaming client due to invalid token', err);
|
|
395
|
+
if (this.saslErrorIsRetryable(err)) {
|
|
396
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.generic, 'Streaming client connection attempted and received a SASL error', err);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
errorForThrowing = new utils_1.StreamingClientError(interfaces_1.StreamingClientErrorTypes.generic, 'Streaming client connection attempted and received an unknown error', err);
|
|
401
|
+
}
|
|
402
|
+
this.logger.error('Failed to connect streaming client', { error: errorForLogging });
|
|
403
|
+
throw errorForThrowing;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
async backoffConnectRetryHandler(connectOpts, err, connectionAttempt) {
|
|
407
|
+
var _a, _b, _c, _d;
|
|
408
|
+
// Check `cancelConnectionAttempt` instead of the error type in case a different error occurred
|
|
409
|
+
// around the same time that might allow for retries to continue.
|
|
410
|
+
if (this.cancelConnectionAttempt) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
// if we exceed the `numOfAttempts` in the backoff config it still calls this retry fn and just ignores the result
|
|
414
|
+
// if that's the case, we just want to bail out and ignore all the extra logging here.
|
|
415
|
+
if (connectionAttempt >= connectOpts.maxConnectionAttempts) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const additionalErrorDetails = { connectionAttempt, error: err };
|
|
419
|
+
if (!err) {
|
|
420
|
+
additionalErrorDetails.error = new Error('streaming client backoff handler received undefined error');
|
|
421
|
+
}
|
|
422
|
+
else if (err.name === 'AxiosError') {
|
|
423
|
+
const axiosError = err;
|
|
424
|
+
const config = axiosError.config || { url: undefined, method: undefined };
|
|
425
|
+
const sanitizedError = {
|
|
426
|
+
config: {
|
|
427
|
+
url: config.url,
|
|
428
|
+
method: config.method
|
|
429
|
+
},
|
|
430
|
+
status: (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
431
|
+
code: axiosError.code,
|
|
432
|
+
name: axiosError.name,
|
|
433
|
+
message: axiosError.message
|
|
434
|
+
};
|
|
435
|
+
additionalErrorDetails.error = sanitizedError;
|
|
436
|
+
if (this.networkErrorNeedsAuth(err)) {
|
|
437
|
+
this.logger.error('Streaming client received an error that it can\'t recover from and will not attempt to reconnect', additionalErrorDetails);
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// If we get a sasl error, that means we made it all the way to the point of trying to open a websocket and
|
|
442
|
+
// it was rejected for some reason. Some errors might resolve if we try connecting again. Others need
|
|
443
|
+
// re-authentication.
|
|
444
|
+
if (err instanceof sasl_error_1.default) {
|
|
445
|
+
if (this.saslErrorIsRetryable(err)) {
|
|
446
|
+
this.logger.info('hardReconnectRequired set to true due to sasl error');
|
|
447
|
+
this.hardReconnectRequired = true;
|
|
448
|
+
Object.assign(additionalErrorDetails, { channelId: err.channelId, stanzaInstanceId: err.stanzaInstanceId });
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
additionalErrorDetails.error = err.condition;
|
|
452
|
+
this.logger.error('Streaming-client received a SASL error that it can\'t recover from and will not attempt to reconnect', additionalErrorDetails);
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// we don't need to log the stack for a timeout message
|
|
457
|
+
if (err instanceof timeout_error_1.TimeoutError) {
|
|
458
|
+
additionalErrorDetails.error = err.message;
|
|
459
|
+
const details = err.details;
|
|
460
|
+
if (details) {
|
|
461
|
+
additionalErrorDetails.details = details;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (err === null || err === void 0 ? void 0 : err.response) {
|
|
465
|
+
// This *should* be an axios error according to typings, but it appears this could be an AxiosError *or* and XmlHttpRequest
|
|
466
|
+
// we'll check both to be safe
|
|
467
|
+
const retryAfter = ((_b = err.response.headers) === null || _b === void 0 ? void 0 : _b['retry-after']) || ((_d = (_c = err.response).getResponseHeader) === null || _d === void 0 ? void 0 : _d.call(_c, 'retry-after'));
|
|
468
|
+
if (retryAfter) {
|
|
469
|
+
// retry after comes in seconds, we need to return milliseconds
|
|
470
|
+
const retryDelay = parseInt(retryAfter, 10) * 1000;
|
|
471
|
+
additionalErrorDetails.retryDelay = retryDelay;
|
|
472
|
+
this.logger.error('Failed streaming client connection attempt, respecting retry-after header and will retry afterwards.', additionalErrorDetails);
|
|
473
|
+
await (0, utils_1.delay)(retryDelay);
|
|
474
|
+
this.logger.debug('finished waiting for retry-after');
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const connectionData = this.increaseBackoff();
|
|
479
|
+
this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails);
|
|
480
|
+
this.logger.debug('debug: retry info', { expectedRetryInMs: connectionData.currentDelayMs, appName: this.config.appName, clientId: this.logger.clientId });
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
networkErrorNeedsAuth(error) {
|
|
484
|
+
var _a;
|
|
485
|
+
return [401, 403].includes(((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) || 0);
|
|
486
|
+
}
|
|
487
|
+
saslErrorIsRetryable(error) {
|
|
488
|
+
const retryConditions = ['encryption-required', 'incorrect-encoding', 'invalid-mechanism', 'malformed-request', 'mechanism-too-weak'];
|
|
489
|
+
return retryConditions.includes(error.condition);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Performs an active network connectivity check by querying the API.
|
|
493
|
+
* navigator.onLine is unreliable (VPNs, virtual adapters, etc.), so we
|
|
494
|
+
* actually reach out to verify we can talk to the server.
|
|
495
|
+
*
|
|
496
|
+
* Returns true if connectivity is confirmed, false otherwise.
|
|
497
|
+
* This is advisory only — it does not gate connection attempts.
|
|
498
|
+
*/
|
|
499
|
+
async checkNetworkConnectivity() {
|
|
500
|
+
// Quick hint check first — if the browser says offline, that's a strong signal
|
|
501
|
+
if (!navigator.onLine) {
|
|
502
|
+
this.logger.warn('navigator.onLine reports offline — connectivity may be unavailable');
|
|
503
|
+
this.emit('networkConnectivityWarning', { reason: 'navigator.onLine is false' });
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
// JWT-based connections (e.g. background assistants) don't have an auth token
|
|
507
|
+
// that works with the users/me endpoint, so we can only rely on navigator.onLine
|
|
508
|
+
if (this.config.jwt && !this.config.authToken) {
|
|
509
|
+
this.logger.debug('Skipping active connectivity check in JWT mode, relying on navigator.onLine');
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const opts = {
|
|
514
|
+
method: 'get',
|
|
515
|
+
host: this.config.apiHost,
|
|
516
|
+
authToken: this.config.authToken,
|
|
517
|
+
logger: this.logger,
|
|
518
|
+
requestTimeout: 10000
|
|
519
|
+
};
|
|
520
|
+
await this.http.requestApi('users/me', opts);
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
this.logger.warn('Active network connectivity check failed — connectivity may be unavailable', { error: err });
|
|
525
|
+
this.emit('networkConnectivityWarning', { reason: 'active connectivity check failed', error: err });
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async makeConnectionAttempt() {
|
|
530
|
+
var _a, _b;
|
|
531
|
+
if (this.cancelConnectionAttempt) {
|
|
532
|
+
throw new user_cancelled_error_1.default('Connection attempt cancelled');
|
|
533
|
+
}
|
|
534
|
+
// navigator.onLine is unreliable — use it as a hint, not a gate.
|
|
535
|
+
// Fire off an active connectivity check in the background. It will
|
|
536
|
+
// log and emit warnings if there's an issue, but we don't wait for it.
|
|
537
|
+
this.checkNetworkConnectivity().then(isConnected => {
|
|
538
|
+
if (!isConnected) {
|
|
539
|
+
this.logger.warn('Network connectivity check failed, but proceeding with connection attempt anyway');
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
let stanzaInstance;
|
|
543
|
+
const previousConnectingState = this.connecting;
|
|
544
|
+
try {
|
|
545
|
+
await this.prepareForConnect();
|
|
546
|
+
if (this.cancelConnectionAttempt) {
|
|
547
|
+
throw new user_cancelled_error_1.default('Connection attempt cancelled');
|
|
548
|
+
}
|
|
549
|
+
stanzaInstance = await this.connectionManager.getNewStanzaConnection();
|
|
550
|
+
this.addInateEventHandlers(stanzaInstance);
|
|
551
|
+
this.proxyStanzaEvents(stanzaInstance);
|
|
552
|
+
// handle any extension configuration
|
|
553
|
+
for (const extension of this.extensions) {
|
|
554
|
+
if (extension.configureNewStanzaInstance) {
|
|
555
|
+
await extension.configureNewStanzaInstance(stanzaInstance);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
for (const extension of this.extensions) {
|
|
559
|
+
extension.handleStanzaInstanceChange(stanzaInstance);
|
|
560
|
+
}
|
|
561
|
+
await this.setupConnectionMonitoring(stanzaInstance);
|
|
562
|
+
this.activeStanzaInstance = stanzaInstance;
|
|
563
|
+
this.connected = true;
|
|
564
|
+
this.connecting = false;
|
|
565
|
+
this.emit('connected');
|
|
566
|
+
}
|
|
567
|
+
catch (err) {
|
|
568
|
+
if (stanzaInstance) {
|
|
569
|
+
this.logger.error('Error occurred in connection attempt, but after websocket connected. Cleaning up connection so backoff is respected', { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
570
|
+
this.removeStanzaBoundEventHandlers();
|
|
571
|
+
(_a = stanzaInstance.pinger) === null || _a === void 0 ? void 0 : _a.stop();
|
|
572
|
+
(_b = stanzaInstance.serverMonitor) === null || _b === void 0 ? void 0 : _b.stop();
|
|
573
|
+
await stanzaInstance.disconnect();
|
|
574
|
+
this.connected = false;
|
|
575
|
+
this.connecting = previousConnectingState;
|
|
576
|
+
}
|
|
577
|
+
throw err;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async setupConnectionMonitoring(stanzaInstance) {
|
|
581
|
+
stanzaInstance.connectionTransfer = new connection_transfer_1.ConnectionTransfer(this, stanzaInstance);
|
|
582
|
+
const setupClientPinger = (message) => {
|
|
583
|
+
const logMessage = `${message}, falling back to client-side pinging`;
|
|
584
|
+
this.logger.warn(logMessage, { stanzaInstanceId: stanzaInstance.id, channelId: stanzaInstance.channelId });
|
|
585
|
+
stanzaInstance.pinger = new ping_1.Ping(this, stanzaInstance);
|
|
586
|
+
};
|
|
587
|
+
if (this.useServerSidePings) {
|
|
588
|
+
try {
|
|
589
|
+
// if this fails, then hawk doesn't support serverside pinging and we need to do client side pings
|
|
590
|
+
await stanzaInstance.subscribeToNode(this._notifications.pubsubHost, 'enable.server.side.pings');
|
|
591
|
+
stanzaInstance.serverMonitor = new server_monitor_1.ServerMonitor(this, stanzaInstance);
|
|
592
|
+
}
|
|
593
|
+
catch (err) {
|
|
594
|
+
setupClientPinger('failed to establish server-side pinging');
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
setupClientPinger('client configured to not use server-side pinging');
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
async prepareForConnect() {
|
|
602
|
+
if (this.config.jwt) {
|
|
603
|
+
this.hardReconnectRequired = false;
|
|
604
|
+
return this.connectionManager.setConfig(this.config);
|
|
605
|
+
}
|
|
606
|
+
if (!this.hardReconnectRequired) {
|
|
607
|
+
this.channelReuses++;
|
|
608
|
+
if (this.channelReuses >= MAX_CHANNEL_REUSES) {
|
|
609
|
+
this.logger.warn('Forcing a hard reconnect due to max channel reuses', { channelId: this.config.channelId, channelReuses: this.channelReuses });
|
|
610
|
+
this.channelReuses = 0;
|
|
611
|
+
this.hardReconnectRequired = true;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (this.hardReconnectRequired) {
|
|
615
|
+
let userPromise;
|
|
616
|
+
if (this.config.userId && this.config.jid) {
|
|
617
|
+
userPromise = Promise.resolve({ userId: this.config.userId, jid: this.config.jid });
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const jidRequestOpts = {
|
|
621
|
+
method: 'get',
|
|
622
|
+
host: this.config.apiHost,
|
|
623
|
+
authToken: this.config.authToken,
|
|
624
|
+
logger: this.logger
|
|
625
|
+
};
|
|
626
|
+
userPromise = this.http.requestApi('users/me', jidRequestOpts)
|
|
627
|
+
.then(res => {
|
|
628
|
+
return {
|
|
629
|
+
userId: res.data.id,
|
|
630
|
+
jid: res.data.chat.jabberId
|
|
631
|
+
};
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
// If no jidResource is provided, generate a random one to maintain ourselves.
|
|
635
|
+
this.jidResource = this.config.jidResource || globalThis.crypto.randomUUID();
|
|
636
|
+
const channelRequestOpts = {
|
|
637
|
+
method: 'post',
|
|
638
|
+
host: this.config.apiHost,
|
|
639
|
+
authToken: this.config.authToken,
|
|
640
|
+
logger: this.logger
|
|
641
|
+
};
|
|
642
|
+
const channelPromise = await this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
|
|
643
|
+
.then(res => res.data.id);
|
|
644
|
+
const [{ userId, jid }, channelId] = await Promise.all([userPromise, channelPromise]);
|
|
645
|
+
this.config.userId = userId;
|
|
646
|
+
this.config.jid = jid;
|
|
647
|
+
this.config.jidResource = this.jidResource;
|
|
648
|
+
this.config.channelId = channelId;
|
|
649
|
+
this.autoReconnect = true;
|
|
650
|
+
this.logger.info('attempting to connect streaming client on channel', { channelId });
|
|
651
|
+
this.connectionManager.setConfig(this.config);
|
|
652
|
+
this.hardReconnectRequired = false;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
stopServerLogging() {
|
|
656
|
+
/* flush all pending logs – then turn off the logger */
|
|
657
|
+
this.logger.sendAllLogsInstantly();
|
|
658
|
+
this.logger.stopServerLogging();
|
|
659
|
+
}
|
|
660
|
+
startServerLogging() {
|
|
661
|
+
this.logger.startServerLogging();
|
|
662
|
+
}
|
|
663
|
+
setAccessToken(token) {
|
|
664
|
+
this.config.authToken = token;
|
|
665
|
+
this.logger.setAccessToken(token);
|
|
666
|
+
}
|
|
667
|
+
static extend(namespace, extension) {
|
|
668
|
+
if (extensions[namespace]) {
|
|
669
|
+
throw new Error(`Cannot register already existing namespace ${namespace}`);
|
|
670
|
+
}
|
|
671
|
+
extensions[namespace] = extension;
|
|
672
|
+
}
|
|
673
|
+
get version() {
|
|
674
|
+
return Client.version;
|
|
675
|
+
}
|
|
676
|
+
static get version() {
|
|
677
|
+
return '20.1.0';
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
exports.Client = Client;
|