polfan-server-js-client 0.4.2 → 0.4.4

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.
@@ -0,0 +1,4 @@
1
+ <changelist name="Uncommitted_changes_before_Update_at_08_09_2026_17_04_[Changes]" date="1788879866780" recycled="true" deleted="true">
2
+ <option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Update_at_08_09_2026_17_04_[Changes]/shelved.patch" />
3
+ <option name="DESCRIPTION" value="Uncommitted changes before Update at 08.09.2026 17:04 [Changes]" />
4
+ </changelist>
@@ -4,7 +4,15 @@
4
4
  <option name="autoReloadType" value="SELECTIVE" />
5
5
  </component>
6
6
  <component name="ChangeListManager">
7
- <list default="true" id="831dae43-0da1-47fd-a5f7-33dd5eec2992" name="Changes" comment="Revert &quot;User presence status support&quot;&#10;&#10;This reverts commit b9ff2004" />
7
+ <list default="true" id="831dae43-0da1-47fd-a5f7-33dd5eec2992" name="Changes" comment="Bump version">
8
+ <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
9
+ <change beforePath="$PROJECT_DIR$/build/index.cjs.js" beforeDir="false" afterPath="$PROJECT_DIR$/build/index.cjs.js" afterDir="false" />
10
+ <change beforePath="$PROJECT_DIR$/build/index.cjs.js.map" beforeDir="false" afterPath="$PROJECT_DIR$/build/index.cjs.js.map" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/build/index.umd.js" beforeDir="false" afterPath="$PROJECT_DIR$/build/index.umd.js" afterDir="false" />
12
+ <change beforePath="$PROJECT_DIR$/build/index.umd.js.map" beforeDir="false" afterPath="$PROJECT_DIR$/build/index.umd.js.map" afterDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/build/types/WebSocketChatClient.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/build/types/WebSocketChatClient.d.ts" afterDir="false" />
14
+ <change beforePath="$PROJECT_DIR$/src/WebSocketChatClient.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/WebSocketChatClient.ts" afterDir="false" />
15
+ </list>
8
16
  <option name="SHOW_DIALOG" value="false" />
9
17
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
18
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -14,7 +22,7 @@
14
22
  <execution />
15
23
  </component>
16
24
  <component name="EmbeddingIndexingInfo">
17
- <option name="cachedIndexableFilesCount" value="213" />
25
+ <option name="cachedIndexableFilesCount" value="223" />
18
26
  </component>
19
27
  <component name="Git.Rebase.Settings">
20
28
  <option name="NEW_BASE" value="master" />
@@ -28,7 +36,7 @@
28
36
  <option name="RECENT_BRANCH_BY_REPOSITORY">
29
37
  <map>
30
38
  <entry key="$PROJECT_DIR$" value="claude/chat-reactions-polls-system-hwea5i" />
31
- <entry key="$PROJECT_DIR$/src/types" value="main" />
39
+ <entry key="$PROJECT_DIR$/src/types" value="claude/chat-reactions-polls-system-hwea5i" />
32
40
  </map>
33
41
  </option>
34
42
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
@@ -44,12 +52,12 @@
44
52
  <component name="GitRewordedCommitMessages">
45
53
  <option name="commitMessagesMapping">
46
54
  <RewordedCommitMessageMapping>
47
- <option name="originalMessage" value="Message reactions and polls&#10;&#10;The history window keeps the global counters on the messages themselves and the&#10;connected user's own votes in a map beside them, so what everybody sees stays&#10;identical and only the active state differs per user. `MessageReactionChanged`&#10;overwrites a counter in place (a pill must not jump around as its number moves)&#10;and retires it at zero; `ReactionToggled` moves the user's own entry.&#10;&#10;A GetMessages response is the truth for every message it covers, so the map is&#10;refreshed from it rather than merged - a vote withdrawn from another device does&#10;not survive a reload.&#10;&#10;The four near-identical fetchers now share one request helper, which is also&#10;where `includeMyReactions` is attached." />
48
- <option name="rewordedMessage" value="Message reactions and polls" />
55
+ <option name="originalMessage" value="Role flags and the MentionAllRoles permission&#10;&#10;Re-exports the RoleFlag enum and registers the new permission bit, plus the&#10;types submodule bump carrying `Role.flags`.&#10;&#10;Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;&#10;Claude-Session: https://claude.ai/code/session_018QUxtDbWJMFih7bKH6thpn" />
56
+ <option name="rewordedMessage" value="Role flags and the MentionAllRoles permission" />
49
57
  </RewordedCommitMessageMapping>
