@stomp/stompjs 7.1.0 → 7.2.0

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.
@@ -499,13 +499,13 @@
499
499
  this.disposeInterval();
500
500
  }
501
501
  shouldUseWorker() {
502
- return typeof (Worker) !== 'undefined' && this._strategy === exports.TickerStrategy.Worker;
502
+ return (typeof Worker !== 'undefined' && this._strategy === exports.TickerStrategy.Worker);
503
503
  }
504
504
  runWorker(tick) {
505
505
  this._debug('Using runWorker for outgoing pings');
506
506
  if (!this._worker) {
507
507
  this._worker = new Worker(URL.createObjectURL(new Blob([this._workerScript], { type: 'text/javascript' })));
508
- this._worker.onmessage = (message) => tick(message.data);
508
+ this._worker.onmessage = message => tick(message.data);
509
509
  }
510
510
  }
511
511
  runInterval(tick) {
@@ -671,6 +671,7 @@
671
671
  this.connectHeaders = config.connectHeaders;
672
672
  this.disconnectHeaders = config.disconnectHeaders;
673
673
  this.heartbeatIncoming = config.heartbeatIncoming;
674
+ this.heartbeatToleranceMultiplier = config.heartbeatGracePeriods;
674
675
  this.heartbeatOutgoing = config.heartbeatOutgoing;
675
676
  this.splitLargeFrames = config.splitLargeFrames;
676
677
  this.maxWebSocketChunkSize = config.maxWebSocketChunkSize;
@@ -686,6 +687,8 @@
686
687
  this.onUnhandledMessage = config.onUnhandledMessage;
687
688
  this.onUnhandledReceipt = config.onUnhandledReceipt;
688
689
  this.onUnhandledFrame = config.onUnhandledFrame;
690
+ this.onHeartbeatReceived = config.onHeartbeatReceived;
691
+ this.onHeartbeatLost = config.onHeartbeatLost;
689
692
  }
