centrifuge 5.0.2 → 5.1.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.
package/README.md CHANGED
@@ -27,6 +27,7 @@ The features implemented by this SDK can be found in [SDK feature matrix](https:
27
27
  * [Protobuf support](#protobuf-support)
28
28
  * [Using with NodeJS](#using-with-nodejs)
29
29
  * [Custom WebSocket constructor](#custom-websocket-constructor)
30
+ * [Run tests locally](#run-tests-locally)
30
31
 
31
32
  ## Install
32
33
 
@@ -815,3 +816,17 @@ var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', {
815
816
  ```
816
817
 
817
818
  See a basic example with React Native where this technique is used [in this comment](https://github.com/centrifugal/centrifuge-js/issues/224#issuecomment-1538820023).
819
+
820
+ ## Run tests locally
821
+
822
+ If you want to run `centrifuge-js` tests locally, start test Centrifugo server:
823
+
824
+ ```
825
+ docker compose up
826
+ ```
827
+
828
+ Then:
829
+
830
+ ```
831
+ yarn test
832
+ ```
@@ -7,6 +7,7 @@ declare const Centrifuge_base: new () => TypedEventEmitter<ClientEvents>;
7
7
  /** Centrifuge is a Centrifuge/Centrifugo bidirectional client. */
8
8
  export declare class Centrifuge extends Centrifuge_base {
9
9
  state: State;
10
+ private _transportIsOpen;
10
11
  private _endpoint;
11
12
  private _emulation;
12
13
  private _transports;
package/build/index.js CHANGED
@@ -783,12 +783,15 @@ class Subscription extends EventEmitter$1 {
783
783
  if (this._setState(exports.SubscriptionState.Subscribing)) {
784
784
  this.emit('subscribing', { channel: this.channel, code: code, reason: reason });
785
785
  }
786
- this._subscribe(false, false);
786
+ this._subscribe();
787
787
  }
788
- _subscribe(optimistic, skipSending) {
788
+ _subscribe() {
789
789
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
790
790
  this._centrifuge._debug('subscribing on', this.channel);
791
- if (this._centrifuge.state !== exports.State.Connected && !optimistic) {
791
+ // need to check transport readiness here, because there's no point for calling getData or getToken
792
+ // if transport is not ready yet
793
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
794
+ if (!this._centrifuge._transportIsOpen) {
792
795
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
793
796
  this._centrifuge._debug('delay subscribe on', this.channel, 'till connected');
794
797
  // subscribe will be called later automatically.
@@ -805,17 +808,14 @@ class Subscription extends EventEmitter$1 {
805
808
  return;
806
809
  }
807
810
  self._data = data;
808
- self._sendSubscribe(self._token, false);
811
+ self._sendSubscribe(self._token);
809
812
  });
810
813
  return null;
811
814
  }
812
815
  else {
813
- return self._sendSubscribe(self._token, skipSending);
816
+ return self._sendSubscribe(self._token);
814
817
  }
815
818
  }
816
- if (optimistic) {
817
- return null;
818
- }
819
819
  this._getSubscriptionToken().then(function (token) {
820
820
  if (!self._isSubscribing()) {
821
821
  return;
@@ -831,11 +831,11 @@ class Subscription extends EventEmitter$1 {
831
831
  return;
832
832
  }
833
833
  self._data = data;
834
- self._sendSubscribe(token, false);
834
+ self._sendSubscribe(token);
835
835
  });
836
836
  }
837
837
  else {
838
- self._sendSubscribe(token, false);
838
+ self._sendSubscribe(token);
839
839
  }
840
840
  }).catch(function (e) {
841
841
  if (!self._isSubscribing()) {
@@ -857,7 +857,13 @@ class Subscription extends EventEmitter$1 {
857
857
  });
858
858
  return null;
859
859
  }
860
- _sendSubscribe(token, skipSending) {
860
+ _sendSubscribe(token) {
861
+ // we also need to check for transport state before sending subscription
862
+ // because it may change for subscription with side effects (getData, getToken options)
863
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
864
+ if (!this._centrifuge._transportIsOpen) {
865
+ return null;
866
+ }
861
867
  const channel = this.channel;
862
868
  const req = {
863
869
  channel: channel
@@ -891,7 +897,7 @@ class Subscription extends EventEmitter$1 {
891
897
  const cmd = { subscribe: req };
892
898
  this._inflight = true;
893
899
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
894
- this._centrifuge._call(cmd, skipSending).then(resolveCtx => {
900
+ this._centrifuge._call(cmd).then(resolveCtx => {
895
901
  this._inflight = false;
896
902
  // @ts-ignore - improve later.
897
903
  const result = resolveCtx.reply.subscribe;
@@ -997,7 +1003,7 @@ class Subscription extends EventEmitter$1 {
997
1003
  const delay = this._getResubscribeDelay();
998
1004
  this._resubscribeTimeout = setTimeout(function () {
999
1005
  if (self._isSubscribing()) {
1000
- self._subscribe(false, false);
1006
+ self._subscribe();
1001
1007
  }
1002
1008
  }, delay);
1003
1009
  }
@@ -1820,6 +1826,7 @@ class Centrifuge extends EventEmitter$1 {
1820
1826
  this._refreshTimeout = null;
1821
1827
  this._serverPingTimeout = null;
1822
1828
  this.state = exports.State.Disconnected;
1829
+ this._transportIsOpen = false;
1823
1830
  this._endpoint = endpoint;
1824
1831
  this._emulation = false;
1825
1832
  this._transports = [];
@@ -2425,25 +2432,10 @@ class Centrifuge extends EventEmitter$1 {
2425
2432
  const transportId = this._nextTransportId();
2426
2433
  self._debug("id of transport", transportId);
2427
2434
  let wasOpen = false;
2428
- let optimistic = true;
2429
- if (this._transport.name() === 'sse') {
2430
- // Avoid using optimistic subscriptions with SSE/EventSource as we are sending
2431
- // initial data in URL params. URL is recommended to be 2048 chars max – so adding
2432
- // subscription data may be risky.
2433
- optimistic = false;
2434
- }
2435
2435
  const initialCommands = [];
2436
2436
  if (this._transport.emulation()) {
2437
2437
  const connectCommand = self._sendConnect(true);
2438
2438
  initialCommands.push(connectCommand);
2439
- if (optimistic) {
2440
- const subscribeCommands = self._sendSubscribeCommands(true, true);
2441
- for (const i in subscribeCommands) {
2442
- if (subscribeCommands.hasOwnProperty(i)) {
2443
- initialCommands.push(subscribeCommands[i]);
2444
- }
2445
- }
2446
- }
2447
2439
  }
2448
2440
  this._setNetworkEvents();
2449
2441
  const initialData = this._codec.encodeCommands(initialCommands);
@@ -2465,16 +2457,17 @@ class Centrifuge extends EventEmitter$1 {
2465
2457
  }
2466
2458
  wasOpen = true;
2467
2459
  self._debug(transport.subName(), 'transport open');
2468
- self._transportWasOpen = true;
2469
2460
  if (transport.emulation()) {
2470
2461
  return;
2471
2462
  }
2463
+ self._transportIsOpen = true;
2464
+ self._transportWasOpen = true;
2472
2465
  self.startBatching();
2473
2466
  self._sendConnect(false);
2474
- if (optimistic) {
2475
- self._sendSubscribeCommands(true, false);
2476
- }
2467
+ self._sendSubscribeCommands();
2477
2468
  self.stopBatching();
2469
+ //@ts-ignore must be used only for debug and test purposes. Exposed only for non-emulation transport.
2470
+ self.emit('__centrifuge_debug:connect_frame_sent', {});
2478
2471
  },
2479
2472
  onError: function (e) {
2480
2473
  if (self._transportId != transportId) {
@@ -2494,6 +2487,7 @@ class Centrifuge extends EventEmitter$1 {
2494
2487
  }
2495
2488
  self._debug(transport.subName(), 'transport closed');
2496
2489
  self._transportClosed = true;
2490
+ self._transportIsOpen = false;
2497
2491
  let reason = 'connection closed';
2498
2492
  let needReconnect = true;
2499
2493
  let code = 0;
@@ -2857,6 +2851,9 @@ class Centrifuge extends EventEmitter$1 {
2857
2851
  if (this._isDisconnected()) {
2858
2852
  return;
2859
2853
  }
2854
+ // we mark transport is closed right away, because _clearConnectedState will move subscriptions to subscribing state
2855
+ // if transport will still be open at this time, subscribe frames will be sent to closing transport
2856
+ this._transportIsOpen = false;
2860
2857
  const previousState = this.state;
2861
2858
  const ctx = {
2862
2859
  code: code,
@@ -2890,6 +2887,8 @@ class Centrifuge extends EventEmitter$1 {
2890
2887
  const transport = this._transport;
2891
2888
  this._transport = null;
2892
2889
  transport.close(); // Close only after setting this._transport to null to avoid recursion when calling transport close().
2890
+ // Need to mark as closed here, because connect call may be sync called after disconnect,
2891
+ // transport onClose callback will not be called yet
2893
2892
  this._transportClosed = true;
2894
2893
  this._nextTransportId();
2895
2894
  }
@@ -2999,7 +2998,7 @@ class Centrifuge extends EventEmitter$1 {
2999
2998
  delete this._subs[sub.channel];
3000
2999
  }
3001
3000
  _unsubscribe(sub) {
3002
- if (!this._isConnected()) {
3001
+ if (!this._transportIsOpen) {
3003
3002
  return;
3004
3003
  }
3005
3004
  const req = {
@@ -3030,7 +3029,7 @@ class Centrifuge extends EventEmitter$1 {
3030
3029
  _isServerSub(channel) {
3031
3030
  return this._serverSubs[channel] !== undefined;
3032
3031
  }
3033
- _sendSubscribeCommands(optimistic, skipSending) {
3032
+ _sendSubscribeCommands() {
3034
3033
  const commands = [];
3035
3034
  for (const channel in this._subs) {
3036
3035
  if (!this._subs.hasOwnProperty(channel)) {
@@ -3043,7 +3042,7 @@ class Centrifuge extends EventEmitter$1 {
3043
3042
  }
3044
3043
  if (sub.state === exports.SubscriptionState.Subscribing) {
3045
3044
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
3046
- const cmd = sub._subscribe(optimistic, skipSending);
3045
+ const cmd = sub._subscribe();
3047
3046
  if (cmd) {
3048
3047
  commands.push(cmd);
3049
3048
  }
@@ -3052,6 +3051,7 @@ class Centrifuge extends EventEmitter$1 {
3052
3051
  return commands;
3053
3052
  }
3054
3053
  _connectResponse(result) {
3054
+ this._transportIsOpen = true;
3055
3055
  this._transportWasOpen = true;
3056
3056
  this._reconnectAttempts = 0;
3057
3057
  this._refreshRequired = false;
@@ -3069,7 +3069,7 @@ class Centrifuge extends EventEmitter$1 {
3069
3069
  this._session = result.session;
3070
3070
  this._node = result.node;
3071
3071
  this.startBatching();
3072
- this._sendSubscribeCommands(false, false);
3072
+ this._sendSubscribeCommands();
3073
3073
  this.stopBatching();
3074
3074
  const ctx = {
3075
3075
  client: result.client,
package/build/index.mjs CHANGED
@@ -781,12 +781,15 @@ class Subscription extends EventEmitter$1 {
781
781
  if (this._setState(SubscriptionState.Subscribing)) {
782
782
  this.emit('subscribing', { channel: this.channel, code: code, reason: reason });
783
783
  }
784
- this._subscribe(false, false);
784
+ this._subscribe();
785
785
  }
786
- _subscribe(optimistic, skipSending) {
786
+ _subscribe() {
787
787
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
788
788
  this._centrifuge._debug('subscribing on', this.channel);
789
- if (this._centrifuge.state !== State.Connected && !optimistic) {
789
+ // need to check transport readiness here, because there's no point for calling getData or getToken
790
+ // if transport is not ready yet
791
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
792
+ if (!this._centrifuge._transportIsOpen) {
790
793
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
791
794
  this._centrifuge._debug('delay subscribe on', this.channel, 'till connected');
792
795
  // subscribe will be called later automatically.
@@ -803,17 +806,14 @@ class Subscription extends EventEmitter$1 {
803
806
  return;
804
807
  }
805
808
  self._data = data;
806
- self._sendSubscribe(self._token, false);
809
+ self._sendSubscribe(self._token);
807
810
  });
808
811
  return null;
809
812
  }
810
813
  else {
811
- return self._sendSubscribe(self._token, skipSending);
814
+ return self._sendSubscribe(self._token);
812
815
  }
813
816
  }
814
- if (optimistic) {
815
- return null;
816
- }
817
817
  this._getSubscriptionToken().then(function (token) {
818
818
  if (!self._isSubscribing()) {
819
819
  return;
@@ -829,11 +829,11 @@ class Subscription extends EventEmitter$1 {
829
829
  return;
830
830
  }
831
831
  self._data = data;
832
- self._sendSubscribe(token, false);
832
+ self._sendSubscribe(token);
833
833
  });
834
834
  }
835
835
  else {
836
- self._sendSubscribe(token, false);
836
+ self._sendSubscribe(token);
837
837
  }
838
838
  }).catch(function (e) {
839
839
  if (!self._isSubscribing()) {
@@ -855,7 +855,13 @@ class Subscription extends EventEmitter$1 {
855
855
  });
856
856
  return null;
857
857
  }
858
- _sendSubscribe(token, skipSending) {
858
+ _sendSubscribe(token) {
859
+ // we also need to check for transport state before sending subscription
860
+ // because it may change for subscription with side effects (getData, getToken options)
861
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
862
+ if (!this._centrifuge._transportIsOpen) {
863
+ return null;
864
+ }
859
865
  const channel = this.channel;
860
866
  const req = {
861
867
  channel: channel
@@ -889,7 +895,7 @@ class Subscription extends EventEmitter$1 {
889
895
  const cmd = { subscribe: req };
890
896
  this._inflight = true;
891
897
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
892
- this._centrifuge._call(cmd, skipSending).then(resolveCtx => {
898
+ this._centrifuge._call(cmd).then(resolveCtx => {
893
899
  this._inflight = false;
894
900
  // @ts-ignore - improve later.
895
901
  const result = resolveCtx.reply.subscribe;
@@ -995,7 +1001,7 @@ class Subscription extends EventEmitter$1 {
995
1001
  const delay = this._getResubscribeDelay();
996
1002
  this._resubscribeTimeout = setTimeout(function () {
997
1003
  if (self._isSubscribing()) {
998
- self._subscribe(false, false);
1004
+ self._subscribe();
999
1005
  }
1000
1006
  }, delay);
1001
1007
  }
@@ -1818,6 +1824,7 @@ class Centrifuge extends EventEmitter$1 {
1818
1824
  this._refreshTimeout = null;
1819
1825
  this._serverPingTimeout = null;
1820
1826
  this.state = State.Disconnected;
1827
+ this._transportIsOpen = false;
1821
1828
  this._endpoint = endpoint;
1822
1829
  this._emulation = false;
1823
1830
  this._transports = [];
@@ -2423,25 +2430,10 @@ class Centrifuge extends EventEmitter$1 {
2423
2430
  const transportId = this._nextTransportId();
2424
2431
  self._debug("id of transport", transportId);
2425
2432
  let wasOpen = false;
2426
- let optimistic = true;
2427
- if (this._transport.name() === 'sse') {
2428
- // Avoid using optimistic subscriptions with SSE/EventSource as we are sending
2429
- // initial data in URL params. URL is recommended to be 2048 chars max – so adding
2430
- // subscription data may be risky.
2431
- optimistic = false;
2432
- }
2433
2433
  const initialCommands = [];
2434
2434
  if (this._transport.emulation()) {
2435
2435
  const connectCommand = self._sendConnect(true);
2436
2436
  initialCommands.push(connectCommand);
2437
- if (optimistic) {
2438
- const subscribeCommands = self._sendSubscribeCommands(true, true);
2439
- for (const i in subscribeCommands) {
2440
- if (subscribeCommands.hasOwnProperty(i)) {
2441
- initialCommands.push(subscribeCommands[i]);
2442
- }
2443
- }
2444
- }
2445
2437
  }
2446
2438
  this._setNetworkEvents();
2447
2439
  const initialData = this._codec.encodeCommands(initialCommands);
@@ -2463,16 +2455,17 @@ class Centrifuge extends EventEmitter$1 {
2463
2455
  }
2464
2456
  wasOpen = true;
2465
2457
  self._debug(transport.subName(), 'transport open');
2466
- self._transportWasOpen = true;
2467
2458
  if (transport.emulation()) {
2468
2459
  return;
2469
2460
  }
2461
+ self._transportIsOpen = true;
2462
+ self._transportWasOpen = true;
2470
2463
  self.startBatching();
2471
2464
  self._sendConnect(false);
2472
- if (optimistic) {
2473
- self._sendSubscribeCommands(true, false);
2474
- }
2465
+ self._sendSubscribeCommands();
2475
2466
  self.stopBatching();
2467
+ //@ts-ignore must be used only for debug and test purposes. Exposed only for non-emulation transport.
2468
+ self.emit('__centrifuge_debug:connect_frame_sent', {});
2476
2469
  },
2477
2470
  onError: function (e) {
2478
2471
  if (self._transportId != transportId) {
@@ -2492,6 +2485,7 @@ class Centrifuge extends EventEmitter$1 {
2492
2485
  }
2493
2486
  self._debug(transport.subName(), 'transport closed');
2494
2487
  self._transportClosed = true;
2488
+ self._transportIsOpen = false;
2495
2489
  let reason = 'connection closed';
2496
2490
  let needReconnect = true;
2497
2491
  let code = 0;
@@ -2855,6 +2849,9 @@ class Centrifuge extends EventEmitter$1 {
2855
2849
  if (this._isDisconnected()) {
2856
2850
  return;
2857
2851
  }
2852
+ // we mark transport is closed right away, because _clearConnectedState will move subscriptions to subscribing state
2853
+ // if transport will still be open at this time, subscribe frames will be sent to closing transport
2854
+ this._transportIsOpen = false;
2858
2855
  const previousState = this.state;
2859
2856
  const ctx = {
2860
2857
  code: code,
@@ -2888,6 +2885,8 @@ class Centrifuge extends EventEmitter$1 {
2888
2885
  const transport = this._transport;
2889
2886
  this._transport = null;
2890
2887
  transport.close(); // Close only after setting this._transport to null to avoid recursion when calling transport close().
2888
+ // Need to mark as closed here, because connect call may be sync called after disconnect,
2889
+ // transport onClose callback will not be called yet
2891
2890
  this._transportClosed = true;
2892
2891
  this._nextTransportId();
2893
2892
  }
@@ -2997,7 +2996,7 @@ class Centrifuge extends EventEmitter$1 {
2997
2996
  delete this._subs[sub.channel];
2998
2997
  }
2999
2998
  _unsubscribe(sub) {
3000
- if (!this._isConnected()) {
2999
+ if (!this._transportIsOpen) {
3001
3000
  return;
3002
3001
  }
3003
3002
  const req = {
@@ -3028,7 +3027,7 @@ class Centrifuge extends EventEmitter$1 {
3028
3027
  _isServerSub(channel) {
3029
3028
  return this._serverSubs[channel] !== undefined;
3030
3029
  }
3031
- _sendSubscribeCommands(optimistic, skipSending) {
3030
+ _sendSubscribeCommands() {
3032
3031
  const commands = [];
3033
3032
  for (const channel in this._subs) {
3034
3033
  if (!this._subs.hasOwnProperty(channel)) {
@@ -3041,7 +3040,7 @@ class Centrifuge extends EventEmitter$1 {
3041
3040
  }
3042
3041
  if (sub.state === SubscriptionState.Subscribing) {
3043
3042
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
3044
- const cmd = sub._subscribe(optimistic, skipSending);
3043
+ const cmd = sub._subscribe();
3045
3044
  if (cmd) {
3046
3045
  commands.push(cmd);
3047
3046
  }
@@ -3050,6 +3049,7 @@ class Centrifuge extends EventEmitter$1 {
3050
3049
  return commands;
3051
3050
  }
3052
3051
  _connectResponse(result) {
3052
+ this._transportIsOpen = true;
3053
3053
  this._transportWasOpen = true;
3054
3054
  this._reconnectAttempts = 0;
3055
3055
  this._refreshRequired = false;
@@ -3067,7 +3067,7 @@ class Centrifuge extends EventEmitter$1 {
3067
3067
  this._session = result.session;
3068
3068
  this._node = result.node;
3069
3069
  this.startBatching();
3070
- this._sendSubscribeCommands(false, false);
3070
+ this._sendSubscribeCommands();
3071
3071
  this.stopBatching();
3072
3072
  const ctx = {
3073
3073
  client: result.client,
@@ -7,6 +7,7 @@ declare const Centrifuge_base: new () => TypedEventEmitter<ClientEvents>;
7
7
  /** Centrifuge is a Centrifuge/Centrifugo bidirectional client. */
8
8
  export declare class Centrifuge extends Centrifuge_base {
9
9
  state: State;
10
+ private _transportIsOpen;
10
11
  private _endpoint;
11
12
  private _emulation;
12
13
  private _transports;
@@ -785,12 +785,15 @@ class Subscription extends EventEmitter$2 {
785
785
  if (this._setState(exports.SubscriptionState.Subscribing)) {
786
786
  this.emit('subscribing', { channel: this.channel, code: code, reason: reason });
787
787
  }
788
- this._subscribe(false, false);
788
+ this._subscribe();
789
789
  }
790
- _subscribe(optimistic, skipSending) {
790
+ _subscribe() {
791
791
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
792
792
  this._centrifuge._debug('subscribing on', this.channel);
793
- if (this._centrifuge.state !== exports.State.Connected && !optimistic) {
793
+ // need to check transport readiness here, because there's no point for calling getData or getToken
794
+ // if transport is not ready yet
795
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
796
+ if (!this._centrifuge._transportIsOpen) {
794
797
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
795
798
  this._centrifuge._debug('delay subscribe on', this.channel, 'till connected');
796
799
  // subscribe will be called later automatically.
@@ -807,17 +810,14 @@ class Subscription extends EventEmitter$2 {
807
810
  return;
808
811
  }
809
812
  self._data = data;
810
- self._sendSubscribe(self._token, false);
813
+ self._sendSubscribe(self._token);
811
814
  });
812
815
  return null;
813
816
  }
814
817
  else {
815
- return self._sendSubscribe(self._token, skipSending);
818
+ return self._sendSubscribe(self._token);
816
819
  }
817
820
  }
818
- if (optimistic) {
819
- return null;
820
- }
821
821
  this._getSubscriptionToken().then(function (token) {
822
822
  if (!self._isSubscribing()) {
823
823
  return;
@@ -833,11 +833,11 @@ class Subscription extends EventEmitter$2 {
833
833
  return;
834
834
  }
835
835
  self._data = data;
836
- self._sendSubscribe(token, false);
836
+ self._sendSubscribe(token);
837
837
  });
838
838
  }
839
839
  else {
840
- self._sendSubscribe(token, false);
840
+ self._sendSubscribe(token);
841
841
  }
842
842
  }).catch(function (e) {
843
843
  if (!self._isSubscribing()) {
@@ -859,7 +859,13 @@ class Subscription extends EventEmitter$2 {
859
859
  });
860
860
  return null;
861
861
  }
862
- _sendSubscribe(token, skipSending) {
862
+ _sendSubscribe(token) {
863
+ // we also need to check for transport state before sending subscription
864
+ // because it may change for subscription with side effects (getData, getToken options)
865
+ // @ts-ignore – we are hiding some symbols from public API autocompletion.
866
+ if (!this._centrifuge._transportIsOpen) {
867
+ return null;
868
+ }
863
869
  const channel = this.channel;
864
870
  const req = {
865
871
  channel: channel
@@ -893,7 +899,7 @@ class Subscription extends EventEmitter$2 {
893
899
  const cmd = { subscribe: req };
894
900
  this._inflight = true;
895
901
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
896
- this._centrifuge._call(cmd, skipSending).then(resolveCtx => {
902
+ this._centrifuge._call(cmd).then(resolveCtx => {
897
903
  this._inflight = false;
898
904
  // @ts-ignore - improve later.
899
905
  const result = resolveCtx.reply.subscribe;
@@ -999,7 +1005,7 @@ class Subscription extends EventEmitter$2 {
999
1005
  const delay = this._getResubscribeDelay();
1000
1006
  this._resubscribeTimeout = setTimeout(function () {
1001
1007
  if (self._isSubscribing()) {
1002
- self._subscribe(false, false);
1008
+ self._subscribe();
1003
1009
  }
1004
1010
  }, delay);
1005
1011
  }
@@ -1822,6 +1828,7 @@ class Centrifuge extends EventEmitter$2 {
1822
1828
  this._refreshTimeout = null;
1823
1829
  this._serverPingTimeout = null;
1824
1830
  this.state = exports.State.Disconnected;
1831
+ this._transportIsOpen = false;
1825
1832
  this._endpoint = endpoint;
1826
1833
  this._emulation = false;
1827
1834
  this._transports = [];
@@ -2427,25 +2434,10 @@ class Centrifuge extends EventEmitter$2 {
2427
2434
  const transportId = this._nextTransportId();
2428
2435
  self._debug("id of transport", transportId);
2429
2436
  let wasOpen = false;
2430
- let optimistic = true;
2431
- if (this._transport.name() === 'sse') {
2432
- // Avoid using optimistic subscriptions with SSE/EventSource as we are sending
2433
- // initial data in URL params. URL is recommended to be 2048 chars max – so adding
2434
- // subscription data may be risky.
2435
- optimistic = false;
2436
- }
2437
2437
  const initialCommands = [];
2438
2438
  if (this._transport.emulation()) {
2439
2439
  const connectCommand = self._sendConnect(true);
2440
2440
  initialCommands.push(connectCommand);
2441
- if (optimistic) {
2442
- const subscribeCommands = self._sendSubscribeCommands(true, true);
2443
- for (const i in subscribeCommands) {
2444
- if (subscribeCommands.hasOwnProperty(i)) {
2445
- initialCommands.push(subscribeCommands[i]);
2446
- }
2447
- }
2448
- }
2449
2441
  }
2450
2442
  this._setNetworkEvents();
2451
2443
  const initialData = this._codec.encodeCommands(initialCommands);
@@ -2467,16 +2459,17 @@ class Centrifuge extends EventEmitter$2 {
2467
2459
  }
2468
2460
  wasOpen = true;
2469
2461
  self._debug(transport.subName(), 'transport open');
2470
- self._transportWasOpen = true;
2471
2462
  if (transport.emulation()) {
2472
2463
  return;
2473
2464
  }
2465
+ self._transportIsOpen = true;
2466
+ self._transportWasOpen = true;
2474
2467
  self.startBatching();
2475
2468
  self._sendConnect(false);
2476
- if (optimistic) {
2477
- self._sendSubscribeCommands(true, false);
2478
- }
2469
+ self._sendSubscribeCommands();
2479
2470
  self.stopBatching();
2471
+ //@ts-ignore must be used only for debug and test purposes. Exposed only for non-emulation transport.
2472
+ self.emit('__centrifuge_debug:connect_frame_sent', {});
2480
2473
  },
2481
2474
  onError: function (e) {
2482
2475
  if (self._transportId != transportId) {
@@ -2496,6 +2489,7 @@ class Centrifuge extends EventEmitter$2 {
2496
2489
  }
2497
2490
  self._debug(transport.subName(), 'transport closed');
2498
2491
  self._transportClosed = true;
2492
+ self._transportIsOpen = false;
2499
2493
  let reason = 'connection closed';
2500
2494
  let needReconnect = true;
2501
2495
  let code = 0;
@@ -2859,6 +2853,9 @@ class Centrifuge extends EventEmitter$2 {
2859
2853
  if (this._isDisconnected()) {
2860
2854
  return;
2861
2855
  }
2856
+ // we mark transport is closed right away, because _clearConnectedState will move subscriptions to subscribing state
2857
+ // if transport will still be open at this time, subscribe frames will be sent to closing transport
2858
+ this._transportIsOpen = false;
2862
2859
  const previousState = this.state;
2863
2860
  const ctx = {
2864
2861
  code: code,
@@ -2892,6 +2889,8 @@ class Centrifuge extends EventEmitter$2 {
2892
2889
  const transport = this._transport;
2893
2890
  this._transport = null;
2894
2891
  transport.close(); // Close only after setting this._transport to null to avoid recursion when calling transport close().
2892
+ // Need to mark as closed here, because connect call may be sync called after disconnect,
2893
+ // transport onClose callback will not be called yet
2895
2894
  this._transportClosed = true;
2896
2895
  this._nextTransportId();
2897
2896
  }
@@ -3001,7 +3000,7 @@ class Centrifuge extends EventEmitter$2 {
3001
3000
  delete this._subs[sub.channel];
3002
3001
  }
3003
3002
  _unsubscribe(sub) {
3004
- if (!this._isConnected()) {
3003
+ if (!this._transportIsOpen) {
3005
3004
  return;
3006
3005
  }
3007
3006
  const req = {
@@ -3032,7 +3031,7 @@ class Centrifuge extends EventEmitter$2 {
3032
3031
  _isServerSub(channel) {
3033
3032
  return this._serverSubs[channel] !== undefined;
3034
3033
  }
3035
- _sendSubscribeCommands(optimistic, skipSending) {
3034
+ _sendSubscribeCommands() {
3036
3035
  const commands = [];
3037
3036
  for (const channel in this._subs) {
3038
3037
  if (!this._subs.hasOwnProperty(channel)) {
@@ -3045,7 +3044,7 @@ class Centrifuge extends EventEmitter$2 {
3045
3044
  }
3046
3045
  if (sub.state === exports.SubscriptionState.Subscribing) {
3047
3046
  // @ts-ignore – we are hiding some symbols from public API autocompletion.
3048
- const cmd = sub._subscribe(optimistic, skipSending);
3047
+ const cmd = sub._subscribe();
3049
3048
  if (cmd) {
3050
3049
  commands.push(cmd);
3051
3050
  }
@@ -3054,6 +3053,7 @@ class Centrifuge extends EventEmitter$2 {
3054
3053
  return commands;
3055
3054
  }
3056
3055
  _connectResponse(result) {
3056
+ this._transportIsOpen = true;
3057
3057
  this._transportWasOpen = true;
3058
3058
  this._reconnectAttempts = 0;
3059
3059
  this._refreshRequired = false;
@@ -3071,7 +3071,7 @@ class Centrifuge extends EventEmitter$2 {
3071
3071
  this._session = result.session;
3072
3072
  this._node = result.node;
3073
3073
  this.startBatching();
3074
- this._sendSubscribeCommands(false, false);
3074
+ this._sendSubscribeCommands();
3075
3075
  this.stopBatching();
3076
3076
  const ctx = {
3077
3077
  client: result.client,