@exotel-npm-dev/exotel-ai-assist 1.0.2 → 1.0.3
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/dist/controller/index.d.ts +5 -5
- package/dist/controller/index.js +54 -43
- package/dist/index.js +54 -43
- package/dist/react/index.js +54 -43
- package/package.json +1 -1
|
@@ -4,11 +4,12 @@ export declare class ExotelAIAssistController extends EventEmitter<ControllerEve
|
|
|
4
4
|
private params;
|
|
5
5
|
private transport;
|
|
6
6
|
private status;
|
|
7
|
+
private destroyed;
|
|
8
|
+
private connectionEstablished;
|
|
7
9
|
private reconnectAttempt;
|
|
8
10
|
private reconnectTimer;
|
|
9
|
-
private
|
|
10
|
-
private readonly
|
|
11
|
-
private readonly maxReconnectAttempts;
|
|
11
|
+
private static readonly MAX_RECONNECT_ATTEMPTS;
|
|
12
|
+
private static readonly RECONNECT_BASE_MS;
|
|
12
13
|
constructor(params: ExotelAIAssistParams);
|
|
13
14
|
connect(): void;
|
|
14
15
|
disconnect(): void;
|
|
@@ -17,9 +18,8 @@ export declare class ExotelAIAssistController extends EventEmitter<ControllerEve
|
|
|
17
18
|
getStatus(): ConnectionStatus;
|
|
18
19
|
private _ensureTransport;
|
|
19
20
|
private _handleTransportMessage;
|
|
20
|
-
private _handleServerPayload;
|
|
21
21
|
private _scheduleReconnect;
|
|
22
22
|
private _clearReconnectTimer;
|
|
23
|
+
private _handleServerPayload;
|
|
23
24
|
private _setStatus;
|
|
24
|
-
private _log;
|
|
25
25
|
}
|
package/dist/controller/index.js
CHANGED
|
@@ -1572,20 +1572,25 @@ Utils.MAX_EXTRA_QUERY_PARAMS = 3;
|
|
|
1572
1572
|
|
|
1573
1573
|
class ExotelAIAssistController extends EventEmitter {
|
|
1574
1574
|
constructor(params) {
|
|
1575
|
-
var _a, _b;
|
|
1576
1575
|
super();
|
|
1577
1576
|
this.transport = null;
|
|
1578
1577
|
this.status = "idle";
|
|
1578
|
+
this.destroyed = false;
|
|
1579
|
+
// True once the WebSocket's onopen fires (CONNECTED received).
|
|
1580
|
+
// Reset to false before every new connection attempt.
|
|
1581
|
+
// This is the gate for the retry decision:
|
|
1582
|
+
// connectionEstablished === false → connection never opened → retry
|
|
1583
|
+
// connectionEstablished === true → server ended a live connection → destroy
|
|
1584
|
+
this.connectionEstablished = false;
|
|
1579
1585
|
this.reconnectAttempt = 0;
|
|
1580
1586
|
this.reconnectTimer = null;
|
|
1581
|
-
this.destroyed = false;
|
|
1582
1587
|
this.params = Object.assign({}, params);
|
|
1583
|
-
this.reconnectInterval = (_a = params.reconnectInterval) !== null && _a !== void 0 ? _a : 3000;
|
|
1584
|
-
this.maxReconnectAttempts = (_b = params.maxReconnectAttempts) !== null && _b !== void 0 ? _b : 5;
|
|
1585
1588
|
}
|
|
1586
1589
|
connect() {
|
|
1587
1590
|
if (this.destroyed) return;
|
|
1588
1591
|
this._clearReconnectTimer();
|
|
1592
|
+
this.connectionEstablished = false;
|
|
1593
|
+
this.reconnectAttempt = 0;
|
|
1589
1594
|
this._setStatus("connecting");
|
|
1590
1595
|
this._ensureTransport();
|
|
1591
1596
|
this.transport.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
@@ -1600,14 +1605,12 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1600
1605
|
var _a, _b;
|
|
1601
1606
|
const prev = this.params;
|
|
1602
1607
|
this.params = Object.assign(Object.assign({}, prev), patch);
|
|
1603
|
-
const
|
|
1604
|
-
const prevHash = Utils.hash(prev);
|
|
1605
|
-
const hashChanged = hash !== prevHash;
|
|
1608
|
+
const hashChanged = Utils.hash(this.params) !== Utils.hash(prev);
|
|
1606
1609
|
if (hashChanged) {
|
|
1607
|
-
this.reconnectAttempt = 0;
|
|
1608
1610
|
this._clearReconnectTimer();
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
+
this.connectionEstablished = false;
|
|
1612
|
+
this.reconnectAttempt = 0;
|
|
1613
|
+
// Tear down the current transport and start fresh with the new params.
|
|
1611
1614
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1612
1615
|
this.transport = null;
|
|
1613
1616
|
this._setStatus("connecting");
|
|
@@ -1622,6 +1625,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1622
1625
|
}
|
|
1623
1626
|
destroy() {
|
|
1624
1627
|
var _a;
|
|
1628
|
+
if (this.destroyed) return;
|
|
1625
1629
|
this.destroyed = true;
|
|
1626
1630
|
this._clearReconnectTimer();
|
|
1627
1631
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
@@ -1640,21 +1644,27 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1640
1644
|
var _a;
|
|
1641
1645
|
switch (msg.type) {
|
|
1642
1646
|
case "CONNECTED":
|
|
1647
|
+
this.connectionEstablished = true;
|
|
1643
1648
|
this.reconnectAttempt = 0;
|
|
1644
1649
|
this._setStatus("connected");
|
|
1645
1650
|
this.emit("onCallStart");
|
|
1646
1651
|
break;
|
|
1647
1652
|
case "DISCONNECTED":
|
|
1648
|
-
this.
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
+
if (this.connectionEstablished) {
|
|
1654
|
+
// Server ended an already-established connection (call ended, stream
|
|
1655
|
+
// not found, auth failure, etc.) — do not retry.
|
|
1656
|
+
this._setStatus("disconnected");
|
|
1657
|
+
this.emit("onCallEnd");
|
|
1658
|
+
this.destroy();
|
|
1659
|
+
} else {
|
|
1660
|
+
// WebSocket never opened — network/DNS/refused error.
|
|
1661
|
+
// Retry up to MAX_RECONNECT_ATTEMPTS then give up.
|
|
1653
1662
|
this._scheduleReconnect();
|
|
1654
1663
|
}
|
|
1655
1664
|
break;
|
|
1656
1665
|
case "ERROR":
|
|
1657
|
-
|
|
1666
|
+
// onerror fires before onclose; DISCONNECTED will arrive right after
|
|
1667
|
+
// and drive the retry / destroy decision. Just surface the error here.
|
|
1658
1668
|
this.emit("error", new Error((_a = msg.message) !== null && _a !== void 0 ? _a : "Unknown transport error"));
|
|
1659
1669
|
break;
|
|
1660
1670
|
case "MESSAGE":
|
|
@@ -1662,6 +1672,32 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1662
1672
|
break;
|
|
1663
1673
|
}
|
|
1664
1674
|
}
|
|
1675
|
+
_scheduleReconnect() {
|
|
1676
|
+
if (this.destroyed) return;
|
|
1677
|
+
if (this.reconnectAttempt >= ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS) {
|
|
1678
|
+
const err = new Error("Connection failed after maximum retries");
|
|
1679
|
+
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
1680
|
+
this._setStatus("error");
|
|
1681
|
+
this.emit("error", err);
|
|
1682
|
+
this.destroy();
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
const delay = Math.min(ExotelAIAssistController.RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempt), 30000);
|
|
1686
|
+
this.reconnectAttempt += 1;
|
|
1687
|
+
this._setStatus("connecting");
|
|
1688
|
+
this.reconnectTimer = setTimeout(() => {
|
|
1689
|
+
var _a;
|
|
1690
|
+
if (this.destroyed) return;
|
|
1691
|
+
this.connectionEstablished = false;
|
|
1692
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
1693
|
+
}, delay);
|
|
1694
|
+
}
|
|
1695
|
+
_clearReconnectTimer() {
|
|
1696
|
+
if (this.reconnectTimer !== null) {
|
|
1697
|
+
clearTimeout(this.reconnectTimer);
|
|
1698
|
+
this.reconnectTimer = null;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1665
1701
|
_handleServerPayload(raw) {
|
|
1666
1702
|
var _a;
|
|
1667
1703
|
let parsed;
|
|
@@ -1724,38 +1760,13 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1724
1760
|
}
|
|
1725
1761
|
}
|
|
1726
1762
|
}
|
|
1727
|
-
_scheduleReconnect() {
|
|
1728
|
-
if (this.destroyed) return;
|
|
1729
|
-
if (this.status === "error") return;
|
|
1730
|
-
if (this.reconnectAttempt >= this.maxReconnectAttempts) {
|
|
1731
|
-
const err = new Error("MAX_RECONNECT_EXCEEDED");
|
|
1732
|
-
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
1733
|
-
this.emit("error", err);
|
|
1734
|
-
return;
|
|
1735
|
-
}
|
|
1736
|
-
const delay = Math.min(this.reconnectInterval * Math.pow(2, this.reconnectAttempt), 30000);
|
|
1737
|
-
this.reconnectTimer = setTimeout(() => {
|
|
1738
|
-
var _a;
|
|
1739
|
-
if (this.destroyed) return;
|
|
1740
|
-
this.reconnectAttempt += 1;
|
|
1741
|
-
this._setStatus("connecting");
|
|
1742
|
-
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
1743
|
-
}, delay);
|
|
1744
|
-
}
|
|
1745
|
-
_clearReconnectTimer() {
|
|
1746
|
-
if (this.reconnectTimer !== null) {
|
|
1747
|
-
clearTimeout(this.reconnectTimer);
|
|
1748
|
-
this.reconnectTimer = null;
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
1763
|
_setStatus(status) {
|
|
1752
1764
|
if (this.status === status) return;
|
|
1753
1765
|
this.status = status;
|
|
1754
1766
|
this.emit("statusChange", status);
|
|
1755
1767
|
}
|
|
1756
|
-
_log(...args) {
|
|
1757
|
-
console.log("[ExotelAIAssist]", ...args);
|
|
1758
|
-
}
|
|
1759
1768
|
}
|
|
1769
|
+
ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS = 5;
|
|
1770
|
+
ExotelAIAssistController.RECONNECT_BASE_MS = 3000;
|
|
1760
1771
|
|
|
1761
1772
|
exports.ExotelAIAssistController = ExotelAIAssistController;
|
package/dist/index.js
CHANGED
|
@@ -20990,20 +20990,25 @@ Utils.MAX_EXTRA_QUERY_PARAMS = 3;
|
|
|
20990
20990
|
|
|
20991
20991
|
class ExotelAIAssistController extends EventEmitter {
|
|
20992
20992
|
constructor(params) {
|
|
20993
|
-
var _a, _b;
|
|
20994
20993
|
super();
|
|
20995
20994
|
this.transport = null;
|
|
20996
20995
|
this.status = "idle";
|
|
20996
|
+
this.destroyed = false;
|
|
20997
|
+
// True once the WebSocket's onopen fires (CONNECTED received).
|
|
20998
|
+
// Reset to false before every new connection attempt.
|
|
20999
|
+
// This is the gate for the retry decision:
|
|
21000
|
+
// connectionEstablished === false → connection never opened → retry
|
|
21001
|
+
// connectionEstablished === true → server ended a live connection → destroy
|
|
21002
|
+
this.connectionEstablished = false;
|
|
20997
21003
|
this.reconnectAttempt = 0;
|
|
20998
21004
|
this.reconnectTimer = null;
|
|
20999
|
-
this.destroyed = false;
|
|
21000
21005
|
this.params = Object.assign({}, params);
|
|
21001
|
-
this.reconnectInterval = (_a = params.reconnectInterval) !== null && _a !== void 0 ? _a : 3000;
|
|
21002
|
-
this.maxReconnectAttempts = (_b = params.maxReconnectAttempts) !== null && _b !== void 0 ? _b : 5;
|
|
21003
21006
|
}
|
|
21004
21007
|
connect() {
|
|
21005
21008
|
if (this.destroyed) return;
|
|
21006
21009
|
this._clearReconnectTimer();
|
|
21010
|
+
this.connectionEstablished = false;
|
|
21011
|
+
this.reconnectAttempt = 0;
|
|
21007
21012
|
this._setStatus("connecting");
|
|
21008
21013
|
this._ensureTransport();
|
|
21009
21014
|
this.transport.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
@@ -21018,14 +21023,12 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21018
21023
|
var _a, _b;
|
|
21019
21024
|
const prev = this.params;
|
|
21020
21025
|
this.params = Object.assign(Object.assign({}, prev), patch);
|
|
21021
|
-
const
|
|
21022
|
-
const prevHash = Utils.hash(prev);
|
|
21023
|
-
const hashChanged = hash !== prevHash;
|
|
21026
|
+
const hashChanged = Utils.hash(this.params) !== Utils.hash(prev);
|
|
21024
21027
|
if (hashChanged) {
|
|
21025
|
-
this.reconnectAttempt = 0;
|
|
21026
21028
|
this._clearReconnectTimer();
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
+
this.connectionEstablished = false;
|
|
21030
|
+
this.reconnectAttempt = 0;
|
|
21031
|
+
// Tear down the current transport and start fresh with the new params.
|
|
21029
21032
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
21030
21033
|
this.transport = null;
|
|
21031
21034
|
this._setStatus("connecting");
|
|
@@ -21040,6 +21043,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21040
21043
|
}
|
|
21041
21044
|
destroy() {
|
|
21042
21045
|
var _a;
|
|
21046
|
+
if (this.destroyed) return;
|
|
21043
21047
|
this.destroyed = true;
|
|
21044
21048
|
this._clearReconnectTimer();
|
|
21045
21049
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
@@ -21058,21 +21062,27 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21058
21062
|
var _a;
|
|
21059
21063
|
switch (msg.type) {
|
|
21060
21064
|
case "CONNECTED":
|
|
21065
|
+
this.connectionEstablished = true;
|
|
21061
21066
|
this.reconnectAttempt = 0;
|
|
21062
21067
|
this._setStatus("connected");
|
|
21063
21068
|
this.emit("onCallStart");
|
|
21064
21069
|
break;
|
|
21065
21070
|
case "DISCONNECTED":
|
|
21066
|
-
this.
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
+
if (this.connectionEstablished) {
|
|
21072
|
+
// Server ended an already-established connection (call ended, stream
|
|
21073
|
+
// not found, auth failure, etc.) — do not retry.
|
|
21074
|
+
this._setStatus("disconnected");
|
|
21075
|
+
this.emit("onCallEnd");
|
|
21076
|
+
this.destroy();
|
|
21077
|
+
} else {
|
|
21078
|
+
// WebSocket never opened — network/DNS/refused error.
|
|
21079
|
+
// Retry up to MAX_RECONNECT_ATTEMPTS then give up.
|
|
21071
21080
|
this._scheduleReconnect();
|
|
21072
21081
|
}
|
|
21073
21082
|
break;
|
|
21074
21083
|
case "ERROR":
|
|
21075
|
-
|
|
21084
|
+
// onerror fires before onclose; DISCONNECTED will arrive right after
|
|
21085
|
+
// and drive the retry / destroy decision. Just surface the error here.
|
|
21076
21086
|
this.emit("error", new Error((_a = msg.message) !== null && _a !== void 0 ? _a : "Unknown transport error"));
|
|
21077
21087
|
break;
|
|
21078
21088
|
case "MESSAGE":
|
|
@@ -21080,6 +21090,32 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21080
21090
|
break;
|
|
21081
21091
|
}
|
|
21082
21092
|
}
|
|
21093
|
+
_scheduleReconnect() {
|
|
21094
|
+
if (this.destroyed) return;
|
|
21095
|
+
if (this.reconnectAttempt >= ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS) {
|
|
21096
|
+
const err = new Error("Connection failed after maximum retries");
|
|
21097
|
+
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
21098
|
+
this._setStatus("error");
|
|
21099
|
+
this.emit("error", err);
|
|
21100
|
+
this.destroy();
|
|
21101
|
+
return;
|
|
21102
|
+
}
|
|
21103
|
+
const delay = Math.min(ExotelAIAssistController.RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempt), 30000);
|
|
21104
|
+
this.reconnectAttempt += 1;
|
|
21105
|
+
this._setStatus("connecting");
|
|
21106
|
+
this.reconnectTimer = setTimeout(() => {
|
|
21107
|
+
var _a;
|
|
21108
|
+
if (this.destroyed) return;
|
|
21109
|
+
this.connectionEstablished = false;
|
|
21110
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
21111
|
+
}, delay);
|
|
21112
|
+
}
|
|
21113
|
+
_clearReconnectTimer() {
|
|
21114
|
+
if (this.reconnectTimer !== null) {
|
|
21115
|
+
clearTimeout(this.reconnectTimer);
|
|
21116
|
+
this.reconnectTimer = null;
|
|
21117
|
+
}
|
|
21118
|
+
}
|
|
21083
21119
|
_handleServerPayload(raw) {
|
|
21084
21120
|
var _a;
|
|
21085
21121
|
let parsed;
|
|
@@ -21142,39 +21178,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21142
21178
|
}
|
|
21143
21179
|
}
|
|
21144
21180
|
}
|
|
21145
|
-
_scheduleReconnect() {
|
|
21146
|
-
if (this.destroyed) return;
|
|
21147
|
-
if (this.status === "error") return;
|
|
21148
|
-
if (this.reconnectAttempt >= this.maxReconnectAttempts) {
|
|
21149
|
-
const err = new Error("MAX_RECONNECT_EXCEEDED");
|
|
21150
|
-
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
21151
|
-
this.emit("error", err);
|
|
21152
|
-
return;
|
|
21153
|
-
}
|
|
21154
|
-
const delay = Math.min(this.reconnectInterval * Math.pow(2, this.reconnectAttempt), 30000);
|
|
21155
|
-
this.reconnectTimer = setTimeout(() => {
|
|
21156
|
-
var _a;
|
|
21157
|
-
if (this.destroyed) return;
|
|
21158
|
-
this.reconnectAttempt += 1;
|
|
21159
|
-
this._setStatus("connecting");
|
|
21160
|
-
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
21161
|
-
}, delay);
|
|
21162
|
-
}
|
|
21163
|
-
_clearReconnectTimer() {
|
|
21164
|
-
if (this.reconnectTimer !== null) {
|
|
21165
|
-
clearTimeout(this.reconnectTimer);
|
|
21166
|
-
this.reconnectTimer = null;
|
|
21167
|
-
}
|
|
21168
|
-
}
|
|
21169
21181
|
_setStatus(status) {
|
|
21170
21182
|
if (this.status === status) return;
|
|
21171
21183
|
this.status = status;
|
|
21172
21184
|
this.emit("statusChange", status);
|
|
21173
21185
|
}
|
|
21174
|
-
_log(...args) {
|
|
21175
|
-
console.log("[ExotelAIAssist]", ...args);
|
|
21176
|
-
}
|
|
21177
21186
|
}
|
|
21187
|
+
ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS = 5;
|
|
21188
|
+
ExotelAIAssistController.RECONNECT_BASE_MS = 3000;
|
|
21178
21189
|
|
|
21179
21190
|
function useExotelAIAssist(params) {
|
|
21180
21191
|
const controllerRef = reactExports.useRef(null);
|
package/dist/react/index.js
CHANGED
|
@@ -6276,20 +6276,25 @@ Utils.MAX_EXTRA_QUERY_PARAMS = 3;
|
|
|
6276
6276
|
|
|
6277
6277
|
class ExotelAIAssistController extends EventEmitter {
|
|
6278
6278
|
constructor(params) {
|
|
6279
|
-
var _a, _b;
|
|
6280
6279
|
super();
|
|
6281
6280
|
this.transport = null;
|
|
6282
6281
|
this.status = "idle";
|
|
6282
|
+
this.destroyed = false;
|
|
6283
|
+
// True once the WebSocket's onopen fires (CONNECTED received).
|
|
6284
|
+
// Reset to false before every new connection attempt.
|
|
6285
|
+
// This is the gate for the retry decision:
|
|
6286
|
+
// connectionEstablished === false → connection never opened → retry
|
|
6287
|
+
// connectionEstablished === true → server ended a live connection → destroy
|
|
6288
|
+
this.connectionEstablished = false;
|
|
6283
6289
|
this.reconnectAttempt = 0;
|
|
6284
6290
|
this.reconnectTimer = null;
|
|
6285
|
-
this.destroyed = false;
|
|
6286
6291
|
this.params = Object.assign({}, params);
|
|
6287
|
-
this.reconnectInterval = (_a = params.reconnectInterval) !== null && _a !== void 0 ? _a : 3000;
|
|
6288
|
-
this.maxReconnectAttempts = (_b = params.maxReconnectAttempts) !== null && _b !== void 0 ? _b : 5;
|
|
6289
6292
|
}
|
|
6290
6293
|
connect() {
|
|
6291
6294
|
if (this.destroyed) return;
|
|
6292
6295
|
this._clearReconnectTimer();
|
|
6296
|
+
this.connectionEstablished = false;
|
|
6297
|
+
this.reconnectAttempt = 0;
|
|
6293
6298
|
this._setStatus("connecting");
|
|
6294
6299
|
this._ensureTransport();
|
|
6295
6300
|
this.transport.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
@@ -6304,14 +6309,12 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6304
6309
|
var _a, _b;
|
|
6305
6310
|
const prev = this.params;
|
|
6306
6311
|
this.params = Object.assign(Object.assign({}, prev), patch);
|
|
6307
|
-
const
|
|
6308
|
-
const prevHash = Utils.hash(prev);
|
|
6309
|
-
const hashChanged = hash !== prevHash;
|
|
6312
|
+
const hashChanged = Utils.hash(this.params) !== Utils.hash(prev);
|
|
6310
6313
|
if (hashChanged) {
|
|
6311
|
-
this.reconnectAttempt = 0;
|
|
6312
6314
|
this._clearReconnectTimer();
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
+
this.connectionEstablished = false;
|
|
6316
|
+
this.reconnectAttempt = 0;
|
|
6317
|
+
// Tear down the current transport and start fresh with the new params.
|
|
6315
6318
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
6316
6319
|
this.transport = null;
|
|
6317
6320
|
this._setStatus("connecting");
|
|
@@ -6326,6 +6329,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6326
6329
|
}
|
|
6327
6330
|
destroy() {
|
|
6328
6331
|
var _a;
|
|
6332
|
+
if (this.destroyed) return;
|
|
6329
6333
|
this.destroyed = true;
|
|
6330
6334
|
this._clearReconnectTimer();
|
|
6331
6335
|
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
@@ -6344,21 +6348,27 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6344
6348
|
var _a;
|
|
6345
6349
|
switch (msg.type) {
|
|
6346
6350
|
case "CONNECTED":
|
|
6351
|
+
this.connectionEstablished = true;
|
|
6347
6352
|
this.reconnectAttempt = 0;
|
|
6348
6353
|
this._setStatus("connected");
|
|
6349
6354
|
this.emit("onCallStart");
|
|
6350
6355
|
break;
|
|
6351
6356
|
case "DISCONNECTED":
|
|
6352
|
-
this.
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
+
if (this.connectionEstablished) {
|
|
6358
|
+
// Server ended an already-established connection (call ended, stream
|
|
6359
|
+
// not found, auth failure, etc.) — do not retry.
|
|
6360
|
+
this._setStatus("disconnected");
|
|
6361
|
+
this.emit("onCallEnd");
|
|
6362
|
+
this.destroy();
|
|
6363
|
+
} else {
|
|
6364
|
+
// WebSocket never opened — network/DNS/refused error.
|
|
6365
|
+
// Retry up to MAX_RECONNECT_ATTEMPTS then give up.
|
|
6357
6366
|
this._scheduleReconnect();
|
|
6358
6367
|
}
|
|
6359
6368
|
break;
|
|
6360
6369
|
case "ERROR":
|
|
6361
|
-
|
|
6370
|
+
// onerror fires before onclose; DISCONNECTED will arrive right after
|
|
6371
|
+
// and drive the retry / destroy decision. Just surface the error here.
|
|
6362
6372
|
this.emit("error", new Error((_a = msg.message) !== null && _a !== void 0 ? _a : "Unknown transport error"));
|
|
6363
6373
|
break;
|
|
6364
6374
|
case "MESSAGE":
|
|
@@ -6366,6 +6376,32 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6366
6376
|
break;
|
|
6367
6377
|
}
|
|
6368
6378
|
}
|
|
6379
|
+
_scheduleReconnect() {
|
|
6380
|
+
if (this.destroyed) return;
|
|
6381
|
+
if (this.reconnectAttempt >= ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS) {
|
|
6382
|
+
const err = new Error("Connection failed after maximum retries");
|
|
6383
|
+
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
6384
|
+
this._setStatus("error");
|
|
6385
|
+
this.emit("error", err);
|
|
6386
|
+
this.destroy();
|
|
6387
|
+
return;
|
|
6388
|
+
}
|
|
6389
|
+
const delay = Math.min(ExotelAIAssistController.RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempt), 30000);
|
|
6390
|
+
this.reconnectAttempt += 1;
|
|
6391
|
+
this._setStatus("connecting");
|
|
6392
|
+
this.reconnectTimer = setTimeout(() => {
|
|
6393
|
+
var _a;
|
|
6394
|
+
if (this.destroyed) return;
|
|
6395
|
+
this.connectionEstablished = false;
|
|
6396
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
6397
|
+
}, delay);
|
|
6398
|
+
}
|
|
6399
|
+
_clearReconnectTimer() {
|
|
6400
|
+
if (this.reconnectTimer !== null) {
|
|
6401
|
+
clearTimeout(this.reconnectTimer);
|
|
6402
|
+
this.reconnectTimer = null;
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6369
6405
|
_handleServerPayload(raw) {
|
|
6370
6406
|
var _a;
|
|
6371
6407
|
let parsed;
|
|
@@ -6428,39 +6464,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6428
6464
|
}
|
|
6429
6465
|
}
|
|
6430
6466
|
}
|
|
6431
|
-
_scheduleReconnect() {
|
|
6432
|
-
if (this.destroyed) return;
|
|
6433
|
-
if (this.status === "error") return;
|
|
6434
|
-
if (this.reconnectAttempt >= this.maxReconnectAttempts) {
|
|
6435
|
-
const err = new Error("MAX_RECONNECT_EXCEEDED");
|
|
6436
|
-
err.code = "MAX_RECONNECT_EXCEEDED";
|
|
6437
|
-
this.emit("error", err);
|
|
6438
|
-
return;
|
|
6439
|
-
}
|
|
6440
|
-
const delay = Math.min(this.reconnectInterval * Math.pow(2, this.reconnectAttempt), 30000);
|
|
6441
|
-
this.reconnectTimer = setTimeout(() => {
|
|
6442
|
-
var _a;
|
|
6443
|
-
if (this.destroyed) return;
|
|
6444
|
-
this.reconnectAttempt += 1;
|
|
6445
|
-
this._setStatus("connecting");
|
|
6446
|
-
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.connect(Utils.buildWsUrl(this.params), this.params.authToken);
|
|
6447
|
-
}, delay);
|
|
6448
|
-
}
|
|
6449
|
-
_clearReconnectTimer() {
|
|
6450
|
-
if (this.reconnectTimer !== null) {
|
|
6451
|
-
clearTimeout(this.reconnectTimer);
|
|
6452
|
-
this.reconnectTimer = null;
|
|
6453
|
-
}
|
|
6454
|
-
}
|
|
6455
6467
|
_setStatus(status) {
|
|
6456
6468
|
if (this.status === status) return;
|
|
6457
6469
|
this.status = status;
|
|
6458
6470
|
this.emit("statusChange", status);
|
|
6459
6471
|
}
|
|
6460
|
-
_log(...args) {
|
|
6461
|
-
console.log("[ExotelAIAssist]", ...args);
|
|
6462
|
-
}
|
|
6463
6472
|
}
|
|
6473
|
+
ExotelAIAssistController.MAX_RECONNECT_ATTEMPTS = 5;
|
|
6474
|
+
ExotelAIAssistController.RECONNECT_BASE_MS = 3000;
|
|
6464
6475
|
|
|
6465
6476
|
function useExotelAIAssist(params) {
|
|
6466
6477
|
const controllerRef = React.useRef(null);
|
package/package.json
CHANGED