690
693
  start() {
691
694
  const parser = new Parser(
@@ -702,6 +705,7 @@
702
705
  // On Incoming Ping
703
706
  () => {
704
707
  this.debug('<<< PONG');
708
+ this.onHeartbeatReceived();
705
709
  });
706
710
  this._webSocket.onmessage = (evt) => {
707
711
  this.debug('Received data');
@@ -766,9 +770,10 @@
766
770
  this.debug(`check PONG every ${ttl}ms`);
767
771
  this._ponger = setInterval(() => {
768
772
  const delta = Date.now() - this._lastServerActivityTS;
769
- // We wait twice the TTL to be flexible on window's setInterval calls
770
- if (delta > ttl * 2) {
773
+ // We wait multiple grace periods to be flexible on window's setInterval calls
774
+ if (delta > ttl * this.heartbeatToleranceMultiplier) {
771
775
  this.debug(`did not receive server activity for the last ${delta}ms`);
776
+ this.onHeartbeatLost();
772
777
  this._closeOrDiscardWebsocket();
773
778
  }
774
779
  }, ttl);
@@ -972,16 +977,52 @@
972
977
  * STOMP Client Class.
973
978
  *
974
979
  * Part of `@stomp/stompjs`.
980
+ *
981
+ * This class provides a robust implementation for connecting to and interacting with a
982
+ * STOMP-compliant messaging broker over WebSocket. It supports STOMP versions 1.2, 1.1, and 1.0.
983
+ *
984
+ * Features:
985
+ * - Handles automatic reconnections.
986
+ * - Supports heartbeat mechanisms to detect and report communication failures.
987
+ * - Allows customization of connection and WebSocket behaviors through configurations.
988
+ * - Compatible with both browser environments and Node.js with polyfill support for WebSocket.
975
989
  */
976
990
  class Client {
977
991
  /**
978
- * Underlying WebSocket instance, READONLY.
992
+ * Provides access to the underlying WebSocket instance.
993
+ * This property is **read-only**.
994
+ *
995
+ * Example:
996
+ * ```javascript
997
+ * const webSocket = client.webSocket;
998
+ * if (webSocket) {
999
+ * console.log('WebSocket is connected:', webSocket.readyState === WebSocket.OPEN);
1000
+ * }
1001
+ * ```
1002
+ *
1003
+ * **Caution:**
1004
+ * Directly interacting with the WebSocket instance (e.g., sending or receiving frames)
1005
+ * can interfere with the proper functioning of this library. Such actions may cause
1006
+ * unexpected behavior, disconnections, or invalid state in the library's internal mechanisms.
1007
+ *
1008
+ * Instead, use the library's provided methods to manage STOMP communication.
1009
+ *
1010
+ * @returns The WebSocket instance used by the STOMP handler, or `undefined` if not connected.
979
1011
  */
980
1012
  get webSocket() {
981
1013
  return this._stompHandler?._webSocket;
982
1014
  }
983
1015
  /**
984
- * Disconnection headers.
1016
+ * Allows customization of the disconnection headers.
1017
+ *
1018
+ * Any changes made during an active session will also be applied immediately.
1019
+ *
1020
+ * Example:
1021
+ * ```javascript
1022
+ * client.disconnectHeaders = {
1023
+ * receipt: 'custom-receipt-id'
1024
+ * };
1025
+ * ```
985
1026
  */
986
1027
  get disconnectHeaders() {
987
1028
  return this._disconnectHeaders;
@@ -993,19 +1034,53 @@
993
1034
  }
994
1035
  }
995
1036
  /**
996
- * `true` if there is an active connection to STOMP Broker
1037
+ * Indicates whether there is an active connection to the STOMP broker.
1038
+ *
1039
+ * Usage:
1040
+ * ```javascript
1041
+ * if (client.connected) {
1042
+ * console.log('Client is connected to the broker.');
1043
+ * } else {
1044
+ * console.log('No connection to the broker.');
1045
+ * }
1046
+ * ```
1047
+ *
1048
+ * @returns `true` if the client is currently connected, `false` otherwise.
997
1049
  */
998
1050
  get connected() {
999
1051
  return !!this._stompHandler && this._stompHandler.connected;
1000
1052
  }
1001
1053
  /**
1002
- * version of STOMP protocol negotiated with the server, READONLY
1054
+ * The version of the STOMP protocol negotiated with the server during connection.
1055
+ *
1056
+ * This is a **read-only** property and reflects the negotiated protocol version after
1057
+ * a successful connection.
1058
+ *
1059
+ * Example:
1060
+ * ```javascript
1061
+ * console.log('Connected STOMP version:', client.connectedVersion);
1062
+ * ```
1063
+ *
1064
+ * @returns The negotiated STOMP protocol version or `undefined` if not connected.
1003
1065
  */
1004
1066
  get connectedVersion() {
1005
1067
  return this._stompHandler ? this._stompHandler.connectedVersion : undefined;
1006
1068
  }
1007
1069
  /**
1008
- * if the client is active (connected or going to reconnect)
1070
+ * Indicates whether the client is currently active.
1071
+ *
1072
+ * A client is considered active if it is connected or actively attempting to reconnect.
1073
+ *
1074
+ * Example:
1075
+ * ```javascript
1076
+ * if (client.active) {
1077
+ * console.log('The client is active.');
1078
+ * } else {
1079
+ * console.log('The client is inactive.');
1080
+ * }
1081
+ * ```
1082
+ *
1083
+ * @returns `true` if the client is active, otherwise `false`.
1009
1084
  */
1010
1085
  get active() {
1011
1086
  return this.state === exports.ActivationState.ACTIVE;
@@ -1015,121 +1090,217 @@
1015
1090
  this.onChangeState(state);
1016
1091
  }
1017
1092
  /**
1018
- * Create an instance.
1093
+ * Constructs a new STOMP client instance.
1094
+ *
1095
+ * The constructor initializes default values and sets up no-op callbacks for all events.
1096
+ * Configuration can be passed during construction, or updated later using `configure`.
1097
+ *
1098
+ * Example:
1099
+ * ```javascript
1100
+ * const client = new Client({
1101
+ * brokerURL: 'wss://broker.example.com',
1102
+ * reconnectDelay: 5000
1103
+ * });
1104
+ * ```
1105
+ *
1106
+ * @param conf Optional configuration object to initialize the client with.
1019
1107
  */
1020
1108
  constructor(conf = {}) {
1021
1109
  /**
1022
- * STOMP versions to attempt during STOMP handshake. By default, versions `1.2`, `1.1`, and `1.0` are attempted.
1110
+ * STOMP protocol versions to use during the handshake. By default, the client will attempt
1111
+ * versions `1.2`, `1.1`, and `1.0` in descending order of preference.
1023
1112
  *
1024
1113
  * Example:
1025
1114
  * ```javascript
1026
- * // Try only versions 1.1 and 1.0
1027
- * client.stompVersions = new Versions(['1.1', '1.0'])
1115
+ * // Configure the client to only use versions 1.1 and 1.0
1116
+ * client.stompVersions = new Versions(['1.1', '1.0']);
1028
1117
  * ```
1029
1118
  */
1030
1119
  this.stompVersions = Versions.default;
1031
1120
  /**
1032
- * Will retry if Stomp connection is not established in specified milliseconds.
1033
- * Default 0, which switches off automatic reconnection.
1121
+ * Timeout for establishing STOMP connection, in milliseconds.
1122
+ *
1123
+ * If the connection is not established within this period, the attempt will fail.
1124
+ * The default is `0`, meaning no timeout is set for connection attempts.
1125
+ *
1126
+ * Example:
1127
+ * ```javascript
1128
+ * client.connectionTimeout = 5000; // Fail connection if not established in 5 seconds
1129
+ * ```
1034
1130
  */
1035
1131
  this.connectionTimeout = 0;
1036
1132
  /**
1037
- * automatically reconnect with delay in milliseconds, set to 0 to disable.
1133
+ * Delay (in milliseconds) between reconnection attempts if the connection drops.
1134
+ *
1135
+ * Set to `0` to disable automatic reconnections. The default value is `5000` ms (5 seconds).
1136
+ *
1137
+ * Example:
1138
+ * ```javascript
1139
+ * client.reconnectDelay = 3000; // Attempt reconnection every 3 seconds
1140
+ * client.reconnectDelay = 0; // Disable automatic reconnection
1141
+ * ```
1038
1142
  */
1039
1143
  this.reconnectDelay = 5000;
1040
1144
  /**
1041
- * tracking the time to the next reconnection. Initialized to [Client#reconnectDelay]{@link Client#reconnectDelay}'s value and it may
1042
- * change depending on the [Client#reconnectTimeMode]{@link Client#reconnectTimeMode} setting
1145
+ * The next reconnection delay, used internally.
1146
+ * Initialized to the value of [Client#reconnectDelay]{@link Client#reconnectDelay}, and it may
1147
+ * dynamically change based on [Client#reconnectTimeMode]{@link Client#reconnectTimeMode}.
1043
1148
  */
1044
1149
  this._nextReconnectDelay = 0;
1045
1150
  /**
1046
- * Maximum time to wait between reconnects, in milliseconds. Defaults to 15 minutes.
1047
- * Only relevant when reconnectTimeMode not LINEAR (e.g. EXPONENTIAL).
1048
- * Set to 0 to wait indefinitely.
1151
+ * Maximum delay (in milliseconds) between reconnection attempts when using exponential backoff.
1152
+ *
1153
+ * Default is 15 minutes (`15 * 60 * 1000` milliseconds). If `0`, there will be no upper limit.
1154
+ *
1155
+ * Example:
1156
+ * ```javascript
1157
+ * client.maxReconnectDelay = 10000; // Maximum wait time is 10 seconds
1158
+ * ```
1049
1159
  */
1050
- this.maxReconnectDelay = 15 * 60 * 1000; // 15 minutes in ms
1160
+ this.maxReconnectDelay = 15 * 60 * 1000;
1051
1161
  /**
1052
- * Reconnection wait time mode, either linear (default) or exponential.
1053
- * Note: See [Client#maxReconnectDelay]{@link Client#maxReconnectDelay} for setting the maximum delay when exponential
1162
+ * Mode for determining the time interval between reconnection attempts.
1163
+ *
1164
+ * Available modes:
1165
+ * - `ReconnectionTimeMode.LINEAR` (default): Fixed delays between reconnection attempts.
1166
+ * - `ReconnectionTimeMode.EXPONENTIAL`: Delay doubles after each attempt, capped by [maxReconnectDelay]{@link Client#maxReconnectDelay}.
1167
+ *
1168
+ * Example:
1169
+ * ```javascript
1170
+ * client.reconnectTimeMode = ReconnectionTimeMode.EXPONENTIAL;
1171
+ * client.reconnectDelay = 200; // Initial delay of 200 ms, doubles with each attempt
1172
+ * client.maxReconnectDelay = 2 * 60 * 1000; // Cap delay at 10 minutes
1173
+ * ```
1054
1174
  */
1055
1175
  this.reconnectTimeMode = exports.ReconnectionTimeMode.LINEAR;
1056
1176
  /**
1057
- * Incoming heartbeat interval in milliseconds. Set to 0 to disable.
1177
+ * Interval (in milliseconds) for receiving heartbeat signals from the server.
1178
+ *
1179
+ * Specifies the expected frequency of heartbeats sent by the server. Set to `0` to disable.
1180
+ *
1181
+ * Example:
1182
+ * ```javascript
1183
+ * client.heartbeatIncoming = 10000; // Expect a heartbeat every 10 seconds
1184
+ * ```
1058
1185
  */
1059
1186
  this.heartbeatIncoming = 10000;
1060
1187
  /**
1061
- * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.
1188
+ * Multiplier for adjusting tolerance when processing heartbeat signals.
1189
+ *
1190
+ * Tolerance level is calculated using the multiplier:
1191
+ * `tolerance = heartbeatIncoming * heartbeatToleranceMultiplier`.
1192
+ * This helps account for delays in network communication or variations in timings.
1193
+ *
1194
+ * Default value is `2`.
1195
+ *
1196
+ * Example:
1197
+ * ```javascript
1198
+ * client.heartbeatToleranceMultiplier = 2.5; // Tolerates longer delays
1199
+ * ```
1062
1200
  */
1063
- this.heartbeatOutgoing = 10000;
1201
+ this.heartbeatToleranceMultiplier = 2;
1064
1202
  /**
1065
- * Outgoing heartbeat strategy.
1066
- * See https://github.com/stomp-js/stompjs/pull/579
1203
+ * Interval (in milliseconds) for sending heartbeat signals to the server.
1204
+ *
1205
+ * Specifies how frequently heartbeats should be sent to the server. Set to `0` to disable.
1067
1206
  *
1068
- * Can be worker or interval strategy, but will always use `interval`
1069
- * if web workers are unavailable, for example, in a non-browser environment.
1207
+ * Example:
1208
+ * ```javascript
1209
+ * client.heartbeatOutgoing = 5000; // Send a heartbeat every 5 seconds
1210
+ * ```
1211
+ */
1212
+ this.heartbeatOutgoing = 10000;
1213
+ /**
1214
+ * Strategy for sending outgoing heartbeats.
1070
1215
  *
1071
- * Using Web Workers may work better on long-running pages
1072
- * and mobile apps, as the browser may suspend Timers in the main page.
1073
- * Try the `Worker` mode if you discover disconnects when the browser tab is in the background.
1216
+ * Options:
1217
+ * - `TickerStrategy.Worker`: Uses Web Workers for sending heartbeats (recommended for long-running or background sessions).
1218
+ * - `TickerStrategy.Interval`: Uses standard JavaScript `setInterval` (default).
1074
1219
  *
1075
- * When used in a JS environment, use 'worker' or 'interval' as valid values.
1220
+ * Note:
1221
+ * - If Web Workers are unavailable (e.g., in Node.js), the `Interval` strategy is used automatically.
1222
+ * - Web Workers are preferable in browsers for reducing disconnects when tabs are in the background.
1076
1223
  *
1077
- * Defaults to `interval` strategy.
1224
+ * Example:
1225
+ * ```javascript
1226
+ * client.heartbeatStrategy = TickerStrategy.Worker;
1227
+ * ```
1078
1228
  */
1079
1229
  this.heartbeatStrategy = exports.TickerStrategy.Interval;
1080
1230
  /**
1081
- * This switches on a non-standard behavior while sending WebSocket packets.
1082
- * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
1083
- * Only Java Spring brokers seem to support this mode.
1231
+ * Enables splitting of large text WebSocket frames into smaller chunks.
1084
1232
  *
1085
- * WebSockets, by itself, split large (text) packets,
1086
- * so it is not needed with a truly compliant STOMP/WebSocket broker.
1087
- * Setting it for such a broker will cause large messages to fail.
1233
+ * This setting is enabled for brokers that support only chunked messages (e.g., Java Spring-based brokers).
1234
+ * Default is `false`.
1088
1235
  *
1089
- * `false` by default.
1236
+ * Warning:
1237
+ * - Should not be used with WebSocket-compliant brokers, as chunking may cause large message failures.
1238
+ * - Binary WebSocket frames are never split.
1090
1239
  *
1091
- * Binary frames are never split.
1240
+ * Example:
1241
+ * ```javascript
1242
+ * client.splitLargeFrames = true;
1243
+ * client.maxWebSocketChunkSize = 4096; // Allow chunks of 4 KB
1244
+ * ```
1092
1245
  */
1093
1246
  this.splitLargeFrames = false;
1094
1247
  /**
1095
- * See [splitLargeFrames]{@link Client#splitLargeFrames}.
1096
- * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
1248
+ * Maximum size (in bytes) for individual WebSocket chunks if [splitLargeFrames]{@link Client#splitLargeFrames} is enabled.
1249
+ *
1250
+ * Default is 8 KB (`8 * 1024` bytes). This value has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
1097
1251
  */
1098
1252
  this.maxWebSocketChunkSize = 8 * 1024;
1099
1253
  /**
1100
- * Usually the
1101
- * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}
1102
- * is automatically decided by type of the payload.
1103
- * Default is `false`, which should work with all compliant brokers.
1254
+ * Forces all WebSocket frames to use binary transport, irrespective of payload type.
1104
1255
  *
1105
- * Set this flag to force binary frames.
1256
+ * Default behavior determines frame type based on payload (e.g., binary data for ArrayBuffers).
1257
+ *
1258
+ * Example:
1259
+ * ```javascript
1260
+ * client.forceBinaryWSFrames = true;
1261
+ * ```
1106
1262
  */
1107
1263
  this.forceBinaryWSFrames = false;
1108
1264
  /**
1109
- * A bug in ReactNative chops a string on occurrence of a NULL.
1110
- * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.
1111
- * This makes incoming WebSocket messages invalid STOMP packets.
1112
- * Setting this flag attempts to reverse the damage by appending a NULL.
1113
- * If the broker splits a large message into multiple WebSocket messages,
1114
- * this flag will cause data loss and abnormal termination of connection.
1265
+ * Workaround for a React Native WebSocket bug, where messages containing `NULL` are chopped.
1266
+ *
1267
+ * Enabling this appends a `NULL` character to incoming frames to ensure they remain valid STOMP packets.
1268
+ *
1269
+ * Warning:
1270
+ * - For brokers that split large messages, this may cause data loss or connection termination.
1115
1271
  *
1116
- * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.
1272
+ * Example:
1273
+ * ```javascript
1274
+ * client.appendMissingNULLonIncoming = true;
1275
+ * ```
1117
1276
  */
1118
1277
  this.appendMissingNULLonIncoming = false;
1119
1278
  /**
1120
- * Browsers do not immediately close WebSockets when `.close` is issued.
1121
- * This may cause reconnection to take a significantly long time in case
1122
- * of some types of failures.
1123
- * In case of incoming heartbeat failure, this experimental flag instructs
1124
- * the library to discard the socket immediately
1125
- * (even before it is actually closed).
1279
+ * Instruct the library to immediately terminate the socket on communication failures, even
1280
+ * before the WebSocket is completely closed.
1281
+ *
1282
+ * This is particularly useful in browser environments where WebSocket closure may get delayed,
1283
+ * causing prolonged reconnection intervals under certain failure conditions.
1284
+ *
1285
+ *
1286
+ * Example:
1287
+ * ```javascript
1288
+ * client.discardWebsocketOnCommFailure = true; // Enable aggressive closing of WebSocket
1289
+ * ```
1290
+ *
1291
+ * Default value: `false`.
1126
1292
  */
1127
1293
  this.discardWebsocketOnCommFailure = false;
1128
1294
  /**
1129
- * Activation state.
1295
+ * Current activation state of the client.
1130
1296
  *
1131
- * It will usually be ACTIVE or INACTIVE.
1132
- * When deactivating, it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
1297
+ * Possible states:
1298
+ * - `ActivationState.ACTIVE`: Client is connected or actively attempting to connect.
1299
+ * - `ActivationState.INACTIVE`: Client is disconnected and not attempting to reconnect.
1300
+ * - `ActivationState.DEACTIVATING`: Client is in the process of disconnecting.
1301
+ *
1302
+ * Note: The client may transition directly from `ACTIVE` to `INACTIVE` without entering
1303
+ * the `DEACTIVATING` state.
1133
1304
  */
1134
1305
  this.state = exports.ActivationState.INACTIVE;
1135
1306
  // No op callbacks
@@ -1141,6 +1312,8 @@
1141
1312
  this.onUnhandledMessage = noOp;
1142
1313
  this.onUnhandledReceipt = noOp;
1143
1314
  this.onUnhandledFrame = noOp;
1315
+ this.onHeartbeatReceived = noOp;
1316
+ this.onHeartbeatLost = noOp;
1144
1317
  this.onStompError = noOp;
1145
1318
  this.onWebSocketClose = noOp;
1146
1319
  this.onWebSocketError = noOp;
@@ -1153,7 +1326,22 @@
1153
1326
  this.configure(conf);
1154
1327
  }
1155
1328
  /**
1156
- * Update configuration.
1329
+ * Updates the client's configuration.
1330
+ *
1331
+ * All properties in the provided configuration object will override the current settings.
1332
+ *
1333
+ * Additionally, a warning is logged if `maxReconnectDelay` is configured to a
1334
+ * value lower than `reconnectDelay`, and `maxReconnectDelay` is adjusted to match `reconnectDelay`.
1335
+ *
1336
+ * Example:
1337
+ * ```javascript
1338
+ * client.configure({
1339
+ * reconnectDelay: 3000,
1340
+ * maxReconnectDelay: 10000
1341
+ * });
1342
+ * ```
1343
+ *
1344
+ * @param conf Configuration object containing the new settings.
1157
1345
  */
1158
1346
  configure(conf) {
1159
1347
  // bulk assign all properties to this
@@ -1166,12 +1354,20 @@
1166
1354
  }
1167
1355
  }
1168
1356
  /**
1169
- * Initiate the connection with the broker.
1170
- * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},
1171
- * it will keep trying to reconnect. If the [Client#reconnectTimeMode]{@link Client#reconnectTimeMode}
1172
- * is set to EXPONENTIAL it will increase the wait time exponentially
1357
+ * Activates the client, initiating a connection to the STOMP broker.
1358
+ *
1359
+ * On activation, the client attempts to connect and sets its state to `ACTIVE`. If the connection
1360
+ * is lost, it will automatically retry based on `reconnectDelay` or `maxReconnectDelay`. If
1361
+ * `reconnectTimeMode` is set to `EXPONENTIAL`, the reconnect delay increases exponentially.
1362
+ *
1363
+ * To stop reconnection attempts and disconnect, call [Client#deactivate]{@link Client#deactivate}.
1173
1364
  *
1174
- * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.
1365
+ * Example:
1366
+ * ```javascript
1367
+ * client.activate(); // Connect to the broker
1368
+ * ```
1369
+ *
1370
+ * If the client is currently `DEACTIVATING`, connection is delayed until the deactivation process completes.
1175
1371
  */
1176
1372
  activate() {
1177
1373
  const _activate = () => {
@@ -1229,6 +1425,7 @@
1229
1425
  connectHeaders: this.connectHeaders,
1230
1426
  disconnectHeaders: this._disconnectHeaders,
1231
1427
  heartbeatIncoming: this.heartbeatIncoming,
1428
+ heartbeatGracePeriods: this.heartbeatToleranceMultiplier,
1232
1429
  heartbeatOutgoing: this.heartbeatOutgoing,
1233
1430
  heartbeatStrategy: this.heartbeatStrategy,
1234
1431
  splitLargeFrames: this.splitLargeFrames,
@@ -1281,6 +1478,12 @@
1281
1478
  onUnhandledFrame: frame => {
1282
1479
  this.onUnhandledFrame(frame);
1283
1480
  },
1481
+ onHeartbeatReceived: () => {
1482
+ this.onHeartbeatReceived();
1483
+ },
1484
+ onHeartbeatLost: () => {
1485
+ this.onHeartbeatLost();
1486
+ },
1284
1487
  });
1285
1488
  this._stompHandler.start();
1286
1489
  }
@@ -1314,27 +1517,36 @@
1314
1517
  }
