@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.
Files changed (176) hide show
  1. package/README.md +47 -0
  2. package/dist/cjs/alerting-leader.d.ts +29 -0
  3. package/dist/cjs/alerting-leader.js +163 -0
  4. package/dist/cjs/client.d.ts +84 -0
  5. package/dist/cjs/client.js +680 -0
  6. package/dist/cjs/connection-manager.d.ts +18 -0
  7. package/dist/cjs/connection-manager.js +120 -0
  8. package/dist/cjs/connection-transfer.d.ts +7 -0
  9. package/dist/cjs/connection-transfer.js +20 -0
  10. package/dist/cjs/http-client.d.ts +18 -0
  11. package/dist/cjs/http-client.js +230 -0
  12. package/dist/cjs/index.d.ts +10 -0
  13. package/dist/cjs/index.js +31 -0
  14. package/dist/cjs/messenger.d.ts +37 -0
  15. package/dist/cjs/messenger.js +54 -0
  16. package/dist/cjs/notifications.d.ts +94 -0
  17. package/dist/cjs/notifications.js +495 -0
  18. package/dist/cjs/ping.d.ts +21 -0
  19. package/dist/cjs/ping.js +60 -0
  20. package/dist/cjs/polyfills.d.ts +0 -0
  21. package/dist/cjs/polyfills.js +6 -0
  22. package/dist/cjs/server-monitor.d.ts +16 -0
  23. package/dist/cjs/server-monitor.js +44 -0
  24. package/dist/cjs/stanza-definitions/webrtc-signaling.d.ts +29 -0
  25. package/dist/cjs/stanza-definitions/webrtc-signaling.js +113 -0
  26. package/dist/cjs/stanza-definitions/xep0051.d.ts +19 -0
  27. package/dist/cjs/stanza-definitions/xep0051.js +28 -0
  28. package/dist/cjs/stats-formatter.d.ts +6 -0
  29. package/dist/cjs/stats-formatter.js +63 -0
  30. package/dist/cjs/types/genesys-cloud-media-session.d.ts +64 -0
  31. package/dist/cjs/types/genesys-cloud-media-session.js +353 -0
  32. package/dist/cjs/types/interfaces.d.ts +310 -0
  33. package/dist/cjs/types/interfaces.js +22 -0
  34. package/dist/cjs/types/libs.d.ts +32 -0
  35. package/dist/cjs/types/libs.js +1 -0
  36. package/dist/cjs/types/media-session.d.ts +76 -0
  37. package/dist/cjs/types/media-session.js +2 -0
  38. package/dist/cjs/types/named-agent.d.ts +13 -0
  39. package/dist/cjs/types/named-agent.js +2 -0
  40. package/dist/cjs/types/offline-error.d.ts +2 -0
  41. package/dist/cjs/types/offline-error.js +5 -0
  42. package/dist/cjs/types/sasl-error.d.ts +8 -0
  43. package/dist/cjs/types/sasl-error.js +12 -0
  44. package/dist/cjs/types/stanza-media-session.d.ts +56 -0
  45. package/dist/cjs/types/stanza-media-session.js +255 -0
  46. package/dist/cjs/types/streaming-client-extension.d.ts +14 -0
  47. package/dist/cjs/types/streaming-client-extension.js +2 -0
  48. package/dist/cjs/types/timeout-error.d.ts +3 -0
  49. package/dist/cjs/types/timeout-error.js +9 -0
  50. package/dist/cjs/types/user-cancelled-error.d.ts +2 -0
  51. package/dist/cjs/types/user-cancelled-error.js +5 -0
  52. package/dist/cjs/utils.d.ts +34 -0
  53. package/dist/cjs/utils.js +202 -0
  54. package/dist/cjs/webrtc.d.ts +126 -0
  55. package/dist/cjs/webrtc.js +891 -0
  56. package/dist/deploy-info.json +21 -0
  57. package/dist/es/alerting-leader.d.ts +29 -0
  58. package/dist/es/alerting-leader.js +177 -0
  59. package/dist/es/client.d.ts +84 -0
  60. package/dist/es/client.js +699 -0
  61. package/dist/es/connection-manager.d.ts +18 -0
  62. package/dist/es/connection-manager.js +124 -0
  63. package/dist/es/connection-transfer.d.ts +7 -0
  64. package/dist/es/connection-transfer.js +16 -0
  65. package/dist/es/http-client.d.ts +18 -0
  66. package/dist/es/http-client.js +194 -0
  67. package/dist/es/index.bundle.js +39629 -0
  68. package/dist/es/index.d.ts +10 -0
  69. package/dist/es/index.js +10 -0
  70. package/dist/es/messenger.d.ts +37 -0
  71. package/dist/es/messenger.js +62 -0
  72. package/dist/es/notifications.d.ts +94 -0
  73. package/dist/es/notifications.js +507 -0
  74. package/dist/es/ping.d.ts +21 -0
  75. package/dist/es/ping.js +68 -0
  76. package/dist/es/polyfills.d.ts +0 -0
  77. package/dist/es/polyfills.js +6 -0
  78. package/dist/es/server-monitor.d.ts +16 -0
  79. package/dist/es/server-monitor.js +41 -0
  80. package/dist/es/stanza-definitions/webrtc-signaling.d.ts +29 -0
  81. package/dist/es/stanza-definitions/webrtc-signaling.js +110 -0
  82. package/dist/es/stanza-definitions/xep0051.d.ts +19 -0
  83. package/dist/es/stanza-definitions/xep0051.js +25 -0
  84. package/dist/es/stats-formatter.d.ts +6 -0
  85. package/dist/es/stats-formatter.js +54 -0
  86. package/dist/es/types/genesys-cloud-media-session.d.ts +64 -0
  87. package/dist/es/types/genesys-cloud-media-session.js +379 -0
  88. package/dist/es/types/interfaces.d.ts +310 -0
  89. package/dist/es/types/interfaces.js +19 -0
  90. package/dist/es/types/libs.d.ts +32 -0
  91. package/dist/es/types/libs.js +1 -0
  92. package/dist/es/types/media-session.d.ts +76 -0
  93. package/dist/es/types/media-session.js +1 -0
  94. package/dist/es/types/named-agent.d.ts +13 -0
  95. package/dist/es/types/named-agent.js +1 -0
  96. package/dist/es/types/offline-error.d.ts +2 -0
  97. package/dist/es/types/offline-error.js +2 -0
  98. package/dist/es/types/sasl-error.d.ts +8 -0
  99. package/dist/es/types/sasl-error.js +9 -0
  100. package/dist/es/types/stanza-media-session.d.ts +56 -0
  101. package/dist/es/types/stanza-media-session.js +264 -0
  102. package/dist/es/types/streaming-client-extension.d.ts +14 -0
  103. package/dist/es/types/streaming-client-extension.js +1 -0
  104. package/dist/es/types/timeout-error.d.ts +3 -0
  105. package/dist/es/types/timeout-error.js +5 -0
  106. package/dist/es/types/user-cancelled-error.d.ts +2 -0
  107. package/dist/es/types/user-cancelled-error.js +2 -0
  108. package/dist/es/utils.d.ts +34 -0
  109. package/dist/es/utils.js +192 -0
  110. package/dist/es/webrtc.d.ts +126 -0
  111. package/dist/es/webrtc.js +917 -0
  112. package/dist/manifest.json +19 -0
  113. package/dist/npm/CHANGELOG.md +637 -0
  114. package/dist/npm/README.md +47 -0
  115. package/dist/npm/alerting-leader.d.ts +29 -0
  116. package/dist/npm/alerting-leader.js +163 -0
  117. package/dist/npm/client.d.ts +84 -0
  118. package/dist/npm/client.js +680 -0
  119. package/dist/npm/connection-manager.d.ts +18 -0
  120. package/dist/npm/connection-manager.js +120 -0
  121. package/dist/npm/connection-transfer.d.ts +7 -0
  122. package/dist/npm/connection-transfer.js +20 -0
  123. package/dist/npm/http-client.d.ts +18 -0
  124. package/dist/npm/http-client.js +230 -0
  125. package/dist/npm/index.d.ts +10 -0
  126. package/dist/npm/index.js +31 -0
  127. package/dist/npm/messenger.d.ts +37 -0
  128. package/dist/npm/messenger.js +54 -0
  129. package/dist/npm/module.js +10 -0
  130. package/dist/npm/notifications.d.ts +94 -0
  131. package/dist/npm/notifications.js +495 -0
  132. package/dist/npm/ping.d.ts +21 -0
  133. package/dist/npm/ping.js +60 -0
  134. package/dist/npm/polyfills.d.ts +0 -0
  135. package/dist/npm/polyfills.js +6 -0
  136. package/dist/npm/server-monitor.d.ts +16 -0
  137. package/dist/npm/server-monitor.js +44 -0
  138. package/dist/npm/stanza-definitions/webrtc-signaling.d.ts +29 -0
  139. package/dist/npm/stanza-definitions/webrtc-signaling.js +113 -0
  140. package/dist/npm/stanza-definitions/xep0051.d.ts +19 -0
  141. package/dist/npm/stanza-definitions/xep0051.js +28 -0
  142. package/dist/npm/stats-formatter.d.ts +6 -0
  143. package/dist/npm/stats-formatter.js +63 -0
  144. package/dist/npm/types/genesys-cloud-media-session.d.ts +64 -0
  145. package/dist/npm/types/genesys-cloud-media-session.js +353 -0
  146. package/dist/npm/types/interfaces.d.ts +310 -0
  147. package/dist/npm/types/interfaces.js +22 -0
  148. package/dist/npm/types/libs.d.ts +32 -0
  149. package/dist/npm/types/libs.js +1 -0
  150. package/dist/npm/types/media-session.d.ts +76 -0
  151. package/dist/npm/types/media-session.js +2 -0
  152. package/dist/npm/types/named-agent.d.ts +13 -0
  153. package/dist/npm/types/named-agent.js +2 -0
  154. package/dist/npm/types/offline-error.d.ts +2 -0
  155. package/dist/npm/types/offline-error.js +5 -0
  156. package/dist/npm/types/sasl-error.d.ts +8 -0
  157. package/dist/npm/types/sasl-error.js +12 -0
  158. package/dist/npm/types/stanza-media-session.d.ts +56 -0
  159. package/dist/npm/types/stanza-media-session.js +255 -0
  160. package/dist/npm/types/streaming-client-extension.d.ts +14 -0
  161. package/dist/npm/types/streaming-client-extension.js +2 -0
  162. package/dist/npm/types/timeout-error.d.ts +3 -0
  163. package/dist/npm/types/timeout-error.js +9 -0
  164. package/dist/npm/types/user-cancelled-error.d.ts +2 -0
  165. package/dist/npm/types/user-cancelled-error.js +5 -0
  166. package/dist/npm/utils.d.ts +34 -0
  167. package/dist/npm/utils.js +207 -0
  168. package/dist/npm/webrtc.d.ts +126 -0
  169. package/dist/npm/webrtc.js +893 -0
  170. package/dist/streaming-client.browser.js +2 -0
  171. package/dist/streaming-client.browser.js.LICENSE.txt +12 -0
  172. package/dist/v20/streaming-client.browser.js +2 -0
  173. package/dist/v20/streaming-client.browser.js.LICENSE.txt +12 -0
  174. package/dist/v20.1.0/streaming-client.browser.js +2 -0
  175. package/dist/v20.1.0/streaming-client.browser.js.LICENSE.txt +12 -0
  176. package/package.json +132 -0