50
58
  </option>
51
59
  <option name="currentCommit" value="1" />
52
- <option name="onto" value="79acd57a1c49b90da43379eaa1805f46ad9a655e" />
60
+ <option name="onto" value="54de756e28152bbda67a6480b48df607e3425e4a" />
53
61
  </component>
54
62
  <component name="GithubPullRequestsUISettings">{
55
63
  &quot;selectedUrlAndAccountId&quot;: {
@@ -253,23 +261,10 @@
253
261
  <workItem from="1786526830597" duration="598000" />
254
262
  <workItem from="1787125977456" duration="278000" />
255
263
  <workItem from="1787576741803" duration="2076000" />
256
- <workItem from="1787648458595" duration="967000" />
257
- </task>
258
- <task id="LOCAL-00067" summary="Ping-pong connection monitoring">
259
- <option name="closed" value="true" />
260
- <created>1765145134871</created>
261
- <option name="number" value="00067" />
262
- <option name="presentableId" value="LOCAL-00067" />
263
- <option name="project" value="LOCAL" />
264
- <updated>1765145134871</updated>
265
- </task>
266
- <task id="LOCAL-00068" summary="Improve reconnect on timeout">
267
- <option name="closed" value="true" />
268
- <created>1765145773196</created>
269
- <option name="number" value="00068" />
270
- <option name="presentableId" value="LOCAL-00068" />
271
- <option name="project" value="LOCAL" />
272
- <updated>1765145773196</updated>
264
+ <workItem from="1787648458595" duration="3034000" />
265
+ <workItem from="1787994100244" duration="588000" />
266
+ <workItem from="1788873818076" duration="1523000" />
267
+ <workItem from="1788879390791" duration="2740000" />
273
268
  </task>
274
269
  <task id="LOCAL-00069" summary="Bump version">
275
270
  <option name="closed" value="true" />
@@ -647,7 +642,23 @@
647
642
  <option name="project" value="LOCAL" />
648
643
  <updated>1787581107970</updated>
649
644
  </task>
650
- <option name="localTasksCounter" value="116" />
645
+ <task id="LOCAL-00116" summary="Bump version&#10;&#10;This reverts commit b9ff2004">
646
+ <option name="closed" value="true" />
647
+ <created>1787649696666</created>
648
+ <option name="number" value="00116" />
649
+ <option name="presentableId" value="LOCAL-00116" />
650
+ <option name="project" value="LOCAL" />
651
+ <updated>1787649696666</updated>
652
+ </task>
653
+ <task id="LOCAL-00117" summary="Bump version">
654
+ <option name="closed" value="true" />
655
+ <created>1788952429127</created>
656
+ <option name="number" value="00117" />
657
+ <option name="presentableId" value="LOCAL-00117" />
658
+ <option name="project" value="LOCAL" />
659
+ <updated>1788952429128</updated>
660
+ </task>
661
+ <option name="localTasksCounter" value="118" />
651
662
  <servers />
652
663
  </component>
653
664
  <component name="TypeScriptGeneratedFilesManager">
@@ -664,6 +675,11 @@
664
675
  <option value="master" />
665
676
  </option>
666
677
  </RecentGroup>
678
+ <RecentGroup>
679
+ <option name="FILTER_VALUES">
680
+ <option value="claude/role-flags-separation-mentions-g6v7s0" />
681
+ </option>
682
+ </RecentGroup>
667
683
  <RecentGroup>
668
684
  <option name="FILTER_VALUES">
669
685
  <option value="claude/chat-reactions-polls-system-hwea5i" />
@@ -699,6 +715,11 @@
699
715
  <entry key="MAIN">
700
716
  <value>
701
717
  <State>
718
+ <option name="CUSTOM_BOOLEAN_PROPERTIES">
719
+ <map>
720
+ <entry key="Show.Git.Branches" value="true" />
721
+ </map>
722
+ </option>
702
723
  <option name="FILTERS">
703
724
  <map>
704
725
  <entry key="branch">
@@ -717,8 +738,6 @@
717
738
  </option>
718
739
  </component>
719
740
  <component name="VcsManagerConfiguration">
720
- <MESSAGE value="Fix room member update event handle" />
721
- <MESSAGE value="Fix room member update event handle" />
722
741
  <MESSAGE value="Add jumpTo message support" />
723
742
  <MESSAGE value="Adjust to followed topics changes in protocol" />
724
743
  <MESSAGE value="SetSessionData command support" />
@@ -731,7 +750,6 @@
731
750
  <MESSAGE value="Fixes in WebApiChatClient" />
732
751
  <MESSAGE value="Fix resolve window status after jump to message" />
733
752
  <MESSAGE value="Fix jumpTo when window is in LIVE state" />
734
- <MESSAGE value="Bump version" />
735
753
  <MESSAGE value="Unread topic counter in summary, bump version" />
736
754
  <MESSAGE value="Unread room counter in summary, bump version" />
737
755
  <MESSAGE value="Reconcile tracked state on reconnect" />
@@ -742,7 +760,10 @@
742
760
  <MESSAGE value="User presence status support" />
743
761
  <MESSAGE value="Reacting changes" />
744
762
  <MESSAGE value="Revert &quot;User presence status support&quot;&#10;&#10;This reverts commit b9ff2004" />
745
- <option name="LAST_COMMIT_MESSAGE" value="Revert &quot;User presence status support&quot;&#10;&#10;This reverts commit b9ff2004" />
763
+ <MESSAGE value="Bump version&#10;&#10;This reverts commit b9ff2004" />
764
+ <MESSAGE value="Role flags and the MentionAllRoles permission" />
765
+ <MESSAGE value="Bump version" />
766
+ <option name="LAST_COMMIT_MESSAGE" value="Bump version" />
746
767
  </component>
747
768
  <component name="XSLT-Support.FileAssociations.UIState">
748
769
  <expand />
package/README.md CHANGED
@@ -24,6 +24,26 @@ TypeScript client library to handle connection with Devana (new Polfan) chat ser
24
24
  })();