1315
1518
  }
1316
1519
  /**
1317
- * Disconnect if connected and stop auto reconnect loop.
1318
- * Appropriate callbacks will be invoked if there is an underlying STOMP connection.
1520
+ * Disconnects the client and stops the automatic reconnection loop.
1319
1521
  *
1320
- * This call is async. It will resolve immediately if there is no underlying active websocket,
1321
- * otherwise, it will resolve after the underlying websocket is properly disposed of.
1522
+ * If there is an active STOMP connection at the time of invocation, the appropriate callbacks
1523
+ * will be triggered during the shutdown sequence. Once deactivated, the client will enter the
1524
+ * `INACTIVE` state, and no further reconnection attempts will be made.
1322
1525
  *
1323
- * It is not an error to invoke this method more than once.
1324
- * Each of those would resolve on completion of deactivation.
1526
+ * **Behavior**:
1527
+ * - If there is no active WebSocket connection, this method resolves immediately.
1528
+ * - If there is an active connection, the method waits for the underlying WebSocket
1529
+ * to properly close before resolving.
1530
+ * - Multiple calls to this method are safe. Each invocation resolves upon completion.
1531
+ * - To reactivate, call [Client#activate]{@link Client#activate}.
1325
1532
  *
1326
- * To reactivate, you can call [Client#activate]{@link Client#activate}.
1533
+ * **Experimental Option:**
1534
+ * - By specifying the `force: true` option, the WebSocket connection is discarded immediately,
1535
+ * bypassing both the STOMP and WebSocket shutdown sequences.
1536
+ * - **Caution:** Using `force: true` may leave the WebSocket in an inconsistent state,
1537
+ * and brokers may not immediately detect the termination.
1327
1538
  *
1328
- * Experimental: pass `force: true` to immediately discard the underlying connection.
1329
- * This mode will skip both the STOMP and the Websocket shutdown sequences.
1330
- * In some cases, browsers take a long time in the Websocket shutdown
1331
- * if the underlying connection had gone stale.
1332
- * Using this mode can speed up.
1333
- * When this mode is used, the actual Websocket may linger for a while
1334
- * and the broker may not realize that the connection is no longer in use.
1539
+ * Example:
1540
+ * ```javascript
1541
+ * // Graceful disconnect
1542
+ * await client.deactivate();
1543
+ *
1544
+ * // Forced disconnect to speed up shutdown when the connection is stale
1545
+ * await client.deactivate({ force: true });
1546
+ * ```
1335
1547
  *
1336
- * It is possible to invoke this method initially without the `force` option
1337
- * and subsequently, say after a wait, with the `force` option.
1548
+ * @param options Configuration options for deactivation. Use `force: true` for immediate shutdown.
1549
+ * @returns A Promise that resolves when the deactivation process completes.
1338
1550
  */