@@ -0,0 +1,495 @@
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.Notifications = void 0;
7
+ const debounce_promise_1 = __importDefault(require("debounce-promise"));
8
+ const utils_1 = require("./utils");
9
+ const _1 = require("./");
10
+ const PUBSUB_HOST_DEFAULT = 'notifications.mypurecloud.com';
11
+ const MAX_SUBSCRIBABLE_TOPICS = 1000;
12
+ const DROPPED_TOPICS_DISPLAY_COUNT = 20;
13
+ const DEFAULT_PRIORITY = 0;
14
+ function mergeAndDedup(arr1, arr2) {
15
+ return [...arr1, ...arr2].filter((t, i, arr) => arr.indexOf(t) === i);
16
+ }
17
+ class Notifications {
18
+ constructor(client, options) {
19
+ var _a;
20
+ this.subscriptions = {};
21
+ this.bulkSubscriptions = {};
22
+ this.topicPriorities = {};
23
+ this.internalSubscriptions = [];
24
+ this.client = client;
25
+ this.enablePartialBulkResubscribe = (_a = options === null || options === void 0 ? void 0 : options.enablePartialBulkResubscribe) !== null && _a !== void 0 ? _a : false;
26
+ client.on('pubsub:event', this.pubsubEvent.bind(this));
27
+ client.on('connected', this.subscriptionsKeepAlive.bind(this));
28
+ this.debouncedResubscribe = (0, debounce_promise_1.default)(this.resubscribe.bind(this), 100);
29
+ }
30
+ get pubsubHost() {
31
+ try {
32
+ const host = this.client.config.apiHost.replace(/http(s?):\/\//, '');
33
+ return `notifications.${host}`;
34
+ }
35
+ catch (e) {
36
+ return PUBSUB_HOST_DEFAULT;
37
+ }
38
+ }
39
+ handleStanzaInstanceChange(stanza) {
40
+ // we need to resub if we go through a hard reconnect
41
+ const needsToResub = this.stanzaInstance &&
42
+ this.stanzaInstance.id !== stanza.id &&
43
+ this.stanzaInstance.channelId !== stanza.channelId;
44
+ this.stanzaInstance = stanza;
45
+ if (needsToResub) {
46
+ this.client.logger.info('resubscribing due to hard reconnect');
47
+ this.debouncedResubscribe().catch((err) => {
48
+ const msg = 'Error resubscribing to topics';
49
+ this.client.logger.error(msg, err);
50
+ this.client.emit('pubsub:error', { msg, err });
51
+ });
52
+ }
53
+ }
54
+ topicHandlers(topic) {
55
+ if (!this.subscriptions[topic]) {
56
+ this.subscriptions[topic] = [];
57
+ }
58
+ return this.subscriptions[topic];
59
+ }
60
+ pubsubEvent({ pubsub }) {
61
+ let topic = pubsub.items.node;
62
+ if (topic.includes('no_longer_subscribed')) {
63
+ topic = 'no_longer_subscribed';
64
+ }
65
+ else if (topic.includes('duplicate_id')) {
66
+ topic = 'duplicate_id';
67
+ }
68
+ const payload = pubsub.items.published[0].content.json;
69
+ const handlers = this.topicHandlers(topic);
70
+ this.client.emit('notify', { topic, data: payload });
71
+ this.client.emit(`notify:${topic}`, payload);
72
+ handlers.forEach((handler) => {
73
+ handler(payload);
74
+ });
75
+ }
76
+ async xmppSubscribe(topic) {
77
+ if (this.topicHandlers(topic).length !== 0 || this.bulkSubscriptions[topic]) {
78
+ return Promise.resolve();
79
+ }
80
+ const subscribe = () => this.stanzaInstance.subscribeToNode(this.pubsubHost, topic);
81
+ if (this.client.connected) {
82
+ return subscribe();
83
+ }
84
+ else {
85
+ return new Promise((resolve, reject) => {
86
+ this.client.once('connected', () => {
87
+ return subscribe().then(resolve, reject);
88
+ });
89
+ });
90
+ }
91
+ }
92
+ xmppUnsubscribe(topic) {
93
+ if (this.topicHandlers(topic).length !== 0 || this.bulkSubscriptions[topic]) {
94
+ return Promise.resolve();
95
+ }
96
+ const unsubscribe = () => this.stanzaInstance.unsubscribeFromNode(this.pubsubHost, topic);
97
+ if (this.client.connected) {
98
+ return unsubscribe();
99
+ }
100
+ else {
101
+ return new Promise((resolve, reject) => {
102
+ this.client.once('connected', () => {
103
+ return unsubscribe().then(resolve, reject);
104
+ });
105
+ });
106
+ }
107
+ }
108
+ mapCombineTopics(topics) {
109
+ const prefixes = {};
110
+ const precombinedTopics = [];
111
+ const uncombinedTopics = [];
112
+ topics.forEach(t => {
113
+ if (t.includes('?')) {
114
+ precombinedTopics.push({ id: t });
115
+ }
116
+ else {
117
+ uncombinedTopics.push(t);
118
+ }
119
+ });
120
+ uncombinedTopics.map(t => {
121
+ const split = t.split('.');
122
+ const postfix = split.splice(split.length - 1);
123
+ const prefix = split.join('.');
124
+ return { prefix, postfix };
125
+ }).forEach(t => {
126
+ if (prefixes[t.prefix]) {
127
+ prefixes[t.prefix].push(t.postfix);
128
+ }
129
+ else {
130
+ prefixes[t.prefix] = [t.postfix];
131
+ }
132
+ });
133
+ const combinedTopics = [];
134
+ // Max length of 200 in topic names
135
+ // so recursively break them up if the combined length exceeds 200
136
+ const combineTopics = (prefix, postFixes) => {
137
+ const delimiter = postFixes.length === 1 ? '.' : '?';
138
+ const id = `${prefix}${delimiter}${postFixes.join('&')}`;
139
+ if (id.length < 200) {
140
+ combinedTopics.push({ id });
141
+ }
142
+ else if (postFixes.length === 1) {
143
+ this.client.logger.error('Refusing to attempt topic with length > 200', id);
144
+ }
145
+ else {
146
+ combineTopics(prefix, postFixes.slice(0, postFixes.length / 2));
147
+ combineTopics(prefix, postFixes.slice(postFixes.length / 2));
148
+ }
149
+ };
150
+ Object.keys(prefixes).forEach(prefix => {
151
+ const postFixes = prefixes[prefix];
152
+ combineTopics(prefix, postFixes);
153
+ });
154
+ const allTopics = combinedTopics.concat(precombinedTopics);
155
+ return this.truncateTopicList(this.prioritizeTopicList(allTopics));
156
+ }
157
+ prioritizeTopicList(topics) {
158
+ topics.sort((topicA, topicB) => {
159
+ return (this.getTopicPriority(topicB.id) - this.getTopicPriority(topicA.id));
160
+ });
161
+ return topics;
162
+ }
163
+ getTopicPriority(topic, returnDefault = true) {
164
+ const { prefix, postfixes } = this.getTopicParts(topic);
165
+ const oldPriorities = this.topicPriorities[prefix];
166
+ const matches = oldPriorities && Object.keys(oldPriorities).filter(p => postfixes.includes(p)).map(p => oldPriorities[p]);
167
+ const priority = matches && matches.length && matches.reduce((max, current) => current > max ? current : max);
168
+ return returnDefault ? priority || DEFAULT_PRIORITY : priority;
169
+ }
170
+ truncateTopicList(topics) {
171
+ const keptTopics = topics.slice(0, MAX_SUBSCRIBABLE_TOPICS);
172
+ if (topics.length > MAX_SUBSCRIBABLE_TOPICS) {
173
+ let droppedTopics = topics.slice(MAX_SUBSCRIBABLE_TOPICS);
174
+ if (droppedTopics.length > DROPPED_TOPICS_DISPLAY_COUNT) {
175
+ const length = droppedTopics.length - DROPPED_TOPICS_DISPLAY_COUNT;
176
+ droppedTopics = droppedTopics.slice(DROPPED_TOPICS_DISPLAY_COUNT);
177
+ droppedTopics.push(`...and ${length} more`);
178
+ }
179
+ this.client.logger.warn('Too many topics to subscribe to; truncating extra topics', { droppedTopics });
180
+ }
181
+ return keptTopics;
182
+ }
183
+ async makeBulkSubscribeRequest(topics, options) {
184
+ // Cancel any in-flight retry or connected-wait from a previous bulk subscribe.
185
+ // If the topic list has changed (subscribe/unsubscribe called between retries),
186
+ // the stale request would send an outdated topic list. Since bulk subscribes with
187
+ // replace=true use PUT (full replacement), only the most recent request matters.
188
+ if (this.pendingConnectedWait) {
189
+ this.pendingConnectedWait.cancel(new Error('Superseded by newer bulk subscribe request'));
190
+ this.pendingConnectedWait = undefined;
191
+ }
192
+ if (this.pendingBulkSubscribeRetry && !this.pendingBulkSubscribeRetry.hasCompleted()) {
193
+ this.client.logger.info('Cancelling previous bulk subscribe retry — new request supersedes it');
194
+ this.pendingBulkSubscribeRetry.cancel(new Error('Superseded by newer bulk subscribe request'));
195
+ }
196
+ // If the client is not connected, wait for reconnection before attempting the request.
197
+ // This prevents firing requests against a dead/stale channel ID which would result in
198
+ // 400 errors (notification.unable.to.get.channel.id). After reconnection, the stanza
199
+ // instance will have the fresh channel ID from the new connection.
200
+ if (!this.client.connected) {
201
+ this.client.logger.info('Client not connected, waiting for reconnection before bulk subscribe', {
202
+ topicCount: topics.length
203
+ });
204
+ await new Promise((resolve, reject) => {
205
+ const onConnected = () => {
206
+ this.pendingConnectedWait = undefined;
207
+ resolve();
208
+ };
209
+ this.client.once('connected', onConnected);
210
+ this.pendingConnectedWait = {
211
+ cancel: (reason) => {
212
+ this.client.off('connected', onConnected);
213
+ reject(reason);
214
+ }
215
+ };
216
+ });
217
+ }
218
+ const requestOptions = {
219
+ method: options.replace ? 'put' : 'post',
220
+ host: this.client.config.apiHost,
221
+ authToken: this.client.config.authToken,
222
+ data: JSON.stringify(this.mapCombineTopics(topics)),
223
+ logger: this.client.logger,
224
+ maxAttempts: 3
225
+ };
226
+ const channelId = this.stanzaInstance.channelId;
227
+ let path = `notifications/channels/${channelId}/subscriptions`;
228
+ if (this.enablePartialBulkResubscribe) {
229
+ path += '?ignoreErrors=true';
230
+ }
231
+ const retry = this.client.http.requestApiWithRetry(path, requestOptions, 2000);
232
+ this.pendingBulkSubscribeRetry = retry;
233
+ return retry.promise;
234
+ }
235
+ createSubscription(topic, handler) {
236
+ const topics = (0, utils_1.splitIntoIndividualTopics)(topic);
237
+ topics.forEach(t => {
238
+ const handlers = this.topicHandlers(t);
239
+ if (!handlers.includes(handler)) {
240
+ handlers.push(handler);
241
+ }
242
+ });
243
+ }
244
+ removeSubscription(topic, handler) {
245
+ const topics = (0, utils_1.splitIntoIndividualTopics)(topic);
246
+ topics.forEach(t => {
247
+ const handlers = this.topicHandlers(t);
248
+ const handlerIndex = handlers.indexOf(handler);
249
+ if (handlerIndex > -1) {
250
+ handlers.splice(handlerIndex, 1);
251
+ }
252
+ /* if we have removed all our individual handlers, we don't need the topic anymore
253
+ (note: we aren't removing any bulkSubs if they exist for this topic) */
254
+ if (!handlers.length) {
255
+ delete this.subscriptions[t];
256
+ }
257
+ });
258
+ }
259
+ removeTopicPriority(topic) {
260
+ if (this.getTopicPriority(topic, false)) {
261
+ const { prefix, postfixes } = this.getTopicParts(topic);
262
+ postfixes.forEach(postfix => {
263
+ delete this.topicPriorities[prefix][postfix];
264
+ });
265
+ if (!Object.keys(this.topicPriorities[prefix]).length) {
266
+ delete this.topicPriorities[prefix];
267
+ }
268
+ }
269
+ }
270
+ getActiveIndividualTopics() {
271
+ const activeTopics = [];
272
+ const topics = Object.keys(this.subscriptions);
273
+ topics.forEach(topic => {
274
+ if (topic === 'streaming-subscriptions-expiring') {
275
+ return; // this doesn't need subscribed
276
+ }
277
+ const handlers = this.topicHandlers(topic);
278
+ if (handlers.length > 0) {
279
+ activeTopics.push(topic);
280
+ }
281
+ });
282
+ return activeTopics;
283
+ }
284
+ resubscribe() {
285
+ const bulkSubs = Object.keys(this.bulkSubscriptions);
286
+ /* if we don't have bulk or individual subs, we don't need to resubscribe */
287
+ const noTopics = bulkSubs.length + this.getActiveIndividualTopics().length === 0;
288
+ if (noTopics) {
289
+ return Promise.resolve({});
290
+ }
291
+ /* only pass in bulk subs with the replace flag – bulkSubscribe() will handle merging our individual topics (see PCM-1846) */
292
+ return this.bulkSubscribe(bulkSubs, { replace: true });
293
+ }
294
+ subscriptionsKeepAlive() {
295
+ const topic = 'streaming-subscriptions-expiring';
296
+ if (this.topicHandlers(topic).length === 0) {
297
+ this.createSubscription(topic, () => {
298
+ this.client.logger.info(`${topic} - Triggering resubscribe.`, { channelId: this.client.config.channelId });
299
+ this.debouncedResubscribe().catch((err) => {
300
+ const msg = 'Error resubscribing to topics';
301
+ this.client.logger.error(msg, err);
302
+ this.client.emit('pubsub:error', { msg, err });
303
+ });
304
+ });
305
+ }
306
+ }
307
+ getTopicParts(topic) {
308
+ const isCombined = topic.includes('?');
309
+ const separator = isCombined ? '?' : '.';
310
+ const split = topic.split(separator);
311
+ const postfix = isCombined ? split[1] : split.splice(split.length - 1);
312
+ const prefix = isCombined ? split[0] : split.join('.');
313
+ let postfixes = [];
314
+ if (isCombined) {
315
+ postfixes = postfix.split('&');
316
+ }
317
+ else {
318
+ postfixes = postfix;
319
+ }
320
+ return { prefix, postfixes };
321
+ }
322
+ setTopicPriorities(priorities = {}) {
323
+ Object.keys(priorities).forEach(priority => {
324
+ const topicParts = this.getTopicParts(priority);
325
+ const oldPriorities = this.topicPriorities[topicParts.prefix];
326
+ const newPriority = priorities[priority];
327
+ if (oldPriorities) {
328
+ topicParts.postfixes.forEach(postfix => {
329
+ const oldPriority = oldPriorities[postfix];
330
+ if ((oldPriority && oldPriority < newPriority) || !oldPriority) {
331
+ oldPriorities[postfix] = newPriority;
332
+ }
333
+ });
334
+ }
335
+ else {
336
+ const newTopics = topicParts.postfixes.reduce((newTopics, p) => {
337
+ newTopics[p] = newPriority;
338
+ return newTopics;
339
+ }, {});
340
+ this.topicPriorities[topicParts.prefix] = newTopics;
341
+ }
342
+ });
343
+ }
344
+ async subscribe(topic, handler, immediate, priority) {
345
+ if (priority) {
346
+ this.setTopicPriorities({ [topic]: priority });
347
+ }
348
+ let promise;
349
+ if (!immediate) {
350
+ // let this and any other subscribe/unsubscribe calls roll in, then trigger a whole resubscribe
351
+ promise = this.debouncedResubscribe();
352
+ }
353
+ else {
354
+ promise = this.xmppSubscribe(topic);
355
+ }
356
+ if (handler) {
357
+ this.createSubscription(topic, handler);
358
+ }
359
+ else {
360
+ this.bulkSubscriptions[topic] = true;
361
+ }
362
+ const result = await promise;
363
+ // Assume topic subscription succeeded if promise is resolved...
364
+ let topicResult = { topic, state: 'Permitted' };
365
+ // ... but if partial bulk resubscribe is enabled, use topic's individual result from the API response.
366
+ if (this.enablePartialBulkResubscribe && result && typeof result === 'object' && isTopicSubscribeResult(result[topic])) {
367
+ topicResult = result[topic];
368
+ }
369
+ // Topic result other than state=Permitted becomes a StreamingSubscriptionError promise rejection.
370
+ if (topicResult.state !== 'Permitted') {
371
+ const message = topicResult.rejectionReason || `Failed to subscribe topic ${topic}`;
372
+ const missingPermissions = topicResult.missingPermissions;
373
+ throw new _1.StreamingSubscriptionError(message, topic, 'subscribe', { missingPermissions });
374
+ }
375
+ return topicResult;
376
+ }
377
+ /**
378
+ * Use `_subscribeInternal` when subscribing to a topic from within streaming-client itself.
379
+ * Internal subscriptions won't be overwritten by subscriptions from consumers and will be prioritized
380
+ * over subscriptions from consumers.
381
+ */
382
+ async _subscribeInternal(topic) {
383
+ if (this.internalSubscriptions.includes(topic)) {
384
+ return Promise.resolve();
385
+ }
386
+ this.setTopicPriorities({ [topic]: Number.MAX_VALUE });
387
+ const promise = this.xmppSubscribe(topic);
388
+ this.internalSubscriptions.push(topic);
389
+ this.bulkSubscriptions[topic] = true;
390
+ return promise;
391
+ }
392
+ unsubscribe(topic, handler, immediate) {
393
+ if (handler) {
394
+ this.removeSubscription(topic, handler);
395
+ }
396
+ else {
397
+ delete this.bulkSubscriptions[topic];
398
+ delete this.subscriptions[topic];
399
+ }
400
+ this.removeTopicPriority(topic);
401
+ if (!immediate) {
402
+ // let this and any other subscribe/unsubscribe calls roll in, then trigger a whole resubscribe
403
+ return this.debouncedResubscribe();
404
+ }
405
+ return this.xmppUnsubscribe(topic);
406
+ }
407
+ async bulkSubscribe(topics, options = { replace: false, force: false }, priorities = {}) {
408
+ var _a, _b;
409
+ this.setTopicPriorities(priorities);
410
+ let toSubscribe = mergeAndDedup(topics, this.internalSubscriptions);
411
+ if (options.replace && !options.force) {
412
+ // if this is a bulk subscription, but not a forcible one, keep all individual subscriptions
413
+ toSubscribe = mergeAndDedup(toSubscribe, this.getActiveIndividualTopics());
414
+ }
415
+ else if (options.force) {
416
+ // if it's a forcible bulk subscribe, wipe out individual subscriptions
417
+ this.subscriptions = {};
418
+ }
419
+ let response;
420
+ try {
421
+ response = await this.makeBulkSubscribeRequest(toSubscribe, options);
422
+ }
423
+ catch (err) {
424
+ // If this request was cancelled because a newer bulk subscribe superseded it,
425
+ // return an 'Unknown' state for all topics. The superseding request will carry
426
+ // the current desired topic list and handle the actual subscription.
427
+ if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes('Superseded by newer bulk subscribe request')) {
428
+ this.client.logger.debug('Bulk subscribe was superseded, deferring to newer request');
429
+ const result = {};
430
+ for (const topic of toSubscribe) {
431
+ result[topic] = { topic, state: 'Unknown' };
432
+ }
433
+ return result;
434
+ }
435
+ throw err;
436
+ }
437
+ let topicResponseEntities = [];
438
+ if (response && response.data && 'entities' in response.data && Array.isArray(response.data.entities)) {
439
+ topicResponseEntities = response.data.entities;
440
+ }
441
+ const result = {};
442
+ for (const topicEntity of topicResponseEntities) {
443
+ const { id, state, rejectionReason, missingPermissions } = topicEntity;
444
+ result[id] = { topic: id, state, rejectionReason, missingPermissions };
445
+ // If response entity is a combined topic ID like "a.b?c&d" include individualized topic IDs
446
+ // as keys in the map. This could either point to the same result as the combined topic ID
447
+ // or to a specific result for that individual topic if backend provides a specific result.
448
+ // Example: caller asked to subscribe "a.b?c&d" but user lacks permission for topic "a.b.d"
449
+ // In this case, API response will include "a.b?c&d" as success along with "a.b.d" as failure.
450
+ if (id.includes('?')) {
451
+ for (const individualTopic of (0, utils_1.splitIntoIndividualTopics)(id)) {
452
+ const hasIndividualTopicResult = result.hasOwnProperty(individualTopic);
453
+ // Only use the combined topic result for this individual topic ID if there isn't already
454
+ // a result for the individual topic itself. Exact topic result takes precedence.
455
+ if (!hasIndividualTopicResult) {
456
+ result[individualTopic] = result[id];
457
+ }
458
+ }
459
+ }
460
+ }
461
+ if (options.replace) {
462
+ this.bulkSubscriptions = {};
463
+ }
464
+ topics.forEach(topic => {
465
+ this.bulkSubscriptions[topic] = true;
466
+ });
467
+ this.internalSubscriptions.forEach(topic => {
468
+ this.bulkSubscriptions[topic] = true;
469
+ });
470
+ // Add a fallback result for any topic in the toSubscribe list that isn't already in result.
471
+ // With partial bulk resubscribe enabled missing result means "Unknown" state but when not
472
+ // enabled the fallback is "Permitted" for backward compatibility (success response means OK).
473
+ for (const topic of toSubscribe) {
474
+ (_b = result[topic]) !== null && _b !== void 0 ? _b : (result[topic] = { topic, state: this.enablePartialBulkResubscribe ? 'Unknown' : 'Permitted' });
475
+ }
476
+ return result;
477
+ }
478
+ get expose() {
479
+ return {
480
+ subscribe: this.subscribe.bind(this),
481
+ unsubscribe: this.unsubscribe.bind(this),
482
+ bulkSubscribe: this.bulkSubscribe.bind(this)
483
+ };
484
+ }
485
+ }
486
+ exports.Notifications = Notifications;
487
+ function isTopicSubscribeResult(value) {
488
+ let hasTopic = false;
489
+ let hasValidState = false;
490
+ if (value && typeof value === 'object') {
491
+ hasTopic = 'topic' in value && typeof value.topic === 'string';
492
+ hasValidState = 'state' in value && ['Permitted', 'Rejected', 'Unknown'].includes(value.state);
493
+ }
494
+ return hasTopic && hasValidState;
495
+ }
@@ -0,0 +1,21 @@
1
+ import { Client } from './client';
2
+ import { NamedAgent } from './types/named-agent';
3
+ export interface PingOptions {
4
+ pingInterval?: number;
5
+ failedPingsBeforeDisconnect?: number;
6
+ jid?: string;
7
+ }
8
+ export declare class Ping {
9
+ private client;
10
+ private stanzaInstance;
11
+ private options;
12
+ private pingInterval;
13
+ private failedPingsBeforeDisconnect;
14
+ private numberOfFailedPings;
15
+ private nextPingTimeoutId;
16
+ constructor(client: Client, stanzaInstance: NamedAgent, options?: PingOptions);
17
+ start(): void;
18
+ stop(): void;
19
+ private performPing;
20
+ private queueNextPing;
21
+ }
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Ping = void 0;
4
+ const DEFAULT_PING_INTERVAL = 14 * 1000;
5
+ const DEFAULT_MAXIMUM_FAILED_PINGS_BEFORE_DISCONNECT = 1;
6
+ class Ping {
7
+ constructor(client, stanzaInstance, options = {}) {
8
+ this.client = client;
9
+ this.stanzaInstance = stanzaInstance;
10
+ this.options = options;
11
+ this.pingInterval = options.pingInterval || DEFAULT_PING_INTERVAL;
12
+ this.failedPingsBeforeDisconnect = options.failedPingsBeforeDisconnect || DEFAULT_MAXIMUM_FAILED_PINGS_BEFORE_DISCONNECT;
13
+ this.numberOfFailedPings = 0;
14
+ this.nextPingTimeoutId = undefined;
15
+ this.start();
16
+ }
17
+ start() {
18
+ if (!this.nextPingTimeoutId) {
19
+ this.nextPingTimeoutId = -1;
20
+ this.queueNextPing();
21
+ }
22
+ }
23
+ stop() {
24
+ clearTimeout(this.nextPingTimeoutId);
25
+ this.nextPingTimeoutId = undefined;
26
+ this.numberOfFailedPings = 0;
27
+ }
28
+ async performPing() {
29
+ var _a;
30
+ try {
31
+ await this.stanzaInstance.ping(this.options.jid);
32
+ this.numberOfFailedPings = 0;
33
+ this.queueNextPing();
34
+ }
35
+ catch (err) {
36
+ const info = {
37
+ channelId: this.client.config.channelId,
38
+ jid: this.stanzaInstance.jid,
39
+ stanzaInstanceId: this.stanzaInstance.id
40
+ };
41
+ this.client.logger.warn('Missed a ping.', Object.assign({ error: err }, info));
42
+ /* if we have reached max number of missed pings, disconnect */
43
+ if (++this.numberOfFailedPings > this.failedPingsBeforeDisconnect) {
44
+ this.client.logger.error('Missed too many pings, disconnecting', Object.assign({ numberOfFailedPings: this.numberOfFailedPings }, info));
45
+ this.stanzaInstance.sendStreamError({ text: 'too many missed pongs', condition: 'connection-timeout' });
46
+ this.stop();
47
+ (_a = this.stanzaInstance.transport) === null || _a === void 0 ? void 0 : _a.disconnect(false);
48
+ }
49
+ else {
50
+ this.queueNextPing();
51
+ }
52
+ }
53
+ }
54
+ queueNextPing() {
55
+ if (this.nextPingTimeoutId) {
56
+ this.nextPingTimeoutId = setTimeout(this.performPing.bind(this), this.pingInterval);
57
+ }
58
+ }
59
+ }
60
+ exports.Ping = Ping;
File without changes
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /* istanbul ignore file */
3
+ if (!global) {
4
+ // @ts-ignore
5
+ Object.defineProperty(window, 'crypto', { value: window.crypto || window.msCrypto, writable: false, configurable: false });
6
+ }
@@ -0,0 +1,16 @@
1
+ import { Client } from './client';
2
+ import { NamedAgent } from './types/named-agent';
3
+ export interface ServerMonitorOptions {
4
+ stanzaTimeout?: number;
5
+ }
6
+ export declare class ServerMonitor {
7
+ private client;
8
+ private stanzaInstance;
9
+ private stanzaTimeout;
10
+ private timeoutId?;
11
+ private boundSetupStanzaTimeout?;
12
+ constructor(client: Client, stanzaInstance: NamedAgent, options?: ServerMonitorOptions);
13
+ private start;
14
+ stop(): void;
15
+ private setupStanzaTimeout;
16
+ }
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServerMonitor = void 0;
4
+ const DEFAULT_STANZA_TIMEOUT = 35 * 1000;
5
+ class ServerMonitor {
6
+ constructor(client, stanzaInstance, options = {}) {
7
+ this.client = client;
8
+ this.stanzaInstance = stanzaInstance;
9
+ this.stanzaTimeout = options.stanzaTimeout || DEFAULT_STANZA_TIMEOUT;
10
+ this.timeoutId = undefined;
11
+ this.start();
12
+ }
13
+ start() {
14
+ this.boundSetupStanzaTimeout = this.setupStanzaTimeout.bind(this);
15
+ this.client.on('connected', this.boundSetupStanzaTimeout);
16
+ this.stanzaInstance.on('raw:incoming', this.boundSetupStanzaTimeout);
17
+ }
18
+ stop() {
19
+ clearTimeout(this.timeoutId);
20
+ this.timeoutId = undefined;
21
+ if (this.boundSetupStanzaTimeout) {
22
+ this.client.off('connected', this.boundSetupStanzaTimeout);
23
+ this.stanzaInstance.off('raw:incoming', this.boundSetupStanzaTimeout);
24
+ this.boundSetupStanzaTimeout = undefined;
25
+ }
26
+ }
27
+ setupStanzaTimeout() {
28
+ clearTimeout(this.timeoutId);
29
+ this.timeoutId = setTimeout(() => {
30
+ var _a;
31
+ const info = {
32
+ channelId: this.client.config.channelId,
33
+ jid: this.stanzaInstance.jid,
34
+ stanzaInstanceId: this.stanzaInstance.id,
35
+ timeout: this.stanzaTimeout
36
+ };
37
+ this.client.logger.error('Time between XMPP stanzas exceeded timeout, disconnecting', info);
38
+ this.stanzaInstance.sendStreamError({ text: 'time between stanzas exceeded timeout', condition: 'connection-timeout' });
39
+ this.stop();
40
+ (_a = this.stanzaInstance.transport) === null || _a === void 0 ? void 0 : _a.disconnect(false);
41
+ }, this.stanzaTimeout);
42
+ }
43
+ }
44
+ exports.ServerMonitor = ServerMonitor;
@@ -0,0 +1,29 @@
1
+ import { DefinitionOptions } from 'stanza/jxt';
2
+ import { Stanzas } from 'stanza';
3
+ import { GenesysMediaMessage, GenesysWebrtcJsonRpcMessage } from '../types/interfaces';
4
+ export interface Propose {
5
+ sessionId: string;
6
+ conversationId: string;
7
+ autoAnswer: boolean;
8
+ persistentConversationId?: string;
9
+ originalRoomJid?: string;
10
+ fromUserId?: string;
11
+ sdpOverXmpp?: boolean;
12
+ privAnswerMode?: 'Auto';
13
+ }
14
+ declare module 'stanza/protocol' {
15
+ interface IQPayload {
16
+ genesysWebrtc?: GenesysWebrtcJsonRpcMessage;
17
+ }
18
+ interface ReceivedMessage {
19
+ mediaMessage?: GenesysMediaMessage;
20
+ }
21
+ }
22
+ declare module 'stanza' {
23
+ interface AgentEvents {
24
+ 'iq:set:genesysWebrtc': Stanzas.ReceivedIQ & {
25
+ genesysWebrtc: GenesysWebrtcJsonRpcMessage;
26
+ };
27
+ }
28
+ }
29
+ export declare const definitions: DefinitionOptions<any>[];