genesys-cloud-streaming-client 19.5.1-develop.15 → 19.6.0-release.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 +15 -12
- package/dist/cjs/alerting-leader.d.ts +14 -0
- package/dist/cjs/alerting-leader.js +51 -0
- package/dist/cjs/client.d.ts +12 -0
- package/dist/cjs/client.js +76 -15
- package/dist/cjs/notifications.d.ts +7 -0
- package/dist/cjs/notifications.js +25 -2
- package/dist/cjs/types/interfaces.d.ts +6 -0
- package/dist/cjs/types/interfaces.js +5 -1
- package/dist/deploy-info.json +5 -5
- package/dist/es/alerting-leader.d.ts +14 -0
- package/dist/es/alerting-leader.js +50 -0
- package/dist/es/client.d.ts +12 -0
- package/dist/es/client.js +78 -15
- package/dist/es/index.bundle.js +1504 -919
- package/dist/es/notifications.d.ts +7 -0
- package/dist/es/notifications.js +27 -2
- package/dist/es/types/interfaces.d.ts +6 -0
- package/dist/es/types/interfaces.js +4 -0
- package/dist/manifest.json +5 -5
- package/dist/npm/CHANGELOG.md +15 -1
- package/dist/npm/alerting-leader.d.ts +14 -0
- package/dist/npm/alerting-leader.js +51 -0
- package/dist/npm/client.d.ts +12 -0
- package/dist/npm/client.js +76 -15
- package/dist/npm/notifications.d.ts +7 -0
- package/dist/npm/notifications.js +25 -2
- package/dist/npm/types/interfaces.d.ts +6 -0
- package/dist/npm/types/interfaces.js +5 -1
- package/dist/streaming-client.browser.js +1 -1
- package/dist/streaming-client.browser.js.LICENSE.txt +1 -1
- package/dist/v19/streaming-client.browser.js +1 -1
- package/dist/v19/streaming-client.browser.js.LICENSE.txt +1 -1
- package/dist/v19.6.0/streaming-client.browser.js +2 -0
- package/dist/{v19.5.1 → v19.6.0}/streaming-client.browser.js.LICENSE.txt +1 -1
- package/package.json +3 -3
- package/dist/v19.5.1/streaming-client.browser.js +0 -2
package/README.md
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
## Streaming Client
|
|
2
|
+
[](https://github.com/purecloudlabs/genesys-cloud-streaming-client/actions/workflows/matrix.yml)
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
### Overview
|
|
5
|
+
Client library for streaming-service
|
|
4
6
|
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
+
### Installation
|
|
8
|
+
Run `npm install` in order to install all the dependencies
|
|
7
9
|
|
|
8
|
-
###
|
|
10
|
+
### Testing
|
|
11
|
+
Run the tests using `npm test` in the command line
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
**In order to see code coverage run `npm run test:coverage`**
|
|
11
14
|
|
|
12
|
-
###
|
|
15
|
+
### Build for Local Use
|
|
16
|
+
Run the script `npm run build`
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
### Linting and Style
|
|
19
|
+
semistandard has been added and you can run linting through the command line via `npm run lint` script
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
To fix minor styling errors, run `npm run lint:fix`
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
* Client Signaling and Streaming Team (STREAM).
|
|
23
|
+
**If you can configure you editor to run linting while typing or on save this is preferrable**
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IClientOptions, StreamingClientExtension } from './types/interfaces';
|
|
2
|
+
import { Client } from './client';
|
|
3
|
+
import { NamedAgent } from './types/named-agent';
|
|
4
|
+
export declare class AlertingLeaderExtension implements StreamingClientExtension {
|
|
5
|
+
private client;
|
|
6
|
+
private connectionId?;
|
|
7
|
+
private alertableInteractionTypes;
|
|
8
|
+
constructor(client: Client, options: IClientOptions);
|
|
9
|
+
handleStanzaInstanceChange(stanzaInstance: NamedAgent): void;
|
|
10
|
+
private markAsAlertable;
|
|
11
|
+
get expose(): AlertingLeaderApi;
|
|
12
|
+
}
|
|
13
|
+
export interface AlertingLeaderApi {
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AlertingLeaderExtension = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
class AlertingLeaderExtension {
|
|
6
|
+
constructor(client, options) {
|
|
7
|
+
var _a;
|
|
8
|
+
this.client = client;
|
|
9
|
+
this.alertableInteractionTypes = (_a = options.alertableInteractionTypes) !== null && _a !== void 0 ? _a : [];
|
|
10
|
+
}
|
|
11
|
+
handleStanzaInstanceChange(stanzaInstance) {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
this.connectionId = (_b = (_a = stanzaInstance.transport) === null || _a === void 0 ? void 0 : _a.stream) === null || _b === void 0 ? void 0 : _b.id;
|
|
14
|
+
if (this.alertableInteractionTypes.length !== 0) {
|
|
15
|
+
this.markAsAlertable();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async markAsAlertable() {
|
|
19
|
+
const userId = this.client.config.userId;
|
|
20
|
+
const connectionsRequestOptions = {
|
|
21
|
+
method: 'patch',
|
|
22
|
+
host: this.client.config.apiHost,
|
|
23
|
+
authToken: this.client.config.authToken,
|
|
24
|
+
logger: this.client.logger,
|
|
25
|
+
data: {
|
|
26
|
+
alertable: true
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
// STREAM-1204
|
|
30
|
+
// There's a race condition between the backend service knowing about the connection
|
|
31
|
+
// and us marking the connection as alertable. For now, we'll just retry with some delay.
|
|
32
|
+
const maxRetries = 16;
|
|
33
|
+
let retryCount = 0;
|
|
34
|
+
const retry = (0, utils_1.retryPromise)(() => this.client.http.requestApi(`apps/users/${userId}/connections/${this.connectionId}`, connectionsRequestOptions), () => {
|
|
35
|
+
retryCount++;
|
|
36
|
+
if (retryCount >= maxRetries) {
|
|
37
|
+
this.client.logger.info('Max retries reached for marking connection as alertable');
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}, 500, this.client.logger);
|
|
42
|
+
return retry.promise
|
|
43
|
+
.catch(() => {
|
|
44
|
+
this.client.logger.warn('Could not mark this connection as alertable; this client may not alert for incoming interactions');
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
get expose() {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.AlertingLeaderExtension = AlertingLeaderExtension;
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Logger } from 'genesys-cloud-client-logger';
|
|
3
3
|
import './polyfills';
|
|
4
|
+
import { AlertingLeaderExtension, AlertingLeaderApi } from './alerting-leader';
|
|
4
5
|
import { Notifications, NotificationsAPI } from './notifications';
|
|
5
6
|
import { WebrtcExtension, WebrtcExtensionAPI } from './webrtc';
|
|
6
7
|
import { Ping } from './ping';
|
|
@@ -39,6 +40,8 @@ export declare class Client extends EventEmitter {
|
|
|
39
40
|
_webrtcSessions: WebrtcExtension;
|
|
40
41
|
messenger: MessengerExtension;
|
|
41
42
|
_messenger: MessengerExtensionApi;
|
|
43
|
+
alertingLeader: AlertingLeaderApi;
|
|
44
|
+
_alertingLeader: AlertingLeaderExtension;
|
|
42
45
|
_ping: Ping;
|
|
43
46
|
constructor(options: IClientOptions);
|
|
44
47
|
private handleSendEventFromExtension;
|
|
@@ -60,6 +63,15 @@ export declare class Client extends EventEmitter {
|
|
|
60
63
|
private backoffConnectRetryHandler;
|
|
61
64
|
private networkErrorNeedsAuth;
|
|
62
65
|
private saslErrorIsRetryable;
|
|
66
|
+
/**
|
|
67
|
+
* Performs an active network connectivity check by querying the API.
|
|
68
|
+
* navigator.onLine is unreliable (VPNs, virtual adapters, etc.), so we
|
|
69
|
+
* actually reach out to verify we can talk to the server.
|
|
70
|
+
*
|
|
71
|
+
* Returns true if connectivity is confirmed, false otherwise.
|
|
72
|
+
* This is advisory only — it does not gate connection attempts.
|
|
73
|
+
*/
|
|
74
|
+
checkNetworkConnectivity(): Promise<boolean>;
|
|
63
75
|
private makeConnectionAttempt;
|
|
64
76
|
private setupConnectionMonitoring;
|
|
65
77
|
private prepareForConnect;
|
package/dist/cjs/client.js
CHANGED
|
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const limiter_1 = require("limiter");
|
|
6
6
|
const genesys_cloud_client_logger_1 = require("genesys-cloud-client-logger");
|
|
7
7
|
require("./polyfills");
|
|
8
|
+
const alerting_leader_1 = require("./alerting-leader");
|
|
8
9
|
const notifications_1 = require("./notifications");
|
|
9
10
|
const webrtc_1 = require("./webrtc");
|
|
10
11
|
const ping_1 = require("./ping");
|
|
@@ -15,7 +16,6 @@ const interfaces_1 = require("./types/interfaces");
|
|
|
15
16
|
const events_1 = tslib_1.__importDefault(require("events"));
|
|
16
17
|
const connection_manager_1 = require("./connection-manager");
|
|
17
18
|
const exponential_backoff_1 = require("exponential-backoff");
|
|
18
|
-
const offline_error_1 = tslib_1.__importDefault(require("./types/offline-error"));
|
|
19
19
|
const sasl_error_1 = tslib_1.__importDefault(require("./types/sasl-error"));
|
|
20
20
|
const timeout_error_1 = require("./types/timeout-error");
|
|
21
21
|
const messenger_1 = require("./messenger");
|
|
@@ -25,7 +25,8 @@ const user_cancelled_error_1 = tslib_1.__importDefault(require("./types/user-can
|
|
|
25
25
|
const extensions = {
|
|
26
26
|
notifications: notifications_1.Notifications,
|
|
27
27
|
webrtcSessions: webrtc_1.WebrtcExtension,
|
|
28
|
-
messenger: messenger_1.MessengerExtension
|
|
28
|
+
messenger: messenger_1.MessengerExtension,
|
|
29
|
+
alertingLeader: alerting_leader_1.AlertingLeaderExtension
|
|
29
30
|
};
|
|
30
31
|
const STANZA_DISCONNECTED = 'stanzaDisconnected';
|
|
31
32
|
const NO_LONGER_SUBSCRIBED = 'notify:no_longer_subscribed';
|
|
@@ -198,7 +199,18 @@ class Client extends events_1.default {
|
|
|
198
199
|
this.activeStanzaInstance = undefined;
|
|
199
200
|
this.emit('disconnected', { reconnecting: this.autoReconnect });
|
|
200
201
|
if (this.autoReconnect) {
|
|
201
|
-
return this.connect({ keepTryingOnFailure: true })
|
|
202
|
+
return this.connect({ keepTryingOnFailure: true })
|
|
203
|
+
.catch(error => {
|
|
204
|
+
this.logger.error('Failed to auto reconnect', {
|
|
205
|
+
keepTryingOnFailure: true,
|
|
206
|
+
stanzaInstanceId: disconnectedInstance.id,
|
|
207
|
+
channelId: disconnectedInstance.channelId
|
|
208
|
+
});
|
|
209
|
+
this.emit('disconnected', {
|
|
210
|
+
error,
|
|
211
|
+
reconnecting: false
|
|
212
|
+
});
|
|
213
|
+
});
|
|
202
214
|
}
|
|
203
215
|
}
|
|
204
216
|
handleNoLongerSubscribed(stanzaInstance) {
|
|
@@ -453,14 +465,14 @@ class Client extends events_1.default {
|
|
|
453
465
|
// retry after comes in seconds, we need to return milliseconds
|
|
454
466
|
const retryDelay = parseInt(retryAfter, 10) * 1000;
|
|
455
467
|
additionalErrorDetails.retryDelay = retryDelay;
|
|
456
|
-
this.logger.error('Failed streaming client connection attempt, respecting retry-after header and will retry afterwards.', additionalErrorDetails
|
|
468
|
+
this.logger.error('Failed streaming client connection attempt, respecting retry-after header and will retry afterwards.', additionalErrorDetails);
|
|
457
469
|
await (0, utils_1.delay)(retryDelay);
|
|
458
470
|
this.logger.debug('finished waiting for retry-after');
|
|
459
471
|
return true;
|
|
460
472
|
}
|
|
461
473
|
}
|
|
462
474
|
const connectionData = this.increaseBackoff();
|
|
463
|
-
this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails
|
|
475
|
+
this.logger.error('Failed streaming client connection attempt, retrying', additionalErrorDetails);
|
|
464
476
|
this.logger.debug('debug: retry info', { expectedRetryInMs: connectionData.currentDelayMs, appName: this.config.appName, clientId: this.logger.clientId });
|
|
465
477
|
return true;
|
|
466
478
|
}
|
|
@@ -472,14 +484,57 @@ class Client extends events_1.default {
|
|
|
472
484
|
const retryConditions = ['encryption-required', 'incorrect-encoding', 'invalid-mechanism', 'malformed-request', 'mechanism-too-weak'];
|
|
473
485
|
return retryConditions.includes(error.condition);
|
|
474
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Performs an active network connectivity check by querying the API.
|
|
489
|
+
* navigator.onLine is unreliable (VPNs, virtual adapters, etc.), so we
|
|
490
|
+
* actually reach out to verify we can talk to the server.
|
|
491
|
+
*
|
|
492
|
+
* Returns true if connectivity is confirmed, false otherwise.
|
|
493
|
+
* This is advisory only — it does not gate connection attempts.
|
|
494
|
+
*/
|
|
495
|
+
async checkNetworkConnectivity() {
|
|
496
|
+
// Quick hint check first — if the browser says offline, that's a strong signal
|
|
497
|
+
if (!navigator.onLine) {
|
|
498
|
+
this.logger.warn('navigator.onLine reports offline — connectivity may be unavailable');
|
|
499
|
+
this.emit('networkConnectivityWarning', { reason: 'navigator.onLine is false' });
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
// JWT-based connections (e.g. background assistants) don't have an auth token
|
|
503
|
+
// that works with the users/me endpoint, so we can only rely on navigator.onLine
|
|
504
|
+
if (this.config.jwt && !this.config.authToken) {
|
|
505
|
+
this.logger.debug('Skipping active connectivity check in JWT mode, relying on navigator.onLine');
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const opts = {
|
|
510
|
+
method: 'get',
|
|
511
|
+
host: this.config.apiHost,
|
|
512
|
+
authToken: this.config.authToken,
|
|
513
|
+
logger: this.logger,
|
|
514
|
+
requestTimeout: 10000
|
|
515
|
+
};
|
|
516
|
+
await this.http.requestApi('users/me', opts);
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
catch (err) {
|
|
520
|
+
this.logger.warn('Active network connectivity check failed — connectivity may be unavailable', { error: err });
|
|
521
|
+
this.emit('networkConnectivityWarning', { reason: 'active connectivity check failed', error: err });
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
475
525
|
async makeConnectionAttempt() {
|
|
476
526
|
var _a, _b;
|
|
477
527
|
if (this.cancelConnectionAttempt) {
|
|
478
528
|
throw new user_cancelled_error_1.default('Connection attempt cancelled');
|
|
479
529
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
530
|
+
// navigator.onLine is unreliable — use it as a hint, not a gate.
|
|
531
|
+
// Fire off an active connectivity check in the background. It will
|
|
532
|
+
// log and emit warnings if there's an issue, but we don't wait for it.
|
|
533
|
+
this.checkNetworkConnectivity().then(isConnected => {
|
|
534
|
+
if (!isConnected) {
|
|
535
|
+
this.logger.warn('Network connectivity check failed, but proceeding with connection attempt anyway');
|
|
536
|
+
}
|
|
537
|
+
});
|
|
483
538
|
let stanzaInstance;
|
|
484
539
|
const previousConnectingState = this.connecting;
|
|
485
540
|
try {
|
|
@@ -553,9 +608,9 @@ class Client extends events_1.default {
|
|
|
553
608
|
}
|
|
554
609
|
}
|
|
555
610
|
if (this.hardReconnectRequired) {
|
|
556
|
-
let
|
|
557
|
-
if (this.config.jid) {
|
|
558
|
-
|
|
611
|
+
let userPromise;
|
|
612
|
+
if (this.config.userId && this.config.jid) {
|
|
613
|
+
userPromise = Promise.resolve({ userId: this.config.userId, jid: this.config.jid });
|
|
559
614
|
}
|
|
560
615
|
else {
|
|
561
616
|
const jidRequestOpts = {
|
|
@@ -564,8 +619,13 @@ class Client extends events_1.default {
|
|
|
564
619
|
authToken: this.config.authToken,
|
|
565
620
|
logger: this.logger
|
|
566
621
|
};
|
|
567
|
-
|
|
568
|
-
.then(res =>
|
|
622
|
+
userPromise = this.http.requestApi('users/me', jidRequestOpts)
|
|
623
|
+
.then(res => {
|
|
624
|
+
return {
|
|
625
|
+
userId: res.data.id,
|
|
626
|
+
jid: res.data.chat.jabberId
|
|
627
|
+
};
|
|
628
|
+
});
|
|
569
629
|
}
|
|
570
630
|
// If no jidResource is provided, generate a random one to maintain ourselves.
|
|
571
631
|
this.jidResource = this.config.jidResource || (0, uuid_1.v4)();
|
|
@@ -577,7 +637,8 @@ class Client extends events_1.default {
|
|
|
577
637
|
};
|
|
578
638
|
const channelPromise = await this.http.requestApi('notifications/channels?connectionType=streaming', channelRequestOpts)
|
|
579
639
|
.then(res => res.data.id);
|
|
580
|
-
const [jid, channelId] = await Promise.all([
|
|
640
|
+
const [{ userId, jid }, channelId] = await Promise.all([userPromise, channelPromise]);
|
|
641
|
+
this.config.userId = userId;
|
|
581
642
|
this.config.jid = jid;
|
|
582
643
|
this.config.jidResource = this.jidResource;
|
|
583
644
|
this.config.channelId = channelId;
|
|
@@ -609,7 +670,7 @@ class Client extends events_1.default {
|
|
|
609
670
|
return Client.version;
|
|
610
671
|
}
|
|
611
672
|
static get version() {
|
|
612
|
-
return '19.
|
|
673
|
+
return '19.6.0';
|
|
613
674
|
}
|
|
614
675
|
}
|
|
615
676
|
exports.Client = Client;
|
|
@@ -11,6 +11,7 @@ export declare class Notifications implements StreamingClientExtension {
|
|
|
11
11
|
topicPriorities: any;
|
|
12
12
|
debouncedResubscribe: () => Promise<BulkSubscribeResult>;
|
|
13
13
|
enablePartialBulkResubscribe: boolean;
|
|
14
|
+
private internalSubscriptions;
|
|
14
15
|
constructor(client: any, options?: IClientOptions);
|
|
15
16
|
get pubsubHost(): string;
|
|
16
17
|
handleStanzaInstanceChange(stanza: NamedAgent): void;
|
|
@@ -47,6 +48,12 @@ export declare class Notifications implements StreamingClientExtension {
|
|
|
47
48
|
};
|
|
48
49
|
setTopicPriorities(priorities?: {}): void;
|
|
49
50
|
subscribe(topic: string, handler?: (..._: any[]) => void, immediate?: boolean, priority?: number): Promise<TopicSubscribeResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Use `_subscribeInternal` when subscribing to a topic from within streaming-client itself.
|
|
53
|
+
* Internal subscriptions won't be overwritten by subscriptions from consumers and will be prioritized
|
|
54
|
+
* over subscriptions from consumers.
|
|
55
|
+
*/
|
|
56
|
+
_subscribeInternal(topic: string): Promise<PubsubSubscriptionWithOptions | void>;
|
|
50
57
|
unsubscribe(topic: string, handler?: (..._: any[]) => void, immediate?: boolean): Promise<any>;
|
|
51
58
|
bulkSubscribe(topics: string[], options?: BulkSubscribeOpts, priorities?: {
|
|
52
59
|
[topicName: string]: number;
|
|
@@ -18,6 +18,7 @@ class Notifications {
|
|
|
18
18
|
this.subscriptions = {};
|
|
19
19
|
this.bulkSubscriptions = {};
|
|
20
20
|
this.topicPriorities = {};
|
|
21
|
+
this.internalSubscriptions = [];
|
|
21
22
|
this.client = client;
|
|
22
23
|
this.enablePartialBulkResubscribe = (_a = options === null || options === void 0 ? void 0 : options.enablePartialBulkResubscribe) !== null && _a !== void 0 ? _a : false;
|
|
23
24
|
client.on('pubsub:event', this.pubsubEvent.bind(this));
|
|
@@ -41,7 +42,11 @@ class Notifications {
|
|
|
41
42
|
this.stanzaInstance = stanza;
|
|
42
43
|
if (needsToResub) {
|
|
43
44
|
this.client.logger.info('resubscribing due to hard reconnect');
|
|
44
|
-
|
|
45
|
+
this.debouncedResubscribe().catch((err) => {
|
|
46
|
+
const msg = 'Error resubscribing to topics';
|
|
47
|
+
this.client.logger.error(msg, err);
|
|
48
|
+
this.client.emit('pubsub:error', { msg, err });
|
|
49
|
+
});
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
topicHandlers(topic) {
|
|
@@ -330,6 +335,21 @@ class Notifications {
|
|
|
330
335
|
}
|
|
331
336
|
return topicResult;
|
|
332
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Use `_subscribeInternal` when subscribing to a topic from within streaming-client itself.
|
|
340
|
+
* Internal subscriptions won't be overwritten by subscriptions from consumers and will be prioritized
|
|
341
|
+
* over subscriptions from consumers.
|
|
342
|
+
*/
|
|
343
|
+
async _subscribeInternal(topic) {
|
|
344
|
+
if (this.internalSubscriptions.includes(topic)) {
|
|
345
|
+
return Promise.resolve();
|
|
346
|
+
}
|
|
347
|
+
this.setTopicPriorities({ [topic]: Number.MAX_VALUE });
|
|
348
|
+
const promise = this.xmppSubscribe(topic);
|
|
349
|
+
this.internalSubscriptions.push(topic);
|
|
350
|
+
this.bulkSubscriptions[topic] = true;
|
|
351
|
+
return promise;
|
|
352
|
+
}
|
|
333
353
|
unsubscribe(topic, handler, immediate) {
|
|
334
354
|
if (handler) {
|
|
335
355
|
this.removeSubscription(topic, handler);
|
|
@@ -348,7 +368,7 @@ class Notifications {
|
|
|
348
368
|
async bulkSubscribe(topics, options = { replace: false, force: false }, priorities = {}) {
|
|
349
369
|
var _a;
|
|
350
370
|
this.setTopicPriorities(priorities);
|
|
351
|
-
let toSubscribe = mergeAndDedup(topics,
|
|
371
|
+
let toSubscribe = mergeAndDedup(topics, this.internalSubscriptions);
|
|
352
372
|
if (options.replace && !options.force) {
|
|
353
373
|
// if this is a bulk subscription, but not a forcible one, keep all individual subscriptions
|
|
354
374
|
toSubscribe = mergeAndDedup(toSubscribe, this.getActiveIndividualTopics());
|
|
@@ -388,6 +408,9 @@ class Notifications {
|
|
|
388
408
|
topics.forEach(topic => {
|
|
389
409
|
this.bulkSubscriptions[topic] = true;
|
|
390
410
|
});
|
|
411
|
+
this.internalSubscriptions.forEach(topic => {
|
|
412
|
+
this.bulkSubscriptions[topic] = true;
|
|
413
|
+
});
|
|
391
414
|
// Add a fallback result for any topic in the toSubscribe list that isn't already in result.
|
|
392
415
|
// With partial bulk resubscribe enabled missing result means "Unknown" state but when not
|
|
393
416
|
// enabled the fallback is "Permitted" for backward compatibility (success response means OK).
|
|
@@ -24,15 +24,21 @@ export interface IClientOptions {
|
|
|
24
24
|
customHeaders?: ICustomHeader;
|
|
25
25
|
/** Allow bulk topic resubscribe to succeed or fail per-topic rather than all or nothing */
|
|
26
26
|
enablePartialBulkResubscribe?: boolean;
|
|
27
|
+
/** Genesys internal use only - non-Genesys apps that pass in `alertableInteractionTypes` may experience unexpected behavior */
|
|
28
|
+
alertableInteractionTypes?: AlertableInteractionTypes[];
|
|
27
29
|
}
|
|
28
30
|
export interface ICustomHeader {
|
|
29
31
|
[header: string]: string;
|
|
30
32
|
}
|
|
33
|
+
export declare enum AlertableInteractionTypes {
|
|
34
|
+
voice = "voice"
|
|
35
|
+
}
|
|
31
36
|
export interface IClientConfig {
|
|
32
37
|
host: string;
|
|
33
38
|
apiHost: string;
|
|
34
39
|
authToken?: string;
|
|
35
40
|
jwt?: string;
|
|
41
|
+
userId?: string;
|
|
36
42
|
jid?: string;
|
|
37
43
|
jidResource?: string;
|
|
38
44
|
channelId: string;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SessionTypes = exports.StreamingClientErrorTypes = void 0;
|
|
3
|
+
exports.SessionTypes = exports.StreamingClientErrorTypes = exports.AlertableInteractionTypes = void 0;
|
|
4
|
+
var AlertableInteractionTypes;
|
|
5
|
+
(function (AlertableInteractionTypes) {
|
|
6
|
+
AlertableInteractionTypes["voice"] = "voice";
|
|
7
|
+
})(AlertableInteractionTypes = exports.AlertableInteractionTypes || (exports.AlertableInteractionTypes = {}));
|
|
4
8
|
var StreamingClientErrorTypes;
|
|
5
9
|
(function (StreamingClientErrorTypes) {
|
|
6
10
|
StreamingClientErrorTypes["generic"] = "generic";
|
package/dist/deploy-info.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
3
|
-
"build": "
|
|
4
|
-
"buildDate": "2026-
|
|
2
|
+
"version": "release/v19.6.0",
|
|
3
|
+
"build": "1",
|
|
4
|
+
"buildDate": "2026-04-14T21:56:54.192Z",
|
|
5
5
|
"indexFiles": [
|
|
6
6
|
{
|
|
7
|
-
"file": "v19.
|
|
7
|
+
"file": "v19.6.0/streaming-client.browser.js"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
|
-
"file": "v19.
|
|
10
|
+
"file": "v19.6.0/streaming-client.browser.js.LICENSE.txt"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"file": "v19/streaming-client.browser.js"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IClientOptions, StreamingClientExtension } from './types/interfaces';
|
|
2
|
+
import { Client } from './client';
|
|
3
|
+
import { NamedAgent } from './types/named-agent';
|
|
4
|
+
export declare class AlertingLeaderExtension implements StreamingClientExtension {
|
|
5
|
+
private client;
|
|
6
|
+
private connectionId?;
|
|
7
|
+
private alertableInteractionTypes;
|
|
8
|
+
constructor(client: Client, options: IClientOptions);
|
|
9
|
+
handleStanzaInstanceChange(stanzaInstance: NamedAgent): void;
|
|
10
|
+
private markAsAlertable;
|
|
11
|
+
get expose(): AlertingLeaderApi;
|
|
12
|
+
}
|
|
13
|
+
export interface AlertingLeaderApi {
|
|
14
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { retryPromise } from './utils';
|
|
3
|
+
export class AlertingLeaderExtension {
|
|
4
|
+
constructor(client, options) {
|
|
5
|
+
var _a;
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.alertableInteractionTypes = (_a = options.alertableInteractionTypes) !== null && _a !== void 0 ? _a : [];
|
|
8
|
+
}
|
|
9
|
+
handleStanzaInstanceChange(stanzaInstance) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
this.connectionId = (_b = (_a = stanzaInstance.transport) === null || _a === void 0 ? void 0 : _a.stream) === null || _b === void 0 ? void 0 : _b.id;
|
|
12
|
+
if (this.alertableInteractionTypes.length !== 0) {
|
|
13
|
+
this.markAsAlertable();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
markAsAlertable() {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
const userId = this.client.config.userId;
|
|
19
|
+
const connectionsRequestOptions = {
|
|
20
|
+
method: 'patch',
|
|
21
|
+
host: this.client.config.apiHost,
|
|
22
|
+
authToken: this.client.config.authToken,
|
|
23
|
+
logger: this.client.logger,
|
|
24
|
+
data: {
|
|
25
|
+
alertable: true
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
// STREAM-1204
|
|
29
|
+
// There's a race condition between the backend service knowing about the connection
|
|
30
|
+
// and us marking the connection as alertable. For now, we'll just retry with some delay.
|
|
31
|
+
const maxRetries = 16;
|
|
32
|
+
let retryCount = 0;
|
|
33
|
+
const retry = retryPromise(() => this.client.http.requestApi(`apps/users/${userId}/connections/${this.connectionId}`, connectionsRequestOptions), () => {
|
|
34
|
+
retryCount++;
|
|
35
|
+
if (retryCount >= maxRetries) {
|
|
36
|
+
this.client.logger.info('Max retries reached for marking connection as alertable');
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}, 500, this.client.logger);
|
|
41
|
+
return retry.promise
|
|
42
|
+
.catch(() => {
|
|
43
|
+
this.client.logger.warn('Could not mark this connection as alertable; this client may not alert for incoming interactions');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
get expose() {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/es/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Logger } from 'genesys-cloud-client-logger';
|
|
3
3
|
import './polyfills';
|
|
4
|
+
import { AlertingLeaderExtension, AlertingLeaderApi } from './alerting-leader';
|
|
4
5
|
import { Notifications, NotificationsAPI } from './notifications';
|
|
5
6
|
import { WebrtcExtension, WebrtcExtensionAPI } from './webrtc';
|
|
6
7
|
import { Ping } from './ping';
|
|
@@ -39,6 +40,8 @@ export declare class Client extends EventEmitter {
|
|
|
39
40
|
_webrtcSessions: WebrtcExtension;
|
|
40
41
|
messenger: MessengerExtension;
|
|
41
42
|
_messenger: MessengerExtensionApi;
|
|
43
|
+
alertingLeader: AlertingLeaderApi;
|
|
44
|
+
_alertingLeader: AlertingLeaderExtension;
|
|
42
45
|
_ping: Ping;
|
|
43
46
|
constructor(options: IClientOptions);
|
|
44
47
|
private handleSendEventFromExtension;
|
|
@@ -60,6 +63,15 @@ export declare class Client extends EventEmitter {
|
|
|
60
63
|
private backoffConnectRetryHandler;
|
|
61
64
|
private networkErrorNeedsAuth;
|
|
62
65
|
private saslErrorIsRetryable;
|
|
66
|
+
/**
|
|
67
|
+
* Performs an active network connectivity check by querying the API.
|
|
68
|
+
* navigator.onLine is unreliable (VPNs, virtual adapters, etc.), so we
|
|
69
|
+
* actually reach out to verify we can talk to the server.
|
|
70
|
+
*
|
|
71
|
+
* Returns true if connectivity is confirmed, false otherwise.
|
|
72
|
+
* This is advisory only — it does not gate connection attempts.
|
|
73
|
+
*/
|
|
74
|
+
checkNetworkConnectivity(): Promise<boolean>;
|
|
63
75
|
private makeConnectionAttempt;
|
|
64
76
|
private setupConnectionMonitoring;
|
|
65
77
|
private prepareForConnect;
|