1339
1551
  async deactivate(options = {}) {
1340
1552
  const force = options.force || false;
@@ -1379,10 +1591,18 @@
1379
1591
  return retPromise;
1380
1592
  }
1381
1593
  /**
1382
- * Force disconnect if there is an active connection by directly closing the underlying WebSocket.
1383
- * This is different from a normal disconnect where a DISCONNECT sequence is carried out with the broker.
1384
- * After forcing disconnect, automatic reconnect will be attempted.
1385
- * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.
1594
+ * Forces a disconnect by directly closing the WebSocket.
1595
+ *
1596
+ * Unlike a normal disconnect, this does not send a DISCONNECT sequence to the broker but
1597
+ * instead closes the WebSocket connection directly. After forcing a disconnect, the client
1598
+ * will automatically attempt to reconnect based on its `reconnectDelay` configuration.
1599
+ *
1600
+ * **Note:** To prevent further reconnect attempts, call [Client#deactivate]{@link Client#deactivate}.
1601
+ *
1602
+ * Example:
1603
+ * ```javascript
1604
+ * client.forceDisconnect();
1605
+ * ```
1386
1606
  */
1387
1607
  forceDisconnect() {
1388
1608
  if (this._stompHandler) {
@@ -1396,39 +1616,38 @@
1396
1616
  }
1397
1617
  }