25
25
  ```
26
26
 
27
+ ## Connection maintenance
28
+
29
+ `WebSocketChatClient` keeps the connection alive: after any loss (error, connecting timeout, missing pong, closure with
30
+ a code other than 1000) it reconnects indefinitely, with an exponential backoff between attempts. The `disconnect` event
31
+ tells whether a retry follows. A pending `connect()` promise resolves once an attempt authenticates, and is rejected by
32
+ `disconnect()`, which also stops reconnecting. An invalid token (`Bye` with `AuthenticationException`) rejects `connect()`
33
+ with the `Bye` data, emits an `error` event and stops reconnecting as well.
34
+
35
+ ```js
36
+ const wsClient = new PServ.WebSocketChatClient({
37
+ token: 'your-access-token',
38
+ url: 'wss://pserv-websocket-address',
39
+ connectingTimeoutMs: 10000, // Per attempt (default)
40
+ reconnect: {
41
+ minDelayMs: 1000, // Delay before the first retry, doubled on every next one (default)
42
+ maxDelayMs: 30000, // Maximum delay between retries (default)
43
+ },
44
+ });
45
+ ```
46
+
27
47
  ## State management
28
48
 
29
49
  The library provides built-in state management for WebSocket client. To enable it, just pass `stateTracking`
@@ -48,6 +48,7 @@ __webpack_require__.d(__webpack_exports__, {
48
48
  ObservableIndexedObjectCollection: () => (/* reexport */ ObservableIndexedObjectCollection),
49
49
  PermissionDefinition: () => (/* reexport */ PermissionDefinition),
50
50
  Permissions: () => (/* reexport */ Permissions),
51
+ RoleFlag: () => (/* reexport */ RoleFlag),
51
52
  UserStatus: () => (/* reexport */ UserStatus),
52
53
  WebApiChatClient: () => (/* reexport */ WebApiChatClient),
53
54
  WebSocketChatClient: () => (/* reexport */ WebSocketChatClient),
@@ -4326,6 +4327,10 @@ Permissions_defineProperty(Permissions, "list", {
4326
4327
  CreatePolls: {
4327
4328
  value: 1 << 22,
4328
4329
  maxLayer: Layer.Topic
4330
+ },
4331
+ MentionAllRoles: {
4332
+ value: 1 << 23,
4333
+ maxLayer: Layer.Topic
4329
4334
  }
4330
4335
  });
4331
4336
  ;// ./src/state-tracker/PermissionsManager.ts
@@ -5434,6 +5439,9 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5434
5439
  WebSocketChatClient_defineProperty(_this, "pingMonitorInterval", void 0);
5435
5440
  WebSocketChatClient_defineProperty(_this, "inFlightPingTimeout", void 0);
5436
5441
  WebSocketChatClient_defineProperty(_this, "lastReceivedMessageAt", void 0);
5442
+ WebSocketChatClient_defineProperty(_this, "reconnectEnabled", false);
5443
+ WebSocketChatClient_defineProperty(_this, "reconnectTimeoutId", void 0);
5444
+ WebSocketChatClient_defineProperty(_this, "reconnectAttempts", 0);
5437
5445
  _this.options = options;
5438
5446
  if ((_this$options$stateTr = _this.options.stateTracking) !== null && _this$options$stateTr !== void 0 ? _this$options$stateTr : true) {
5439
5447
  _this.state = new ChatStateTracker(_this);
@@ -5449,14 +5457,14 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5449
5457
  key: "connect",
5450
5458
  value: function () {
5451
5459
  var _connect = WebSocketChatClient_asyncToGenerator(/*#__PURE__*/WebSocketChatClient_regenerator().m(function _callee() {
5452
- var _this$connectPromise2,
5453
- _this2 = this,
5454
- _this$options$queryPa,
5455
- _this$options$connect;
5456
- var _this$connectPromise, params;
5460
+ var _this2 = this;
5461
+ var _this$connectPromise, connectPromise;
5457
5462
  return WebSocketChatClient_regenerator().w(function (_context) {
5458
5463
  while (1) switch (_context.n) {
5459
5464
  case 0:
5465
+ this.reconnectEnabled = true;
5466
+ // A manual call does not wait for a scheduled retry.
5467
+ this.cancelScheduledReconnect();
5460
5468
  if (!(this.isOpenWsState() || this.isConnectingWsState())) {
5461
5469
  _context.n = 1;
5462
5470
  break;
@@ -5466,26 +5474,23 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5466
5474
  // Reuse the promise of an attempt that has not settled yet (an
5467
5475
  // automatic reconnect), so the caller that started connecting is
5468
5476
  // resolved by whichever attempt eventually authenticates.
5469
- (_this$connectPromise2 = this.connectPromise) !== null && _this$connectPromise2 !== void 0 ? _this$connectPromise2 : this.connectPromise = new Promise(function () {
5470
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
5471
- args[_key] = arguments[_key];
5472
- }
5473
- return _this2.authenticatedResolvers = args;
5474
- });
5475
- params = new URLSearchParams((_this$options$queryPa = this.options.queryParams) !== null && _this$options$queryPa !== void 0 ? _this$options$queryPa : {});
5476
- params.set('token', this.options.token);
5477
- this.ws = new WebSocket("".concat(this.options.url, "?").concat(params));
5478
- this.ws.onclose = function (ev) {
5479
- return _this2.onClose(ev);
5480
- };
5481
- this.ws.onmessage = function (ev) {
5482
- return _this2.onMessage(ev);
5483
- };
5484
- this.connectingTimeoutId = setTimeout(function () {
5485
- return _this2.triggerConnectionTimeout();
5486
- }, (_this$options$connect = this.options.connectingTimeoutMs) !== null && _this$options$connect !== void 0 ? _this$options$connect : 10000);
5487
- this.authenticated = false;
5488
- return _context.a(2, this.connectPromise);
5477
+ if (!this.connectPromise) {
5478
+ this.connectPromise = new Promise(function () {
5479
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
5480
+ args[_key] = arguments[_key];
5481
+ }
5482
+ return _this2.authenticatedResolvers = args;
5483
+ });
5484
+ // Automatic reconnects create this promise with no one awaiting it,
5485
+ // so its rejection must not surface as an unhandled one. Callers
5486
+ // awaiting it still receive the rejection.
5487
+ this.connectPromise["catch"](function () {
5488
+ return undefined;
5489
+ });
5490
+ }
5491
+ connectPromise = this.connectPromise;
5492
+ this.openSocket();
5493
+ return _context.a(2, connectPromise);
5489
5494
  }
5490
5495
  }, _callee, this);
5491
5496
  }));
@@ -5497,10 +5502,16 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5497
5502
  }, {
5498
5503
  key: "disconnect",
5499
5504
  value: function disconnect() {
5500
- var _this$ws;
5505
+ var wasActive = this.ws !== null || this.reconnectTimeoutId !== undefined;
5506
+ this.reconnectEnabled = false;
5507
+ this.reconnectAttempts = 0;
5508
+ this.cancelScheduledReconnect();
5509
+ this.releaseSocket(1000); // Normal closure
5501
5510
  this.failPendingCommands(new Error('Client disconnected before the command was answered'));
5502
- (_this$ws = this.ws) === null || _this$ws === void 0 || _this$ws.close(1000); // Normal closure
5503
- this.ws = null;
5511
+ this.settleConnect(new Error('Client disconnected before authentication'));
5512
+ if (wasActive) {
5513
+ this.emit(this.Event.disconnect, false);
5514
+ }
5504
5515
  }
5505
5516
  }, {
5506
5517
  key: "send",
@@ -5534,15 +5545,52 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5534
5545
  get: function get() {
5535
5546
  return this.isOpenWsState() && this.authenticated;
5536
5547
  }
5548
+ }, {
5549
+ key: "openSocket",
5550
+ value: function openSocket() {
5551
+ var _this$options$queryPa,
5552
+ _this3 = this,
5553
+ _this$options$connect;
5554
+ // Never leave a previous socket attached (e.g. one still closing).
5555
+ this.releaseSocket(1000);
5556
+ var params = new URLSearchParams((_this$options$queryPa = this.options.queryParams) !== null && _this$options$queryPa !== void 0 ? _this$options$queryPa : {});
5557
+ params.set('token', this.options.token);
5558
+ var ws;
5559
+ try {
5560
+ ws = new WebSocket("".concat(this.options.url, "?").concat(params));
5561
+ } catch (error) {
5562
+ // Invalid URL - no retry can fix that.
5563
+ this.settleConnect(error);
5564
+ return;
5565
+ }
5566
+
5567
+ // Events of an abandoned socket must not touch the current connection.
5568
+ ws.onmessage = function (ev) {
5569
+ return ws === _this3.ws && _this3.onMessage(ev);
5570
+ };
5571
+ ws.onclose = function (ev) {
5572
+ return ws === _this3.ws && _this3.onClose(ev);
5573
+ };
5574
+ // Not every implementation follows a failed handshake with a close
5575
+ // event (e.g. Node.js), so an error alone means the connection is lost.
5576
+ ws.onerror = function () {
5577
+ return ws === _this3.ws && _this3.handleConnectionLoss(true);
5578
+ };
5579
+ this.ws = ws;
5580
+ this.authenticated = false;
5581
+ this.connectingTimeoutId = setTimeout(function () {
5582
+ return _this3.triggerConnectionTimeout();
5583
+ }, (_this$options$connect = this.options.connectingTimeoutMs) !== null && _this$options$connect !== void 0 ? _this$options$connect : 10000);
5584
+ }
5537
5585
  }, {
5538
5586
  key: "sendEnvelope",
5539
5587
  value: function sendEnvelope(envelope) {
5540
- var _this$ws$readyState, _this$ws2;
5588
+ var _this$ws$readyState, _this$ws;
5541
5589
  if (this.isReady) {
5542
5590
  this.ws.send(JSON.stringify(envelope));
5543
5591
  return;
5544
5592
  }
5545
- this.handleEnvelopeSendError(envelope, new Error("Cannot send - client is not ready (state=".concat((_this$ws$readyState = (_this$ws2 = this.ws) === null || _this$ws2 === void 0 ? void 0 : _this$ws2.readyState) !== null && _this$ws$readyState !== void 0 ? _this$ws$readyState : '[no connection]', "; authenticated=").concat(this.authenticated, ")")));
5593
+ this.handleEnvelopeSendError(envelope, new Error("Cannot send - client is not ready (state=".concat((_this$ws$readyState = (_this$ws = this.ws) === null || _this$ws === void 0 ? void 0 : _this$ws.readyState) !== null && _this$ws$readyState !== void 0 ? _this$ws$readyState : '[no connection]', "; authenticated=").concat(this.authenticated, ")")));
5546
5594
  }
5547
5595
  }, {
5548
5596
  key: "onMessage",
@@ -5558,35 +5606,110 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5558
5606
  var isAuthenticated = envelope.type !== 'Bye';
5559
5607
  this.authenticated = isAuthenticated;
5560
5608
  if (isAuthenticated) {
5609
+ this.reconnectAttempts = 0;
5561
5610
  this.startConnectionMonitor();
5562
5611
  this.settleConnect();
5563
5612
  this.emit(this.Event.connect);
5564
5613
  this.sendFromQueue();
5565
5614
  } else {
5615
+ var _envelope$data;
5566
5616
  this.settleConnect(envelope.data);
5617
+ var error = (_envelope$data = envelope.data) === null || _envelope$data === void 0 || (_envelope$data = _envelope$data.reason) === null || _envelope$data === void 0 ? void 0 : _envelope$data.error;
5618
+ if ((error === null || error === void 0 ? void 0 : error.code) === 'AuthenticationException') {
5619
+ // Invalid token - retrying would be rejected the same way.
5620
+ this.handleConnectionLoss(false);
5621
+ this.emit(this.Event.error, new Error("Authentication rejected: ".concat(error.message)));
5622
+ }
5567
5623
  }
5568
5624
  }
5569
5625
  }
5570
5626
  }, {
5571
5627
  key: "onClose",
5572
5628
  value: function onClose(event) {
5573
- this.stopConnectionMonitor();
5574
- clearTimeout(this.connectingTimeoutId);
5575
- var reconnect = event.code !== 1000; // Connection was closed because of error
5629
+ // Connection was closed because of error
5630
+ this.handleConnectionLoss(event.code !== 1000);
5631
+ }
5632
+
5633
+ /**
5634
+ * Abandon the current socket without waiting for its close event (which
5635
+ * may never come, or take minutes on a dead TCP connection), settle
5636
+ * everything that depended on it and schedule a retry when requested.
5637
+ */
5638
+ }, {
5639
+ key: "handleConnectionLoss",
5640
+ value: function handleConnectionLoss(reconnect) {
5641
+ this.releaseSocket(reconnect ? 3000 : 1000);
5576
5642
 
5577
5643
  // The server can no longer answer anything that was queued or in
5578
5644
  // flight, so settle those promises instead of leaving them pending.
5579
5645
  this.failPendingCommands(new Error('Connection closed before the command was answered'));
5646
+ reconnect && (reconnect = this.reconnectEnabled);
5580
5647
  if (reconnect) {
5581
5648
  // Keep a pending connect() promise unsettled - the retry below is
5582
5649
  // expected to authenticate and will resolve it.
5583
- void this.connect();
5650
+ this.scheduleReconnect();
5584
5651
  } else {
5652
+ this.reconnectEnabled = false;
5585
5653
  this.settleConnect(new Error('Connection closed before authentication'));
5586
5654
  }
5587
5655
  this.emit(this.Event.disconnect, reconnect);
5588
5656
  }
5589
5657
 
5658
+ /**
5659
+ * Detach the current socket from the client and close it if it is still
5660
+ * alive, together with all timers bound to it.
5661
+ */
5662
+ }, {
5663
+ key: "releaseSocket",
5664
+ value: function releaseSocket(closeCode) {
5665
+ this.stopConnectionMonitor();
5666
+ clearTimeout(this.connectingTimeoutId);
5667
+ this.connectingTimeoutId = undefined;
5668
+ this.authenticated = false;
5669
+ var ws = this.ws;
5670
+ this.ws = null;
5671
+ if (!ws) {
5672
+ return;
5673
+ }
5674
+ ws.onmessage = ws.onclose = ws.onerror = null;
5675
+ if (ws.readyState === ws.CONNECTING || ws.readyState === ws.OPEN) {
5676
+ try {
5677
+ ws.close(closeCode);
5678
+ } catch (_unused) {
5679
+ // Nothing more can be done with a broken socket.
5680
+ }
5681
+ }
5682
+ }
5683
+ }, {
5684
+ key: "scheduleReconnect",
5685
+ value: function scheduleReconnect() {
5686
+ var _this$options$reconne,
5687
+ _this$options$reconne2,
5688
+ _this$options$reconne3,
5689
+ _this$options$reconne4,
5690
+ _this4 = this;
5691
+ this.cancelScheduledReconnect();
5692
+ var minDelay = Math.max(0, (_this$options$reconne = (_this$options$reconne2 = this.options.reconnect) === null || _this$options$reconne2 === void 0 ? void 0 : _this$options$reconne2.minDelayMs) !== null && _this$options$reconne !== void 0 ? _this$options$reconne : 1000);
5693
+ var maxDelay = Math.max(minDelay, (_this$options$reconne3 = (_this$options$reconne4 = this.options.reconnect) === null || _this$options$reconne4 === void 0 ? void 0 : _this$options$reconne4.maxDelayMs) !== null && _this$options$reconne3 !== void 0 ? _this$options$reconne3 : 30000);
5694
+ var delay = Math.min(maxDelay, minDelay * Math.pow(2, Math.min(this.reconnectAttempts, 30)));
5695
+ this.reconnectAttempts++;
5696
+
5697
+ // Random jitter (50-100% of the delay) keeps clients from reconnecting
5698
+ // in lockstep after a server restart.
5699
+ this.reconnectTimeoutId = setTimeout(function () {
5700
+ _this4.reconnectTimeoutId = undefined;
5701
+ _this4.connect()["catch"](function () {
5702
+ return undefined;
5703
+ });
5704
+ }, delay / 2 + Math.random() * delay / 2);
5705
+ }
5706
+ }, {
5707
+ key: "cancelScheduledReconnect",
5708
+ value: function cancelScheduledReconnect() {
5709
+ clearTimeout(this.reconnectTimeoutId);
5710
+ this.reconnectTimeoutId = undefined;
5711
+ }
5712
+
5590
5713
  /**
5591
5714
  * Resolve (or reject, when an error is given) a pending connect() promise.
5592
5715
  * No-op when there is nothing pending.
@@ -5629,16 +5752,16 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5629
5752
  }, {
5630
5753
  key: "sendFromQueue",
5631
5754
  value: function sendFromQueue() {
5632
- var _this3 = this;
5755
+ var _this5 = this;
5633
5756
  // Send awaiting data to server
5634
5757
  var lastDelay = 0;
5635
5758
  var _loop = function _loop() {
5636
- var _this3$options$awaitQ;
5637
- var envelope = _this3.sendQueue[dataIndex];
5759
+ var _this5$options$awaitQ;
5760
+ var envelope = _this5.sendQueue[dataIndex];
5638
5761
  setTimeout(function () {
5639
- return _this3.sendEnvelope(envelope);
5762
+ return _this5.sendEnvelope(envelope);
5640
5763
  }, lastDelay);
5641
- lastDelay += (_this3$options$awaitQ = _this3.options.awaitQueueSendDelayMs) !== null && _this3$options$awaitQ !== void 0 ? _this3$options$awaitQ : 500;
5764
+ lastDelay += (_this5$options$awaitQ = _this5.options.awaitQueueSendDelayMs) !== null && _this5$options$awaitQ !== void 0 ? _this5$options$awaitQ : 500;
5642
5765
  };
5643
5766
  for (var dataIndex in this.sendQueue) {
5644
5767
  _loop();
@@ -5649,7 +5772,7 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5649
5772
  }, {
5650
5773
  key: "triggerConnectionTimeout",
5651
5774
  value: function triggerConnectionTimeout() {
5652
- this.disconnect();
5775
+ this.handleConnectionLoss(true);
5653
5776
  this.emit(this.Event.error, new Error('Connection timeout'));
5654
5777
  }
5655
5778
  }, {
@@ -5665,7 +5788,8 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5665
5788
  }, {
5666
5789
  key: "startConnectionMonitor",
5667
5790
  value: function startConnectionMonitor() {
5668
- var _this4 = this;
5791
+ var _this6 = this;
5792
+ this.stopConnectionMonitor();
5669
5793
  if (!this.options.ping.enabled) {
5670
5794
  return;
5671
5795
  }
@@ -5674,30 +5798,32 @@ var WebSocketChatClient = /*#__PURE__*/function (_AbstractChatClient) {
5674
5798
  return WebSocketChatClient_regenerator().w(function (_context3) {
5675
5799
  while (1) switch (_context3.n) {
5676
5800
  case 0:
5677
- if (!(!_this4.isReady || _this4.inFlightPingTimeout)) {
5801
+ if (!(!_this6.isReady || _this6.inFlightPingTimeout)) {
5678
5802
  _context3.n = 1;
5679
5803
  break;
5680
5804
  }
5681
5805
  return _context3.a(2);
5682
5806
  case 1:
5683
- if (!(Date.now() - _this4.lastReceivedMessageAt < _this4.options.ping.noActivityTimeoutMs)) {
5807
+ if (!(Date.now() - _this6.lastReceivedMessageAt < _this6.options.ping.noActivityTimeoutMs)) {
5684
5808
  _context3.n = 2;
5685
5809
  break;
5686
5810
  }
5687
5811
  return _context3.a(2);
5688
5812
  case 2:
5689
- _this4.inFlightPingTimeout = setTimeout(function () {
5690
- _this4.inFlightPingTimeout = undefined;
5691
- _this4.ws.close(3000); // Service Restart (reconnect)
5692
- }, _this4.options.ping.pongBackTimeoutMs);
5813
+ _this6.inFlightPingTimeout = setTimeout(function () {
5814
+ _this6.inFlightPingTimeout = undefined;
5815
+ // Closing a dead connection can hang in CLOSING for minutes,
5816
+ // so drop it right away instead of waiting for the close event.
5817
+ _this6.handleConnectionLoss(true);
5818
+ }, _this6.options.ping.pongBackTimeoutMs);
5693
5819
 
5694
5820
  // A rejection here means the connection dropped while the ping was
5695
- // in flight; onClose already handles that, so just stop waiting.
5696
- _this4.send('Ping', {})["catch"](function () {
5821
+ // in flight; the loss is already handled, so just stop waiting.
5822
+ _this6.send('Ping', {})["catch"](function () {
5697
5823
  return undefined;
5698
5824
  }).then(function () {
5699
- clearTimeout(_this4.inFlightPingTimeout);
5700
- _this4.inFlightPingTimeout = undefined;
5825
+ clearTimeout(_this6.inFlightPingTimeout);
5826
+ _this6.inFlightPingTimeout = undefined;
5701
5827
  });
5702
5828
  case 3:
5703
5829
  return _context3.a(2);
@@ -6134,6 +6260,21 @@ var FilesClient = /*#__PURE__*/function (_AbstractRestClient) {
6134
6260
  }()
6135
6261
  }]);
6136
6262
  }(AbstractRestClient);
6263
+ ;// ./src/types/src/schemes/Role.ts
6264
+ /**
6265
+ * Bit flags of a role, stored in {@link Role.flags}.
6266
+ *
6267
+ * - `SeparateOnMembersList` - available holders of the role are listed in their
6268
+ * own group of the members list, headed by the role name.
6269
+ * - `MentionableByEveryone` - anybody may mention the role with notification
6270
+ * effects; without it the mention is decorative only, unless the author holds
6271
+ * the `MentionAllRoles` permission.
6272
+ */
6273
+ var RoleFlag = /*#__PURE__*/function (RoleFlag) {
6274
+ RoleFlag[RoleFlag["SeparateOnMembersList"] = 1] = "SeparateOnMembersList";
6275
+ RoleFlag[RoleFlag["MentionableByEveryone"] = 2] = "MentionableByEveryone";
6276
+ return RoleFlag;
6277
+ }({});
6137
6278
  ;// ./src/types/src/schemes/User.ts
6138
6279
  /**
6139
6280
  * Availability of a user, aggregated over all of their sessions.