1398
1618
  /**
1399
- * Send a message to a named destination. Refer to your STOMP broker documentation for types
1400
- * and naming of destinations.
1401
- *
1402
- * STOMP protocol specifies and suggests some headers and also allows broker-specific headers.
1619
+ * Sends a message to the specified destination on the STOMP broker.
1403
1620
  *
1404
- * `body` must be String.
1405
- * You will need to covert the payload to string in case it is not string (e.g. JSON).
1621
+ * The `body` must be a `string`. For non-string payloads (e.g., JSON), encode it as a string before sending.
1622
+ * If sending binary data, use the `binaryBody` parameter as a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
1406
1623
  *
1407
- * To send a binary message body, use `binaryBody` parameter. It should be a
1408
- * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
1409
- * Sometimes brokers may not support binary frames out of the box.
1410
- * Please check your broker documentation.
1624
+ * **Content-Length Behavior**:
1625
+ * - For non-binary messages, the `content-length` header is added by default.
1626
+ * - The `content-length` header can be skipped for text frames by setting `skipContentLengthHeader: true` in the parameters.
1627
+ * - For binary messages, the `content-length` header is always included.
1411
1628
  *
1412
- * `content-length` header is automatically added to the STOMP Frame sent to the broker.
1413
- * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.
1414
- * For binary messages, `content-length` header is always added.
1415
- *
1416
- * Caution: The broker will, most likely, report an error and disconnect
1417
- * if the message body has NULL octet(s) and `content-length` header is missing.
1629
+ * **Notes**:
1630
+ * - Ensure that brokers support binary frames before using `binaryBody`.
1631
+ * - Sending messages with NULL octets and missing `content-length` headers can cause brokers to disconnect and throw errors.
1418
1632
  *
1633
+ * Example:
1419
1634
  * ```javascript
1420
- * client.publish({destination: "/queue/test", headers: {priority: 9}, body: "Hello, STOMP"});
1421
- *
1422
- * // Only destination is mandatory parameter
1423
- * client.publish({destination: "/queue/test", body: "Hello, STOMP"});
1424
- *
1425
- * // Skip content-length header in the frame to the broker
1426
- * client.publish({"/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true});
1427
- *
1428
- * var binaryData = generateBinaryData(); // This need to be of type Uint8Array
1429
- * // setting content-type header is not mandatory, however a good practice
1430
- * client.publish({destination: '/topic/special', binaryBody: binaryData,
1431
- * headers: {'content-type': 'application/octet-stream'}});
1635
+ * // Basic text message
1636
+ * client.publish({ destination: "/queue/test", body: "Hello, STOMP" });
1637
+ *
1638
+ * // Text message with additional headers
1639
+ * client.publish({ destination: "/queue/test", headers: { priority: 9 }, body: "Hello, STOMP" });
1640
+ *
1641
+ * // Skip content-length header
1642
+ * client.publish({ destination: "/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true });
1643
+ *
1644
+ * // Binary message
1645
+ * const binaryData = new Uint8Array([1, 2, 3, 4]);
1646
+ * client.publish({
1647
+ * destination: '/topic/special',
1648
+ * binaryBody: binaryData,
1649
+ * headers: { 'content-type': 'application/octet-stream' }
1650
+ * });
1432
1651
  * ```
1433
1652
  */
1434
1653
  publish(params) {
@@ -1442,39 +1661,27 @@
1442
1661
  }
1443
1662
  }
1444
1663
  /**
1445
- * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.
1446
- * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.
1447
- * The value (say receipt-id) for this header needs to be unique for each use.
1448
- * Typically, a sequence, a UUID, a random number or a combination may be used.
1664
+ * Monitors for a receipt acknowledgment from the broker for specific operations.
1449
1665
  *
1450
- * A complaint broker will send a RECEIPT frame when an operation has actually been completed.
1451
- * The operation needs to be matched based on the value of the receipt-id.
1666
+ * Add a `receipt` header to the operation (like subscribe or publish), and use this method with
1667
+ * the same receipt ID to detect when the broker has acknowledged the operation's completion.
1452
1668
  *
1453
- * This method allows watching for a receipt and invoking the callback
1454
- * when the corresponding receipt has been received.
1455
- *
1456
- * The actual {@link IFrame} will be passed as parameter to the callback.
1669
+ * The callback is invoked with the corresponding {@link IFrame} when the receipt is received.
1457
1670
  *
1458
1671
  * Example:
1459
1672
  * ```javascript
1460
- * // Subscribing with acknowledgement
1461
- * let receiptId = randomText();
1462
- *
1463
- * client.watchForReceipt(receiptId, function() {
1464
- * // Will be called after server acknowledges
1465
- * });
1466
- *
1467
- * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});
1468
- *
1673
+ * const receiptId = "unique-receipt-id";
1469
1674
  *
1470
- * // Publishing with acknowledgement
1471
- * receiptId = randomText();
1675
+ * client.watchForReceipt(receiptId, (frame) => {
1676
+ * console.log("Operation acknowledged by the broker:", frame);
1677
+ * });
1472
1678
  *
1473
- * client.watchForReceipt(receiptId, function() {
1474
- * // Will be called after server acknowledges
1475
- * });
1476
- * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});
1679
+ * // Attach the receipt header to an operation
1680
+ * client.publish({ destination: "/queue/test", headers: { receipt: receiptId }, body: "Hello" });
1477
1681
  * ```
1682
+ *
1683
+ * @param receiptId Unique identifier for the receipt.
1684
+ * @param callback Callback function invoked on receiving the RECEIPT frame.
1478
1685
  */
1479
1686
  watchForReceipt(receiptId, callback) {
1480
1687
  this._checkConnection();
@@ -1482,28 +1689,33 @@
1482
1689
  this._stompHandler.watchForReceipt(receiptId, callback);
1483
1690
  }
1484
1691
  /**
1485
- * Subscribe to a STOMP Broker location. The callback will be invoked for each
1486
- * received message with the {@link IMessage} as argument.
1692
+ * Subscribes to a destination on the STOMP broker.
1487
1693
  *
1488
- * Note: The library will generate a unique ID if there is none provided in the headers.
1489
- * To use your own ID, pass it using the `headers` argument.
1694
+ * The callback is triggered for each message received from the subscribed destination. The message
1695
+ * is passed as an {@link IMessage} instance.
1490
1696
  *
1697
+ * **Subscription ID**:
1698
+ * - If no `id` is provided in `headers`, the library generates a unique subscription ID automatically.
1699
+ * - Provide an explicit `id` in `headers` if you wish to manage the subscription ID manually.
1700
+ *
1701
+ * Example:
1491
1702
  * ```javascript
1492
- * callback = function(message) {
1493
- * // called when the client receives a STOMP message from the server
1494
- * if (message.body) {
1495
- * alert("got message with body " + message.body)
1496
- * } else {
1497
- * alert("got empty message");
1498
- * }
1499
- * });
1703
+ * const callback = (message) => {
1704
+ * console.log("Received message:", message.body);
1705
+ * };
1500
1706
  *
1501
- * var subscription = client.subscribe("/queue/test", callback);
1707
+ * // Auto-generated subscription ID
1708
+ * const subscription = client.subscribe("/queue/test", callback);
1502
1709
  *
1503
- * // Explicit subscription id
1504
- * var mySubId = 'my-subscription-id-001';
1505
- * var subscription = client.subscribe(destination, callback, { id: mySubId });
1710
+ * // Explicit subscription ID
1711
+ * const mySubId = "my-subscription-id";
1712
+ * const subscription = client.subscribe("/queue/test", callback, { id: mySubId });
1506
1713
  * ```
1714
+ *
1715
+ * @param destination Destination to subscribe to.
1716
+ * @param callback Function invoked for each received message.
1717
+ * @param headers Optional headers for subscription, such as `id`.
1718
+ * @returns A {@link StompSubscription} which can be used to manage the subscription.
1507
1719
  */
1508
1720
  subscribe(destination, callback, headers = {}) {
1509
1721
  this._checkConnection();
@@ -1511,16 +1723,24 @@
1511
1723
  return this._stompHandler.subscribe(destination, callback, headers);
1512
1724
  }
1513
1725
  /**
1514
- * It is preferable to unsubscribe from a subscription by calling
1515
- * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:
1726
+ * Unsubscribes from a subscription on the STOMP broker.
1727
+ *
1728
+ * Prefer using the `unsubscribe` method directly on the {@link StompSubscription} returned from `subscribe` for cleaner management:
1729
+ * ```javascript
1730
+ * const subscription = client.subscribe("/queue/test", callback);
1731
+ * // Unsubscribe using the subscription object
1732
+ * subscription.unsubscribe();
1733
+ * ```
1734
+ *
1735
+ * This method can also be used directly with the subscription ID.
1516
1736
  *
1737
+ * Example:
1517
1738
  * ```javascript
1518
- * var subscription = client.subscribe(destination, onmessage);
1519
- * // ...
1520
- * subscription.unsubscribe();
1739
+ * client.unsubscribe("my-subscription-id");
1521
1740
  * ```
1522
1741
  *
1523
- * See: https://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame
1742
+ * @param id Subscription ID to unsubscribe.
1743
+ * @param headers Optional headers to pass for the UNSUBSCRIBE frame.
1524
1744
  */
1525
1745
  unsubscribe(id, headers = {}) {
1526
1746
  this._checkConnection();
@@ -1528,10 +1748,21 @@
1528
1748
  this._stompHandler.unsubscribe(id, headers);
1529
1749
  }
1530
1750
  /**
1531
- * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}
1532
- * and [abort]{@link ITransaction#abort}.
1751
+ * Starts a new transaction. The returned {@link ITransaction} object provides
1752
+ * methods for [commit]{@link ITransaction#commit} and [abort]{@link ITransaction#abort}.
1753
+ *
1754
+ * If `transactionId` is not provided, the library generates a unique ID internally.
1755
+ *
1756
+ * Example:
1757
+ * ```javascript
1758
+ * const tx = client.begin(); // Auto-generated ID
1759
+ *
1760
+ * // Or explicitly specify a transaction ID
1761
+ * const tx = client.begin("my-transaction-id");
1762
+ * ```
1533
1763
  *
1534
- * `transactionId` is optional, if not passed the library will generate it internally.
1764
+ * @param transactionId Optional transaction ID.
1765
+ * @returns An instance of {@link ITransaction}.
1535
1766
  */
1536
1767
  begin(transactionId) {
1537
1768
  this._checkConnection();
@@ -1539,16 +1770,19 @@
1539
1770
  return this._stompHandler.begin(transactionId);
1540
1771
  }
1541
1772
  /**
1542
- * Commit a transaction.
1773
+ * Commits a transaction.
1543
1774
  *
1544
- * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on
1545
- * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
1775
+ * It is strongly recommended to call [commit]{@link ITransaction#commit} on
1776
+ * the transaction object returned by [client#begin]{@link Client#begin}.
1546
1777
  *
1778
+ * Example:
1547
1779
  * ```javascript
1548
- * var tx = client.begin(txId);
1549
- * //...
1550
- * tx.commit();
1780
+ * const tx = client.begin();
1781
+ * // Perform operations under this transaction
1782
+ * tx.commit();
1551
1783
  * ```
1784
+ *
1785
+ * @param transactionId The ID of the transaction to commit.
1552
1786
  */
1553
1787
  commit(transactionId) {
1554
1788
  this._checkConnection();
@@ -1556,15 +1790,19 @@
1556
1790
  this._stompHandler.commit(transactionId);
1557
1791
  }
1558
1792
  /**
1559
- * Abort a transaction.
1560
- * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on
1561
- * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
1793
+ * Aborts a transaction.
1794
+ *
1795
+ * It is strongly recommended to call [abort]{@link ITransaction#abort} directly
1796
+ * on the transaction object returned by [client#begin]{@link Client#begin}.
1562
1797
  *
1798
+ * Example:
1563
1799
  * ```javascript
1564
- * var tx = client.begin(txId);
1565
- * //...
1566
- * tx.abort();
1800
+ * const tx = client.begin();
1801
+ * // Perform operations under this transaction
1802
+ * tx.abort(); // Abort the transaction
1567
1803
  * ```
1804
+ *
1805
+ * @param transactionId The ID of the transaction to abort.
1568
1806
  */
1569
1807
  abort(transactionId) {
1570
1808
  this._checkConnection();
@@ -1572,17 +1810,23 @@
1572
1810
  this._stompHandler.abort(transactionId);
1573
1811
  }
1574
1812
  /**
1575
- * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly
1576
- * on the {@link IMessage} handled by a subscription callback:
1813
+ * Acknowledges receipt of a message. Typically, this should be done by calling
1814
+ * [ack]{@link IMessage#ack} directly on the {@link IMessage} instance passed
1815
+ * to the subscription callback.
1577
1816
  *
1817
+ * Example:
1578
1818
  * ```javascript
1579
- * var callback = function (message) {
1580
- * // process the message
1581
- * // acknowledge it
1582
- * message.ack();
1583
- * };
1584
- * client.subscribe(destination, callback, {'ack': 'client'});
1819
+ * const callback = (message) => {
1820
+ * // Process the message
1821
+ * message.ack(); // Acknowledge the message
1822
+ * };
1823
+ *
1824
+ * client.subscribe("/queue/example", callback, { ack: "client" });
1585
1825
  * ```
1826
+ *
1827
+ * @param messageId The ID of the message to acknowledge.
1828
+ * @param subscriptionId The ID of the subscription.
1829
+ * @param headers Optional headers for the acknowledgment frame.
1586
1830
  */
1587
1831
  ack(messageId, subscriptionId, headers = {}) {
1588
1832
  this._checkConnection();
@@ -1590,17 +1834,25 @@
1590
1834
  this._stompHandler.ack(messageId, subscriptionId, headers);
1591
1835
  }
1592
1836
  /**
1593
- * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly
1594
- * on the {@link IMessage} handled by a subscription callback:
1837
+ * Rejects a message (negative acknowledgment). Like acknowledgments, this should
1838
+ * typically be done by calling [nack]{@link IMessage#nack} directly on the {@link IMessage}
1839
+ * instance passed to the subscription callback.
1595
1840
  *
1841
+ * Example:
1596
1842
  * ```javascript
1597
- * var callback = function (message) {
1598
- * // process the message
1599
- * // an error occurs, nack it
1600
- * message.nack();
1601
- * };
1602
- * client.subscribe(destination, callback, {'ack': 'client'});
1843
+ * const callback = (message) => {
1844
+ * // Process the message
1845
+ * if (isError(message)) {
1846
+ * message.nack(); // Reject the message
1847
+ * }
1848
+ * };
1849
+ *
1850
+ * client.subscribe("/queue/example", callback, { ack: "client" });
1603
1851
  * ```
1852
+ *
1853
+ * @param messageId The ID of the message to negatively acknowledge.
1854
+ * @param subscriptionId The ID of the subscription.
1855
+ * @param headers Optional headers for the NACK frame.
1604
1856
  */
1605
1857
  nack(messageId, subscriptionId, headers = {}) {
1606
1858
  this._checkConnection();
@@ -1620,7 +1872,7 @@
1620
1872
  }
1621
1873
 
1622
1874
  /**
1623
- * STOMP headers. Many functions calls will accept headers as parameters.
1875
+ * STOMP headers. Many function calls will accept headers as parameters.
1624
1876
  * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}.
1625
1877
  *
1626
1878
  * `key` and `value` must be valid strings.