cross-tab-worker-databus 0.21.57 → 0.21.59
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/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.js +1 -1
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +1682 -644
- package/dist/centrifuge.shared.worker.js.map +3 -3
- package/dist/centrifuge.worker.js +1682 -644
- package/dist/centrifuge.worker.js.map +3 -3
- package/dist/{chunk-ZH7VP3IY.js → chunk-SGZG6775.js} +3 -3
- package/dist/chunk-SGZG6775.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +2 -2
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +2 -2
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/api.md +4 -0
- package/docs/configuration.md +1 -1
- package/docs/roadmap.md +1 -1
- package/docs/zh/api.md +4 -0
- package/docs/zh/configuration.md +1 -1
- package/docs/zh/roadmap.md +1 -1
- package/package.json +13 -13
- package/dist/chunk-ZH7VP3IY.js.map +0 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// node_modules/.pnpm/centrifuge@5.
|
|
1
|
+
// node_modules/.pnpm/centrifuge@5.8.0/node_modules/centrifuge/build/index.mjs
|
|
2
2
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
3
3
|
function adopt(value) {
|
|
4
4
|
return value instanceof P ? value : new P(function(resolve) {
|
|
@@ -504,6 +504,23 @@ function localStorageItem(key) {
|
|
|
504
504
|
return null;
|
|
505
505
|
}
|
|
506
506
|
}
|
|
507
|
+
function hasOffset(offset) {
|
|
508
|
+
return offset !== void 0 && offset !== null && Number(offset) > 0;
|
|
509
|
+
}
|
|
510
|
+
function toOffset(offset) {
|
|
511
|
+
if (offset === void 0 || offset === null) {
|
|
512
|
+
return 0;
|
|
513
|
+
}
|
|
514
|
+
if (typeof offset === "object") {
|
|
515
|
+
const low = offset.low;
|
|
516
|
+
const high = offset.high;
|
|
517
|
+
if (typeof low === "number" && typeof high === "number") {
|
|
518
|
+
return (high >>> 0) * 4294967296 + (low >>> 0);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const value = Number(offset);
|
|
522
|
+
return isNaN(value) ? 0 : value;
|
|
523
|
+
}
|
|
507
524
|
var MapPhase;
|
|
508
525
|
(function(MapPhase2) {
|
|
509
526
|
MapPhase2[MapPhase2["Live"] = 0] = "Live";
|
|
@@ -516,6 +533,8 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
516
533
|
super();
|
|
517
534
|
this._resubscribeTimeout = null;
|
|
518
535
|
this._refreshTimeout = null;
|
|
536
|
+
this._pendingOffset = null;
|
|
537
|
+
this._removed = false;
|
|
519
538
|
this._getState = null;
|
|
520
539
|
this._map = false;
|
|
521
540
|
this._mapPresenceType = 1;
|
|
@@ -525,6 +544,18 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
525
544
|
this._mapCursor = "";
|
|
526
545
|
this._mapPageSize = 0;
|
|
527
546
|
this._mapUnrecoverableStrategy = "from_scratch";
|
|
547
|
+
this._mapPositionResets = 0;
|
|
548
|
+
this._subscribeFlow = 0;
|
|
549
|
+
this._refreshGeneration = 0;
|
|
550
|
+
this._sharedPollReplayPending = false;
|
|
551
|
+
this._stateTransitions = 0;
|
|
552
|
+
this._sharedPollQueue = Promise.resolve();
|
|
553
|
+
this._subscribeCallFlow = null;
|
|
554
|
+
this._deferredUnsubscribe = null;
|
|
555
|
+
this._mapFlowRecovering = false;
|
|
556
|
+
this._mapFlowOffset = 0;
|
|
557
|
+
this._mapFlowEpoch = null;
|
|
558
|
+
this._mapFlowPositionResets = 0;
|
|
528
559
|
this._debounceMs = 0;
|
|
529
560
|
this._debouncePending = /* @__PURE__ */ new Map();
|
|
530
561
|
this._sharedPoll = false;
|
|
@@ -540,6 +571,8 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
540
571
|
this._sharedPollSignatures = [];
|
|
541
572
|
this._sharedPollSignatureRefreshTargetMs = null;
|
|
542
573
|
this._sharedPollSignatureRefreshInFlight = false;
|
|
574
|
+
this._sharedPollSignatureRefreshAgain = false;
|
|
575
|
+
this._sharedPollSignatureRefreshKeys = /* @__PURE__ */ new Set();
|
|
543
576
|
this.channel = channel;
|
|
544
577
|
this.state = SubscriptionState.Unsubscribed;
|
|
545
578
|
this._centrifuge = centrifuge;
|
|
@@ -598,29 +631,36 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
598
631
|
return Promise.resolve();
|
|
599
632
|
}
|
|
600
633
|
return new Promise((res, rej) => {
|
|
634
|
+
const id = this._nextPromiseId();
|
|
601
635
|
const ctx = {
|
|
602
636
|
resolve: res,
|
|
603
637
|
reject: rej
|
|
604
638
|
};
|
|
605
639
|
if (timeout) {
|
|
606
|
-
ctx.timeout = setTimeout(
|
|
640
|
+
ctx.timeout = setTimeout(() => {
|
|
641
|
+
delete this._promises[id];
|
|
607
642
|
rej({ code: errorCodes.timeout, message: "timeout" });
|
|
608
643
|
}, timeout);
|
|
609
644
|
}
|
|
610
|
-
this._promises[
|
|
645
|
+
this._promises[id] = ctx;
|
|
611
646
|
});
|
|
612
647
|
}
|
|
613
648
|
/** subscribe to a channel.*/
|
|
614
649
|
subscribe() {
|
|
650
|
+
if (this._removed) {
|
|
651
|
+
throw new Error("Subscription to the channel " + this.channel + " was removed from the client");
|
|
652
|
+
}
|
|
615
653
|
if (this._isSubscribed()) {
|
|
616
654
|
return;
|
|
617
655
|
}
|
|
618
|
-
this.
|
|
656
|
+
if (this._isUnsubscribed()) {
|
|
657
|
+
this._resubscribeAttempts = 0;
|
|
658
|
+
}
|
|
619
659
|
this._setSubscribing(subscribingCodes.subscribeCalled, "subscribe called");
|
|
620
660
|
}
|
|
621
661
|
/** unsubscribe from a channel, keeping position state.*/
|
|
622
662
|
unsubscribe() {
|
|
623
|
-
this.
|
|
663
|
+
this._setUnsubscribed(unsubscribedCodes.unsubscribeCalled, "unsubscribe called", true);
|
|
624
664
|
}
|
|
625
665
|
_debouncedPublish(key, data, isMap) {
|
|
626
666
|
const existing = this._debouncePending.get(key);
|
|
@@ -648,7 +688,8 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
648
688
|
return;
|
|
649
689
|
}
|
|
650
690
|
this._debouncePending.delete(key);
|
|
651
|
-
this._debouncedPublish(key, p.data, isMap)
|
|
691
|
+
this._debouncedPublish(key, p.data, isMap).catch(() => {
|
|
692
|
+
});
|
|
652
693
|
}, this._debounceMs);
|
|
653
694
|
}, this._debounceMs);
|
|
654
695
|
this._debouncePending.set(key, entry);
|
|
@@ -731,6 +772,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
731
772
|
this._recover = false;
|
|
732
773
|
this._offset = null;
|
|
733
774
|
this._epoch = null;
|
|
775
|
+
this._mapPositionResets++;
|
|
734
776
|
}
|
|
735
777
|
}
|
|
736
778
|
/** setData allows setting subscription data. This only applied on the next subscription attempt,
|
|
@@ -763,10 +805,12 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
763
805
|
}
|
|
764
806
|
return new Promise((resolve, reject) => {
|
|
765
807
|
const timeoutDuration = this._centrifuge._config.timeout;
|
|
808
|
+
const id = this._nextPromiseId();
|
|
766
809
|
const timeout = setTimeout(() => {
|
|
810
|
+
delete this._promises[id];
|
|
767
811
|
reject({ code: errorCodes.timeout, message: "timeout" });
|
|
768
812
|
}, timeoutDuration);
|
|
769
|
-
this._promises[
|
|
813
|
+
this._promises[id] = {
|
|
770
814
|
timeout,
|
|
771
815
|
resolve,
|
|
772
816
|
reject
|
|
@@ -792,6 +836,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
792
836
|
if (this.state !== newState) {
|
|
793
837
|
const oldState = this.state;
|
|
794
838
|
this.state = newState;
|
|
839
|
+
this._stateTransitions++;
|
|
795
840
|
this.emit("state", { newState, oldState, channel: this.channel });
|
|
796
841
|
return true;
|
|
797
842
|
}
|
|
@@ -806,18 +851,24 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
806
851
|
}
|
|
807
852
|
_clearSubscribedState() {
|
|
808
853
|
this._clearRefreshTimeout();
|
|
854
|
+
this._refreshGeneration++;
|
|
809
855
|
this._cancelAllDebounce();
|
|
810
856
|
this._clearSharedPollSignatureRefresh();
|
|
811
857
|
this._clearSharedPollTrackRetry();
|
|
812
858
|
this._clearSharedPollReplayRetry();
|
|
813
859
|
this._sharedPollSignatureRefreshTargetMs = null;
|
|
814
860
|
this._sharedPollSignatureRefreshInFlight = false;
|
|
861
|
+
this._sharedPollSignatureRefreshAgain = false;
|
|
815
862
|
}
|
|
816
863
|
/** Called on "state invalidated" — unsubscribe code 2502 for this channel,
|
|
817
|
-
* or connection disconnect code 3014. Clears the token
|
|
818
|
-
* fetches a fresh one) and the fossil delta base (every
|
|
819
|
-
* uses _prevValueMap; a stale base would corrupt decoding
|
|
820
|
-
* publication after re-subscribe).
|
|
864
|
+
* or connection disconnect code 3014. Clears the token when getToken is set
|
|
865
|
+
* (next subscribe fetches a fresh one) and the fossil delta base (every
|
|
866
|
+
* subscription type uses _prevValueMap; a stale base would corrupt decoding
|
|
867
|
+
* of the first publication after re-subscribe).
|
|
868
|
+
*
|
|
869
|
+
* Without getToken there is no new token to get: the subscription keeps the
|
|
870
|
+
* token it has (or none) and resubscribes with it. The server rejects it if
|
|
871
|
+
* it's no longer valid.
|
|
821
872
|
*
|
|
822
873
|
* Map subscriptions restart from scratch: their recovery position and
|
|
823
874
|
* materialized-state buffers are dropped so the next subscribe does a full
|
|
@@ -831,17 +882,20 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
831
882
|
* subscribe), while a non-recoverable one simply resubscribes. The real
|
|
832
883
|
* epoch/offset are adopted from the subscribe reply. */
|
|
833
884
|
_invalidateState() {
|
|
834
|
-
this.
|
|
885
|
+
if (this._getToken !== null) {
|
|
886
|
+
this._token = "";
|
|
887
|
+
}
|
|
835
888
|
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
836
889
|
if (this._map) {
|
|
837
890
|
this._offset = null;
|
|
838
891
|
this._epoch = null;
|
|
839
892
|
this._recover = false;
|
|
893
|
+
this._mapPositionResets++;
|
|
840
894
|
this._mapStateBuffer = [];
|
|
841
895
|
this._mapStreamBuffer = [];
|
|
842
896
|
this._mapCursor = "";
|
|
843
897
|
this._mapPhase = null;
|
|
844
|
-
} else {
|
|
898
|
+
} else if (this._offset !== null) {
|
|
845
899
|
this._offset = 0;
|
|
846
900
|
this._epoch = "_";
|
|
847
901
|
}
|
|
@@ -856,8 +910,10 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
856
910
|
}
|
|
857
911
|
if (result.recoverable) {
|
|
858
912
|
this._recover = true;
|
|
859
|
-
this._offset = result.offset
|
|
913
|
+
this._offset = toOffset(result.offset);
|
|
860
914
|
this._epoch = result.epoch || "";
|
|
915
|
+
} else {
|
|
916
|
+
this._recover = false;
|
|
861
917
|
}
|
|
862
918
|
if (result.delta) {
|
|
863
919
|
this._delta_negotiated = true;
|
|
@@ -877,19 +933,20 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
877
933
|
this._sharedPollEpoch = newEpoch;
|
|
878
934
|
}
|
|
879
935
|
this._setState(SubscriptionState.Subscribed);
|
|
936
|
+
if (!this._isSubscribed()) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
880
939
|
const ctx = this._centrifuge._getSubscribeContext(this.channel, result);
|
|
881
940
|
this.emit("subscribed", ctx);
|
|
941
|
+
if (!this._isSubscribed()) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
882
944
|
this._resolvePromises();
|
|
883
|
-
const pubs = result.publications;
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (!pubs.hasOwnProperty(i)) {
|
|
887
|
-
continue;
|
|
888
|
-
}
|
|
889
|
-
this._handlePublication(pubs[i]);
|
|
890
|
-
}
|
|
945
|
+
const pubs = result.publications || [];
|
|
946
|
+
for (let i = 0; i < pubs.length && this._isSubscribed(); i++) {
|
|
947
|
+
this._handlePublication(pubs[i]);
|
|
891
948
|
}
|
|
892
|
-
if (result.expires === true) {
|
|
949
|
+
if (result.expires === true && this._isSubscribed()) {
|
|
893
950
|
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
894
951
|
}
|
|
895
952
|
}
|
|
@@ -902,11 +959,14 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
902
959
|
this._clearSubscribedState();
|
|
903
960
|
}
|
|
904
961
|
this._id = 0;
|
|
905
|
-
|
|
962
|
+
const transition = this._stateTransitions + 1;
|
|
963
|
+
if (this._setState(SubscriptionState.Subscribing) && this._stateTransitions === transition) {
|
|
906
964
|
this.emit("subscribing", { channel: this.channel, code, reason });
|
|
907
965
|
}
|
|
908
966
|
if (this._centrifuge._transport && this._centrifuge._transport.emulation()) {
|
|
909
967
|
yield this._unsubPromise;
|
|
968
|
+
yield this._centrifuge._removedSubscriptionUnsubscribed(this.channel);
|
|
969
|
+
yield this._sharedPollQueue;
|
|
910
970
|
}
|
|
911
971
|
if (!this._isSubscribing()) {
|
|
912
972
|
return;
|
|
@@ -928,25 +988,60 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
928
988
|
this._mapSubscribe();
|
|
929
989
|
return null;
|
|
930
990
|
}
|
|
991
|
+
const flow = this._subscribeFlow;
|
|
931
992
|
if (this._getState && this._offset === null) {
|
|
932
|
-
this._loadStreamState();
|
|
993
|
+
this._loadStreamState(flow);
|
|
933
994
|
return null;
|
|
934
995
|
}
|
|
935
996
|
if (this._canSubscribeWithoutGettingToken()) {
|
|
936
|
-
return this._subscribeWithoutToken();
|
|
997
|
+
return this._subscribeWithoutToken(flow);
|
|
937
998
|
}
|
|
938
|
-
this._getSubscriptionToken().then((token) => this._handleTokenResponse(token)).catch((e) => this._handleTokenError(e));
|
|
999
|
+
this._getSubscriptionToken().then((token) => this._handleTokenResponse(token, flow)).catch((e) => this._handleTokenError(e, flow));
|
|
939
1000
|
return null;
|
|
940
1001
|
}
|
|
1002
|
+
_flowOutdated(flow) {
|
|
1003
|
+
return flow !== this._subscribeFlow;
|
|
1004
|
+
}
|
|
1005
|
+
// Over emulation commands go in separate requests the server may handle in any
|
|
1006
|
+
// order: an unsubscribe of a subscribe in flight could overtake it, and the
|
|
1007
|
+
// server would keep the subscription. The unsubscribe waits for the
|
|
1008
|
+
// subscribe's reply then.
|
|
1009
|
+
_mustDeferUnsubscribe() {
|
|
1010
|
+
const transport = this._centrifuge._transport;
|
|
1011
|
+
return this._subscribeCallFlow === this._subscribeFlow && !!transport && transport.emulation();
|
|
1012
|
+
}
|
|
1013
|
+
// Called first by the reply handlers of a subscribe command or map page. True
|
|
1014
|
+
// when the reply belongs to a subscribe whose unsubscribe was deferred, which is
|
|
1015
|
+
// handled here: the unsubscribe goes out if the subscribe succeeded. A timeout
|
|
1016
|
+
// leaves unknown whether the server subscribed: the client reconnects.
|
|
1017
|
+
_settleSubscribeCall(flow, ok, error) {
|
|
1018
|
+
if (this._subscribeCallFlow === flow) {
|
|
1019
|
+
this._subscribeCallFlow = null;
|
|
1020
|
+
}
|
|
1021
|
+
const deferred = this._deferredUnsubscribe;
|
|
1022
|
+
if (deferred === null || deferred.flow !== flow) {
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
this._deferredUnsubscribe = null;
|
|
1026
|
+
if (ok) {
|
|
1027
|
+
this._centrifuge._unsubscribe(this).then(deferred.resolve);
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1030
|
+
deferred.resolve();
|
|
1031
|
+
if (error && error.code === errorCodes.timeout) {
|
|
1032
|
+
this._centrifuge._disconnect(connectingCodes.subscribeTimeout, "subscribe timeout", true);
|
|
1033
|
+
}
|
|
1034
|
+
return true;
|
|
1035
|
+
}
|
|
941
1036
|
_isTransportOpen() {
|
|
942
1037
|
return this._centrifuge._transportIsOpen;
|
|
943
1038
|
}
|
|
944
1039
|
_canSubscribeWithoutGettingToken() {
|
|
945
1040
|
return !this._usesToken() || !!this._token;
|
|
946
1041
|
}
|
|
947
|
-
_subscribeWithoutToken() {
|
|
1042
|
+
_subscribeWithoutToken(flow) {
|
|
948
1043
|
if (this._getData) {
|
|
949
|
-
this._getDataAndSubscribe(this._token);
|
|
1044
|
+
this._getDataAndSubscribe(this._token, flow);
|
|
950
1045
|
return null;
|
|
951
1046
|
} else {
|
|
952
1047
|
return this._sendSubscribe(this._token);
|
|
@@ -969,25 +1064,31 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
969
1064
|
* This order is critical: reading position first ensures it's a lower bound.
|
|
970
1065
|
* Recovered publications may overlap with data the app already loaded — this
|
|
971
1066
|
* requires idempotent updates or offset-based dedup. */
|
|
972
|
-
_loadStreamState() {
|
|
1067
|
+
_loadStreamState(flow) {
|
|
973
1068
|
if (!this._isSubscribing()) {
|
|
974
1069
|
this._inflight = false;
|
|
975
1070
|
return;
|
|
976
1071
|
}
|
|
977
|
-
this._getState().then((result) => {
|
|
1072
|
+
this._callAsync(() => this._getState()).then((result) => {
|
|
1073
|
+
if (this._flowOutdated(flow)) {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
978
1076
|
if (!this._isSubscribing()) {
|
|
979
1077
|
this._inflight = false;
|
|
980
1078
|
return;
|
|
981
1079
|
}
|
|
982
|
-
this._offset = result.offset;
|
|
1080
|
+
this._offset = toOffset(result.offset);
|
|
983
1081
|
this._epoch = result.epoch;
|
|
984
1082
|
this._recover = true;
|
|
985
1083
|
if (this._canSubscribeWithoutGettingToken()) {
|
|
986
|
-
this._subscribeWithoutToken();
|
|
1084
|
+
this._subscribeWithoutToken(flow);
|
|
987
1085
|
} else {
|
|
988
|
-
this._getSubscriptionToken().then((token) => this._handleTokenResponse(token)).catch((e) => this._handleTokenError(e));
|
|
1086
|
+
this._getSubscriptionToken().then((token) => this._handleTokenResponse(token, flow)).catch((e) => this._handleTokenError(e, flow));
|
|
989
1087
|
}
|
|
990
1088
|
}).catch((e) => {
|
|
1089
|
+
if (this._flowOutdated(flow)) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
991
1092
|
if (!this._isSubscribing()) {
|
|
992
1093
|
this._inflight = false;
|
|
993
1094
|
return;
|
|
@@ -1000,21 +1101,27 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1000
1101
|
});
|
|
1001
1102
|
});
|
|
1002
1103
|
}
|
|
1003
|
-
_getDataAndSubscribe(token) {
|
|
1104
|
+
_getDataAndSubscribe(token, flow) {
|
|
1004
1105
|
if (!this._getData) {
|
|
1005
1106
|
this._inflight = false;
|
|
1006
1107
|
return;
|
|
1007
1108
|
}
|
|
1008
|
-
this._getData({ channel: this.channel }).then((data) => {
|
|
1109
|
+
this._callAsync(() => this._getData({ channel: this.channel })).then((data) => {
|
|
1110
|
+
if (this._flowOutdated(flow)) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1009
1113
|
if (!this._isSubscribing()) {
|
|
1010
1114
|
this._inflight = false;
|
|
1011
1115
|
return;
|
|
1012
1116
|
}
|
|
1013
1117
|
this._data = data;
|
|
1014
1118
|
this._sendSubscribe(token);
|
|
1015
|
-
}).catch((e) => this._handleGetDataError(e));
|
|
1119
|
+
}).catch((e) => this._handleGetDataError(e, flow));
|
|
1016
1120
|
}
|
|
1017
|
-
_handleGetDataError(error) {
|
|
1121
|
+
_handleGetDataError(error, flow) {
|
|
1122
|
+
if (this._flowOutdated(flow)) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1018
1125
|
if (!this._isSubscribing()) {
|
|
1019
1126
|
this._inflight = false;
|
|
1020
1127
|
return;
|
|
@@ -1024,18 +1131,22 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1024
1131
|
this._failUnauthorized();
|
|
1025
1132
|
return;
|
|
1026
1133
|
}
|
|
1027
|
-
this.
|
|
1134
|
+
this._emitError({
|
|
1028
1135
|
type: "subscribeData",
|
|
1029
1136
|
channel: this.channel,
|
|
1030
1137
|
error: {
|
|
1031
1138
|
code: errorCodes.badConfiguration,
|
|
1032
1139
|
message: (error === null || error === void 0 ? void 0 : error.toString()) || ""
|
|
1033
1140
|
}
|
|
1141
|
+
}, () => {
|
|
1142
|
+
this._inflight = false;
|
|
1143
|
+
this._scheduleResubscribe();
|
|
1034
1144
|
});
|
|
1035
|
-
this._inflight = false;
|
|
1036
|
-
this._scheduleResubscribe();
|
|
1037
1145
|
}
|
|
1038
|
-
_handleTokenResponse(token) {
|
|
1146
|
+
_handleTokenResponse(token, flow) {
|
|
1147
|
+
if (this._flowOutdated(flow)) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1039
1150
|
if (!this._isSubscribing()) {
|
|
1040
1151
|
this._inflight = false;
|
|
1041
1152
|
return;
|
|
@@ -1047,12 +1158,15 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1047
1158
|
}
|
|
1048
1159
|
this._token = token;
|
|
1049
1160
|
if (this._getData) {
|
|
1050
|
-
this._getDataAndSubscribe(token);
|
|
1161
|
+
this._getDataAndSubscribe(token, flow);
|
|
1051
1162
|
} else {
|
|
1052
1163
|
this._sendSubscribe(token);
|
|
1053
1164
|
}
|
|
1054
1165
|
}
|
|
1055
|
-
_handleTokenError(error) {
|
|
1166
|
+
_handleTokenError(error, flow) {
|
|
1167
|
+
if (this._flowOutdated(flow)) {
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1056
1170
|
if (!this._isSubscribing()) {
|
|
1057
1171
|
this._inflight = false;
|
|
1058
1172
|
return;
|
|
@@ -1062,16 +1176,17 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1062
1176
|
this._failUnauthorized();
|
|
1063
1177
|
return;
|
|
1064
1178
|
}
|
|
1065
|
-
this.
|
|
1179
|
+
this._emitError({
|
|
1066
1180
|
type: "subscribeToken",
|
|
1067
1181
|
channel: this.channel,
|
|
1068
1182
|
error: {
|
|
1069
1183
|
code: errorCodes.subscriptionSubscribeToken,
|
|
1070
1184
|
message: (error === null || error === void 0 ? void 0 : error.toString()) || ""
|
|
1071
1185
|
}
|
|
1186
|
+
}, () => {
|
|
1187
|
+
this._inflight = false;
|
|
1188
|
+
this._scheduleResubscribe();
|
|
1072
1189
|
});
|
|
1073
|
-
this._inflight = false;
|
|
1074
|
-
this._scheduleResubscribe();
|
|
1075
1190
|
}
|
|
1076
1191
|
_sendSubscribe(token) {
|
|
1077
1192
|
if (!this._isTransportOpen()) {
|
|
@@ -1079,18 +1194,41 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1079
1194
|
return null;
|
|
1080
1195
|
}
|
|
1081
1196
|
const cmd = this._buildSubscribeCommand(token);
|
|
1197
|
+
const flow = this._subscribeFlow;
|
|
1198
|
+
this._subscribeCallFlow = flow;
|
|
1082
1199
|
this._centrifuge._call(cmd).then((resolveCtx) => {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1200
|
+
try {
|
|
1201
|
+
if (this._settleSubscribeCall(flow, true)) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
if (flow !== this._subscribeFlow) {
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
this._inflight = false;
|
|
1208
|
+
this._handleSubscribeResponse(resolveCtx.reply.subscribe);
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
this._centrifuge._dispatchFailed(err);
|
|
1211
|
+
} finally {
|
|
1212
|
+
if (resolveCtx.next) {
|
|
1213
|
+
resolveCtx.next();
|
|
1214
|
+
}
|
|
1088
1215
|
}
|
|
1089
1216
|
}, (rejectCtx) => {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1217
|
+
try {
|
|
1218
|
+
if (this._settleSubscribeCall(flow, false, rejectCtx.error)) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (flow !== this._subscribeFlow) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
this._inflight = false;
|
|
1225
|
+
this._handleSubscribeError(rejectCtx.error);
|
|
1226
|
+
} catch (err) {
|
|
1227
|
+
this._centrifuge._rejectionFailed(err, rejectCtx);
|
|
1228
|
+
} finally {
|
|
1229
|
+
if (rejectCtx.next) {
|
|
1230
|
+
rejectCtx.next();
|
|
1231
|
+
}
|
|
1094
1232
|
}
|
|
1095
1233
|
});
|
|
1096
1234
|
return cmd;
|
|
@@ -1135,10 +1273,22 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1135
1273
|
_debug(...args) {
|
|
1136
1274
|
this._centrifuge._debug(...args);
|
|
1137
1275
|
}
|
|
1276
|
+
/** @internal An exception escaping a handler stops the client: see Centrifuge._handlerFailed. */
|
|
1277
|
+
emit(event, ...args) {
|
|
1278
|
+
try {
|
|
1279
|
+
return EventEmitter.prototype.emit.call(this, event, ...args);
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
this._centrifuge._handlerFailed(e);
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1138
1285
|
_handleSubscribeError(error) {
|
|
1139
1286
|
if (!this._isSubscribing()) {
|
|
1140
1287
|
return;
|
|
1141
1288
|
}
|
|
1289
|
+
if (error.code === errorCodes.connectionClosed) {
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1142
1292
|
if (error.code === errorCodes.timeout) {
|
|
1143
1293
|
this._centrifuge._disconnect(connectingCodes.subscribeTimeout, "subscribe timeout", true);
|
|
1144
1294
|
return;
|
|
@@ -1157,7 +1307,12 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1157
1307
|
if (!this._isSubscribing()) {
|
|
1158
1308
|
return;
|
|
1159
1309
|
}
|
|
1160
|
-
this.
|
|
1310
|
+
this._sharedPollReplayPending = this._sharedPoll;
|
|
1311
|
+
try {
|
|
1312
|
+
this._setSubscribed(result);
|
|
1313
|
+
} finally {
|
|
1314
|
+
this._sharedPollReplayPending = false;
|
|
1315
|
+
}
|
|
1161
1316
|
if (this._sharedPoll) {
|
|
1162
1317
|
this._sharedPollReplayTrack();
|
|
1163
1318
|
}
|
|
@@ -1169,45 +1324,61 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1169
1324
|
let promise = Promise.resolve();
|
|
1170
1325
|
if (this._isSubscribed()) {
|
|
1171
1326
|
if (sendUnsubscribe) {
|
|
1172
|
-
promise = this._centrifuge._unsubscribe(this);
|
|
1327
|
+
promise = this._unsubPromise = this._centrifuge._unsubscribe(this);
|
|
1173
1328
|
}
|
|
1174
1329
|
this._clearSubscribedState();
|
|
1175
1330
|
} else if (this._isSubscribing()) {
|
|
1176
1331
|
if (this._inflight && sendUnsubscribe) {
|
|
1177
|
-
|
|
1332
|
+
if (this._mustDeferUnsubscribe()) {
|
|
1333
|
+
const flow = this._subscribeFlow;
|
|
1334
|
+
promise = this._unsubPromise = new Promise((resolve) => {
|
|
1335
|
+
this._deferredUnsubscribe = { flow, resolve };
|
|
1336
|
+
});
|
|
1337
|
+
} else {
|
|
1338
|
+
promise = this._unsubPromise = this._centrifuge._unsubscribe(this);
|
|
1339
|
+
}
|
|
1178
1340
|
}
|
|
1179
1341
|
this._clearSubscribingState();
|
|
1180
1342
|
}
|
|
1181
1343
|
this._inflight = false;
|
|
1344
|
+
this._subscribeFlow++;
|
|
1182
1345
|
this._id = 0;
|
|
1183
1346
|
this._sharedPollEpoch = "";
|
|
1184
1347
|
this._sharedPollSignatures = [];
|
|
1185
1348
|
this._sharedPollTrackedItems.clear();
|
|
1186
1349
|
this._sharedPollSignatureRefreshInFlight = false;
|
|
1350
|
+
this._sharedPollSignatureRefreshAgain = false;
|
|
1187
1351
|
this._sharedPollSignatureRefreshTargetMs = null;
|
|
1188
1352
|
this._cancelAllDebounce();
|
|
1189
|
-
|
|
1353
|
+
this._rejectPromises({ code: errorCodes.subscriptionUnsubscribed, message: SubscriptionState.Unsubscribed });
|
|
1354
|
+
const transition = this._stateTransitions + 1;
|
|
1355
|
+
if (this._setState(SubscriptionState.Unsubscribed) && this._stateTransitions === transition) {
|
|
1190
1356
|
this.emit("unsubscribed", { channel: this.channel, code, reason });
|
|
1191
1357
|
}
|
|
1192
|
-
this._rejectPromises({ code: errorCodes.subscriptionUnsubscribed, message: this.state });
|
|
1193
1358
|
return promise;
|
|
1194
1359
|
}
|
|
1195
1360
|
_handlePublication(pub) {
|
|
1361
|
+
if (this._sharedPoll && pub.key && !pub.removed && hasOffset(pub.version)) {
|
|
1362
|
+
const known = this._sharedPollTrackedItems.get(pub.key);
|
|
1363
|
+
if (known !== void 0 && known > 0 && toOffset(pub.version) <= known) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1196
1367
|
if (this._delta && this._delta_negotiated) {
|
|
1197
1368
|
const deltaKey = this._map || this._sharedPoll ? pub.key || "" : "";
|
|
1198
|
-
const { newData, newPrevValue, isDelta, wireBytes, fullBytes } = this._centrifuge._codec.applyDeltaIfNeeded(pub, this._prevValueMap.get(deltaKey));
|
|
1199
|
-
pub.data = newData;
|
|
1200
|
-
this._deltaNumPubs++;
|
|
1201
|
-
this._deltaBytesReceived += wireBytes;
|
|
1202
|
-
this._deltaBytesDecoded += fullBytes;
|
|
1203
|
-
if (isDelta) {
|
|
1204
|
-
this._deltaNumDelta++;
|
|
1205
|
-
} else {
|
|
1206
|
-
this._deltaNumFull++;
|
|
1207
|
-
}
|
|
1208
1369
|
if (pub.removed) {
|
|
1209
1370
|
this._prevValueMap.delete(deltaKey);
|
|
1210
1371
|
} else {
|
|
1372
|
+
const { newData, newPrevValue, isDelta, wireBytes, fullBytes } = this._centrifuge._codec.applyDeltaIfNeeded(pub, this._prevValueMap.get(deltaKey));
|
|
1373
|
+
pub.data = newData;
|
|
1374
|
+
this._deltaNumPubs++;
|
|
1375
|
+
this._deltaBytesReceived += wireBytes;
|
|
1376
|
+
this._deltaBytesDecoded += fullBytes;
|
|
1377
|
+
if (isDelta) {
|
|
1378
|
+
this._deltaNumDelta++;
|
|
1379
|
+
} else {
|
|
1380
|
+
this._deltaNumFull++;
|
|
1381
|
+
}
|
|
1211
1382
|
this._prevValueMap.set(deltaKey, newPrevValue);
|
|
1212
1383
|
}
|
|
1213
1384
|
}
|
|
@@ -1216,25 +1387,43 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1216
1387
|
if (pub.key && !this._sharedPollTrackedItems.has(pub.key)) {
|
|
1217
1388
|
return;
|
|
1218
1389
|
}
|
|
1219
|
-
if (pub.key) {
|
|
1220
|
-
if (pub.removed) {
|
|
1221
|
-
this._sharedPollTrackedItems.delete(pub.key);
|
|
1222
|
-
} else if (pub.version) {
|
|
1223
|
-
this._sharedPollTrackedItems.set(pub.key, pub.version);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
1390
|
ctx = this._getSharedPollUpdateContext(pub);
|
|
1227
1391
|
} else if (this._map) {
|
|
1228
1392
|
ctx = this._getMapUpdateContext(pub);
|
|
1229
1393
|
} else {
|
|
1230
1394
|
ctx = this._centrifuge._getPublicationContext(this.channel, pub);
|
|
1231
1395
|
}
|
|
1232
|
-
this.emit("publication", ctx);
|
|
1233
1396
|
if (this._map || this._sharedPoll) {
|
|
1397
|
+
this.emit("publication", ctx);
|
|
1398
|
+
if (!this._isSubscribed()) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
this._setPublicationPosition(pub);
|
|
1234
1402
|
this.emit("update", ctx);
|
|
1403
|
+
} else {
|
|
1404
|
+
this._pendingOffset = hasOffset(pub.offset) ? toOffset(pub.offset) : null;
|
|
1405
|
+
const failures = this._centrifuge._handlerFailures;
|
|
1406
|
+
try {
|
|
1407
|
+
this.emit("publication", ctx);
|
|
1408
|
+
} finally {
|
|
1409
|
+
this._pendingOffset = null;
|
|
1410
|
+
}
|
|
1411
|
+
if (this._centrifuge._handlerFailures !== failures) {
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
this._setPublicationPosition(pub);
|
|
1235
1415
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1416
|
+
}
|
|
1417
|
+
_setPublicationPosition(pub) {
|
|
1418
|
+
if (this._sharedPoll && pub.key && this._sharedPollTrackedItems.has(pub.key)) {
|
|
1419
|
+
if (pub.removed) {
|
|
1420
|
+
this._sharedPollTrackedItems.delete(pub.key);
|
|
1421
|
+
} else if (pub.version) {
|
|
1422
|
+
this._sharedPollTrackedItems.set(pub.key, toOffset(pub.version));
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (hasOffset(pub.offset)) {
|
|
1426
|
+
this._offset = toOffset(pub.offset);
|
|
1238
1427
|
}
|
|
1239
1428
|
if (pub.epoch) {
|
|
1240
1429
|
this._epoch = pub.epoch;
|
|
@@ -1242,18 +1431,24 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1242
1431
|
}
|
|
1243
1432
|
/** Seed per-key delta tracking from state/stream entries.
|
|
1244
1433
|
* Handles JSON-escaped data (server-side delta escaping) and protobuf data.
|
|
1245
|
-
* Decodes escaped data back to original format for user consumption.
|
|
1246
|
-
|
|
1434
|
+
* Decodes escaped data back to original format for user consumption.
|
|
1435
|
+
* Values are escaped only when delta is negotiated: otherwise they come as
|
|
1436
|
+
* they are, and a string value must not be parsed. */
|
|
1437
|
+
_seedDeltaTracking(pub, deltaNegotiated) {
|
|
1247
1438
|
if (!this._delta || !pub.key)
|
|
1248
1439
|
return;
|
|
1440
|
+
if (pub.removed) {
|
|
1441
|
+
this._prevValueMap.delete(pub.key);
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
if (!deltaNegotiated) {
|
|
1445
|
+
this._prevValueMap.set(pub.key, pub.data);
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1249
1448
|
if (typeof pub.data === "string") {
|
|
1250
1449
|
const rawBytes = pub.data;
|
|
1251
1450
|
const encoded = new TextEncoder().encode(rawBytes);
|
|
1252
|
-
|
|
1253
|
-
this._prevValueMap.set(pub.key, encoded);
|
|
1254
|
-
} else {
|
|
1255
|
-
this._prevValueMap.delete(pub.key);
|
|
1256
|
-
}
|
|
1451
|
+
this._prevValueMap.set(pub.key, encoded);
|
|
1257
1452
|
const byteLen = encoded.length;
|
|
1258
1453
|
this._deltaNumPubs++;
|
|
1259
1454
|
this._deltaNumFull++;
|
|
@@ -1261,11 +1456,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1261
1456
|
this._deltaBytesDecoded += byteLen;
|
|
1262
1457
|
pub.data = JSON.parse(rawBytes);
|
|
1263
1458
|
} else if (pub.data instanceof Uint8Array) {
|
|
1264
|
-
|
|
1265
|
-
this._prevValueMap.set(pub.key, pub.data);
|
|
1266
|
-
} else {
|
|
1267
|
-
this._prevValueMap.delete(pub.key);
|
|
1268
|
-
}
|
|
1459
|
+
this._prevValueMap.set(pub.key, pub.data);
|
|
1269
1460
|
const byteLen = pub.data.length;
|
|
1270
1461
|
this._deltaNumPubs++;
|
|
1271
1462
|
this._deltaNumFull++;
|
|
@@ -1312,17 +1503,47 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1312
1503
|
}
|
|
1313
1504
|
const self2 = this;
|
|
1314
1505
|
const delay = this._getResubscribeDelay();
|
|
1506
|
+
this._clearResubscribeTimeout();
|
|
1315
1507
|
this._resubscribeTimeout = setTimeout(function() {
|
|
1508
|
+
self2._resubscribeTimeout = null;
|
|
1316
1509
|
if (self2._isSubscribing()) {
|
|
1317
1510
|
self2._subscribe();
|
|
1318
1511
|
}
|
|
1319
1512
|
}, delay);
|
|
1320
1513
|
this._debug("resubscribe scheduled after " + delay, this.channel);
|
|
1321
1514
|
}
|
|
1515
|
+
// Emits an error event, then runs the continuation: not when a handler unsubscribed,
|
|
1516
|
+
// maybe to subscribe again, whose new subscribe the continuation must not touch.
|
|
1517
|
+
_emitError(ctx, continuation) {
|
|
1518
|
+
const flow = this._subscribeFlow;
|
|
1519
|
+
try {
|
|
1520
|
+
this.emit("error", ctx);
|
|
1521
|
+
} finally {
|
|
1522
|
+
if (flow === this._subscribeFlow) {
|
|
1523
|
+
continuation();
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1322
1527
|
_subscribeError(err) {
|
|
1323
1528
|
if (!this._isSubscribing()) {
|
|
1324
1529
|
return;
|
|
1325
1530
|
}
|
|
1531
|
+
if (err.code === 109 && this._getToken === null) {
|
|
1532
|
+
try {
|
|
1533
|
+
this.emit("error", {
|
|
1534
|
+
type: "configuration",
|
|
1535
|
+
channel: this.channel,
|
|
1536
|
+
error: {
|
|
1537
|
+
code: errorCodes.badConfiguration,
|
|
1538
|
+
message: "subscription token expired but no getToken function set"
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
} catch (e) {
|
|
1542
|
+
this._centrifuge._reportDispatchError(e);
|
|
1543
|
+
}
|
|
1544
|
+
this._setUnsubscribed(unsubscribedCodes.unauthorized, "unauthorized", false);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1326
1547
|
if (err.code < 100 || err.code === 109 || err.temporary === true) {
|
|
1327
1548
|
if (err.code === 109) {
|
|
1328
1549
|
this._token = "";
|
|
@@ -1333,9 +1554,10 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1333
1554
|
error: err
|
|
1334
1555
|
};
|
|
1335
1556
|
if (this._centrifuge.state === State.Connected) {
|
|
1336
|
-
this.
|
|
1557
|
+
this._emitError(errContext, () => this._scheduleResubscribe());
|
|
1558
|
+
} else {
|
|
1559
|
+
this._scheduleResubscribe();
|
|
1337
1560
|
}
|
|
1338
|
-
this._scheduleResubscribe();
|
|
1339
1561
|
} else {
|
|
1340
1562
|
this._setUnsubscribed(err.code, err.message, false);
|
|
1341
1563
|
}
|
|
@@ -1350,7 +1572,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1350
1572
|
return;
|
|
1351
1573
|
}
|
|
1352
1574
|
if (options.since) {
|
|
1353
|
-
this._offset = options.since.offset
|
|
1575
|
+
this._offset = toOffset(options.since.offset);
|
|
1354
1576
|
this._epoch = options.since.epoch || "";
|
|
1355
1577
|
this._recover = true;
|
|
1356
1578
|
}
|
|
@@ -1418,6 +1640,9 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1418
1640
|
}
|
|
1419
1641
|
}
|
|
1420
1642
|
_getOffset() {
|
|
1643
|
+
if (this._pendingOffset !== null) {
|
|
1644
|
+
return this._pendingOffset;
|
|
1645
|
+
}
|
|
1421
1646
|
const offset = this._offset;
|
|
1422
1647
|
if (offset !== null) {
|
|
1423
1648
|
return offset;
|
|
@@ -1450,23 +1675,28 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1450
1675
|
};
|
|
1451
1676
|
const getToken = this._getToken;
|
|
1452
1677
|
if (getToken === null) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1678
|
+
try {
|
|
1679
|
+
this.emit("error", {
|
|
1680
|
+
type: "configuration",
|
|
1681
|
+
channel: this.channel,
|
|
1682
|
+
error: {
|
|
1683
|
+
code: errorCodes.badConfiguration,
|
|
1684
|
+
message: "provide a function to get channel subscription token"
|
|
1685
|
+
}
|
|
1686
|
+
});
|
|
1687
|
+
} catch (e) {
|
|
1688
|
+
this._centrifuge._reportDispatchError(e);
|
|
1689
|
+
}
|
|
1461
1690
|
return Promise.reject(new UnauthorizedError(""));
|
|
1462
1691
|
}
|
|
1463
|
-
return getToken(ctx);
|
|
1692
|
+
return this._callAsync(() => getToken(ctx));
|
|
1464
1693
|
}
|
|
1465
1694
|
_refresh() {
|
|
1466
1695
|
this._clearRefreshTimeout();
|
|
1467
1696
|
const self2 = this;
|
|
1697
|
+
const generation = this._refreshGeneration;
|
|
1468
1698
|
this._getSubscriptionToken().then(function(token) {
|
|
1469
|
-
if (!self2._isSubscribed()) {
|
|
1699
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed()) {
|
|
1470
1700
|
return;
|
|
1471
1701
|
}
|
|
1472
1702
|
if (!token) {
|
|
@@ -1482,31 +1712,47 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1482
1712
|
"sub_refresh": req
|
|
1483
1713
|
};
|
|
1484
1714
|
self2._centrifuge._call(msg).then((resolveCtx) => {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1715
|
+
try {
|
|
1716
|
+
self2._refreshResponse(resolveCtx.reply.sub_refresh);
|
|
1717
|
+
} catch (err) {
|
|
1718
|
+
self2._centrifuge._dispatchFailed(err);
|
|
1719
|
+
} finally {
|
|
1720
|
+
if (resolveCtx.next) {
|
|
1721
|
+
resolveCtx.next();
|
|
1722
|
+
}
|
|
1489
1723
|
}
|
|
1490
1724
|
}, (rejectCtx) => {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1725
|
+
try {
|
|
1726
|
+
self2._refreshError(rejectCtx.error);
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
self2._centrifuge._rejectionFailed(err, rejectCtx);
|
|
1729
|
+
} finally {
|
|
1730
|
+
if (rejectCtx.next) {
|
|
1731
|
+
rejectCtx.next();
|
|
1732
|
+
}
|
|
1494
1733
|
}
|
|
1495
1734
|
});
|
|
1496
1735
|
}).catch(function(e) {
|
|
1736
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed()) {
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1497
1739
|
if (e instanceof UnauthorizedError) {
|
|
1498
1740
|
self2._failUnauthorized();
|
|
1499
1741
|
return;
|
|
1500
1742
|
}
|
|
1501
|
-
self2.
|
|
1743
|
+
self2._emitError({
|
|
1502
1744
|
type: "refreshToken",
|
|
1503
1745
|
channel: self2.channel,
|
|
1504
1746
|
error: {
|
|
1505
1747
|
code: errorCodes.subscriptionRefreshToken,
|
|
1506
1748
|
message: e !== void 0 ? e.toString() : ""
|
|
1507
1749
|
}
|
|
1750
|
+
}, () => {
|
|
1751
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed()) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
self2._refreshTimeout = setTimeout(() => self2._refresh(), self2._getRefreshRetryDelay());
|
|
1508
1755
|
});
|
|
1509
|
-
self2._refreshTimeout = setTimeout(() => self2._refresh(), self2._getRefreshRetryDelay());
|
|
1510
1756
|
});
|
|
1511
1757
|
}
|
|
1512
1758
|
_refreshResponse(result) {
|
|
@@ -1524,12 +1770,16 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1524
1770
|
return;
|
|
1525
1771
|
}
|
|
1526
1772
|
if (err.code < 100 || err.temporary === true) {
|
|
1527
|
-
this.
|
|
1773
|
+
this._emitError({
|
|
1528
1774
|
type: "refresh",
|
|
1529
1775
|
channel: this.channel,
|
|
1530
1776
|
error: err
|
|
1777
|
+
}, () => {
|
|
1778
|
+
if (!this._isSubscribed()) {
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
1531
1782
|
});
|
|
1532
|
-
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
1533
1783
|
} else {
|
|
1534
1784
|
this._setUnsubscribed(err.code, err.message, true);
|
|
1535
1785
|
}
|
|
@@ -1537,6 +1787,16 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1537
1787
|
_getRefreshRetryDelay() {
|
|
1538
1788
|
return backoff(0, 1e4, 2e4);
|
|
1539
1789
|
}
|
|
1790
|
+
// Calls an app callback expected to return a promise: one throwing instead of
|
|
1791
|
+
// returning a rejected promise fails the same way, not by throwing out of the
|
|
1792
|
+
// subscribe or refresh in progress.
|
|
1793
|
+
_callAsync(callback) {
|
|
1794
|
+
try {
|
|
1795
|
+
return Promise.resolve(callback());
|
|
1796
|
+
} catch (e) {
|
|
1797
|
+
return Promise.reject(e);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1540
1800
|
_failUnauthorized() {
|
|
1541
1801
|
this._setUnsubscribed(unsubscribedCodes.unauthorized, "unauthorized", true);
|
|
1542
1802
|
}
|
|
@@ -1569,7 +1829,8 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1569
1829
|
}
|
|
1570
1830
|
if (current.length > 0)
|
|
1571
1831
|
frames.push(current);
|
|
1572
|
-
const
|
|
1832
|
+
const keysFrom = (idx) => frames.slice(idx).flatMap((f) => f.flatMap((b) => b.items.map((i) => i.key)));
|
|
1833
|
+
const send = (frame, idx, frameUntrackKeys) => new Promise((resolve, reject) => {
|
|
1573
1834
|
const req = {
|
|
1574
1835
|
channel: this.channel,
|
|
1575
1836
|
type: 1,
|
|
@@ -1583,20 +1844,55 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1583
1844
|
}
|
|
1584
1845
|
const msg = { "sub_refresh": req };
|
|
1585
1846
|
this._centrifuge._call(msg).then((resolveCtx) => {
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1847
|
+
try {
|
|
1848
|
+
this._handleTrackResponse(resolveCtx.reply.sub_refresh, frame.flatMap((b) => b.items.map((i) => i.key)), frameUntrackKeys);
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
this._centrifuge._dispatchFailed(err);
|
|
1851
|
+
} finally {
|
|
1852
|
+
if (resolveCtx.next)
|
|
1853
|
+
resolveCtx.next();
|
|
1854
|
+
}
|
|
1589
1855
|
resolve();
|
|
1590
1856
|
}, (rejectCtx) => {
|
|
1591
1857
|
if (rejectCtx.next)
|
|
1592
1858
|
rejectCtx.next();
|
|
1593
|
-
|
|
1859
|
+
this._sharedPollRequestFailed(rejectCtx.error);
|
|
1860
|
+
reject({ error: rejectCtx.error, keys: keysFrom(idx) });
|
|
1594
1861
|
});
|
|
1595
1862
|
});
|
|
1596
|
-
return frames.reduce((chain, frame, idx) => chain.then(() => send(frame, idx === 0 ? untrackKeys : void 0)), Promise.resolve());
|
|
1863
|
+
return this._sharedPollSerial(() => frames.reduce((chain, frame, idx) => chain.then(() => send(frame, idx, idx === 0 ? untrackKeys : void 0)), Promise.resolve()));
|
|
1864
|
+
}
|
|
1865
|
+
_isEmulation() {
|
|
1866
|
+
const transport = this._centrifuge._transport;
|
|
1867
|
+
return !!transport && transport.emulation();
|
|
1868
|
+
}
|
|
1869
|
+
// Over emulation each request is a separate HTTP request the server may handle
|
|
1870
|
+
// in any order: a track and an untrack of a key could overtake each other. The
|
|
1871
|
+
// channel's requests go one at a time then, in the order made; one made for a
|
|
1872
|
+
// subscription that has ended by its turn is dropped.
|
|
1873
|
+
_sharedPollSerial(send) {
|
|
1874
|
+
if (!this._isEmulation()) {
|
|
1875
|
+
return send();
|
|
1876
|
+
}
|
|
1877
|
+
const generation = this._refreshGeneration;
|
|
1878
|
+
const run = this._sharedPollQueue.then(() => {
|
|
1879
|
+
if (generation !== this._refreshGeneration || !this._isSubscribed()) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
return send();
|
|
1883
|
+
});
|
|
1884
|
+
this._sharedPollQueue = run.then(() => void 0, () => void 0);
|
|
1885
|
+
return run;
|
|
1886
|
+
}
|
|
1887
|
+
// A timed-out request over emulation may still reach the server after later
|
|
1888
|
+
// ones: whether the keys are tracked is unknown, the client reconnects.
|
|
1889
|
+
_sharedPollRequestFailed(error) {
|
|
1890
|
+
if (error && error.code === errorCodes.timeout && this._isEmulation()) {
|
|
1891
|
+
this._centrifuge._disconnect(connectingCodes.subscribeTimeout, "shared poll request timeout", true);
|
|
1892
|
+
}
|
|
1597
1893
|
}
|
|
1598
1894
|
_sendUntrackRequest(keys) {
|
|
1599
|
-
return new Promise((resolve, reject) => {
|
|
1895
|
+
return this._sharedPollSerial(() => new Promise((resolve, reject) => {
|
|
1600
1896
|
const req = {
|
|
1601
1897
|
channel: this.channel,
|
|
1602
1898
|
type: 2,
|
|
@@ -1612,19 +1908,36 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1612
1908
|
if (rejectCtx.next) {
|
|
1613
1909
|
rejectCtx.next();
|
|
1614
1910
|
}
|
|
1911
|
+
this._sharedPollRequestFailed(rejectCtx.error);
|
|
1615
1912
|
reject(rejectCtx.error);
|
|
1616
1913
|
});
|
|
1617
|
-
});
|
|
1914
|
+
}));
|
|
1618
1915
|
}
|
|
1619
|
-
_handleTrackResponse(result) {
|
|
1916
|
+
_handleTrackResponse(result, keys, untrackKeys) {
|
|
1917
|
+
if (!this._isSubscribed()) {
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1620
1920
|
this._clearSharedPollTrackRetry();
|
|
1621
1921
|
if (result && result.items && result.items.length > 0) {
|
|
1622
1922
|
for (const pub of result.items) {
|
|
1923
|
+
if (!this._isSubscribed()) {
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1623
1926
|
this._handlePublication(pub);
|
|
1624
1927
|
}
|
|
1625
1928
|
}
|
|
1626
|
-
if (
|
|
1627
|
-
|
|
1929
|
+
if (!this._isSubscribed()) {
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
const untracked = new Set(untrackKeys || []);
|
|
1933
|
+
const stale = keys.filter((key) => !untracked.has(key) && !this._sharedPollTrackedItems.has(key));
|
|
1934
|
+
if (stale.length > 0) {
|
|
1935
|
+
this._sendUntrackRequest(stale).catch((err) => {
|
|
1936
|
+
this.emit("error", { type: "untrack", channel: this.channel, error: err });
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
if (result && result.expires === true) {
|
|
1940
|
+
const targetMs = Date.now() + (result.ttl > 0 ? result.ttl * 1e3 : 1e3);
|
|
1628
1941
|
this._maybeScheduleSharedPollSignatureRefresh(targetMs);
|
|
1629
1942
|
}
|
|
1630
1943
|
}
|
|
@@ -1660,6 +1973,12 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1660
1973
|
}
|
|
1661
1974
|
this._sharedPollTrackRetryAttempts = 0;
|
|
1662
1975
|
}
|
|
1976
|
+
_scheduleSharedPollReplayRetry() {
|
|
1977
|
+
if (this._sharedPollReplayRetryTimeout !== null) {
|
|
1978
|
+
clearTimeout(this._sharedPollReplayRetryTimeout);
|
|
1979
|
+
}
|
|
1980
|
+
this._sharedPollReplayRetryTimeout = setTimeout(() => this._sharedPollReplayTrack(), backoff(this._sharedPollReplayRetryAttempts++, 5e3, 3e4));
|
|
1981
|
+
}
|
|
1663
1982
|
_clearSharedPollReplayRetry() {
|
|
1664
1983
|
if (this._sharedPollReplayRetryTimeout !== null) {
|
|
1665
1984
|
clearTimeout(this._sharedPollReplayRetryTimeout);
|
|
@@ -1667,21 +1986,59 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1667
1986
|
}
|
|
1668
1987
|
this._sharedPollReplayRetryAttempts = 0;
|
|
1669
1988
|
}
|
|
1670
|
-
|
|
1989
|
+
// Adds a signature to the library, dropping the entries whose keys it covers
|
|
1990
|
+
// entirely: they may have expired, and a replay sends every entry in one request,
|
|
1991
|
+
// which a single expired signature fails as a whole.
|
|
1992
|
+
_addSharedPollSignature(keys, signature) {
|
|
1993
|
+
const covered = new Set(keys);
|
|
1994
|
+
this._sharedPollSignatures = this._sharedPollSignatures.filter((entry) => !entry.keys.every((key) => covered.has(key)));
|
|
1995
|
+
this._sharedPollSignatures.push({ keys, signature });
|
|
1996
|
+
}
|
|
1997
|
+
_handleTrackError(err, keys) {
|
|
1671
1998
|
if (!this._isSubscribed()) {
|
|
1672
1999
|
return;
|
|
1673
2000
|
}
|
|
1674
|
-
this.
|
|
2001
|
+
this._emitError({
|
|
1675
2002
|
type: "track",
|
|
1676
2003
|
channel: this.channel,
|
|
1677
2004
|
error: err
|
|
2005
|
+
}, () => {
|
|
2006
|
+
if (!this._isSubscribed()) {
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
if (err.code === 109) {
|
|
2010
|
+
this._sharedPollRefreshSignature();
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
if (err.code < 100 || err.temporary === true) {
|
|
2014
|
+
this._sharedPollTrackRetryTimeout = setTimeout(() => this._sharedPollReplayTrack(), backoff(this._sharedPollTrackRetryAttempts++, 1e3, 15e3));
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
this._untrackFailedKeys(keys);
|
|
1678
2018
|
});
|
|
1679
|
-
|
|
1680
|
-
|
|
2019
|
+
}
|
|
2020
|
+
_untrackFailedKeys(keys) {
|
|
2021
|
+
const failed = keys.filter((key) => this._sharedPollTrackedItems.has(key));
|
|
2022
|
+
if (failed.length === 0) {
|
|
1681
2023
|
return;
|
|
1682
2024
|
}
|
|
1683
|
-
|
|
1684
|
-
this.
|
|
2025
|
+
for (const key of failed) {
|
|
2026
|
+
this._sharedPollTrackedItems.delete(key);
|
|
2027
|
+
}
|
|
2028
|
+
this._sharedPollSignatures = this._sharedPollSignatures.filter((entry) => entry.keys.some((k) => this._sharedPollTrackedItems.has(k)));
|
|
2029
|
+
this._sendUntrackRequest(failed).catch((err) => {
|
|
2030
|
+
this.emit("error", { type: "untrack", channel: this.channel, error: err });
|
|
2031
|
+
});
|
|
2032
|
+
for (const key of failed) {
|
|
2033
|
+
if (!this._isSubscribed()) {
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
this.emit("update", {
|
|
2037
|
+
channel: this.channel,
|
|
2038
|
+
key,
|
|
2039
|
+
data: null,
|
|
2040
|
+
removed: true
|
|
2041
|
+
});
|
|
1685
2042
|
}
|
|
1686
2043
|
}
|
|
1687
2044
|
// _sharedPollRefreshSignature obtains a fresh consolidated signature
|
|
@@ -1697,23 +2054,41 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1697
2054
|
return;
|
|
1698
2055
|
if (this._sharedPollTrackedItems.size === 0)
|
|
1699
2056
|
return;
|
|
1700
|
-
if (this._sharedPollSignatureRefreshInFlight)
|
|
2057
|
+
if (this._sharedPollSignatureRefreshInFlight) {
|
|
2058
|
+
for (const key of this._sharedPollTrackedItems.keys()) {
|
|
2059
|
+
if (!this._sharedPollSignatureRefreshKeys.has(key)) {
|
|
2060
|
+
this._sharedPollSignatureRefreshAgain = true;
|
|
2061
|
+
break;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1701
2064
|
return;
|
|
2065
|
+
}
|
|
1702
2066
|
this._sharedPollSignatureRefreshTargetMs = null;
|
|
1703
2067
|
this._sharedPollSignatureRefreshInFlight = true;
|
|
1704
2068
|
const keys = Array.from(this._sharedPollTrackedItems.keys());
|
|
2069
|
+
this._sharedPollSignatureRefreshKeys = new Set(keys);
|
|
1705
2070
|
const self2 = this;
|
|
1706
|
-
|
|
2071
|
+
const generation = this._refreshGeneration;
|
|
2072
|
+
this._callAsync(() => this._sharedPollGetSignature({ keys })).then((result) => {
|
|
2073
|
+
if (generation !== self2._refreshGeneration)
|
|
2074
|
+
return;
|
|
1707
2075
|
self2._sharedPollSignatureRefreshInFlight = false;
|
|
1708
2076
|
if (!self2._isSubscribed())
|
|
1709
2077
|
return;
|
|
1710
2078
|
self2._sharedPollSignatureRefreshAttempts = 0;
|
|
2079
|
+
if (self2._sharedPollSignatureRefreshAgain) {
|
|
2080
|
+
self2._sharedPollSignatureRefreshAgain = false;
|
|
2081
|
+
Promise.resolve().then(() => self2._sharedPollRefreshSignature());
|
|
2082
|
+
}
|
|
1711
2083
|
const returnedKeys = new Set(result.keys);
|
|
1712
2084
|
const revokedKeys = [];
|
|
1713
2085
|
for (const key of keys) {
|
|
1714
|
-
if (!returnedKeys.has(key)) {
|
|
2086
|
+
if (!returnedKeys.has(key) && this._sharedPollTrackedItems.has(key)) {
|
|
1715
2087
|
self2._sharedPollTrackedItems.delete(key);
|
|
1716
2088
|
revokedKeys.push(key);
|
|
2089
|
+
if (!self2._isSubscribed()) {
|
|
2090
|
+
continue;
|
|
2091
|
+
}
|
|
1717
2092
|
self2.emit("update", {
|
|
1718
2093
|
channel: self2.channel,
|
|
1719
2094
|
key,
|
|
@@ -1739,20 +2114,27 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1739
2114
|
self2._sharedPollSignatures = items.length > 0 ? [{ keys: result.keys, signature: result.signature }, ...uncovered] : uncovered;
|
|
1740
2115
|
if (items.length === 0)
|
|
1741
2116
|
return;
|
|
1742
|
-
self2._sendTrackRequest([{ items, signature: result.signature }]).catch((
|
|
1743
|
-
self2._handleTrackError(
|
|
2117
|
+
self2._sendTrackRequest([{ items, signature: result.signature }]).catch((e) => {
|
|
2118
|
+
self2._handleTrackError(e.error, e.keys);
|
|
1744
2119
|
});
|
|
1745
2120
|
}).catch((e) => {
|
|
2121
|
+
if (generation !== self2._refreshGeneration)
|
|
2122
|
+
return;
|
|
1746
2123
|
self2._sharedPollSignatureRefreshInFlight = false;
|
|
1747
|
-
self2.
|
|
2124
|
+
self2._sharedPollSignatureRefreshAgain = false;
|
|
2125
|
+
self2._emitError({
|
|
1748
2126
|
type: "signatureRefresh",
|
|
1749
2127
|
channel: self2.channel,
|
|
1750
2128
|
error: {
|
|
1751
2129
|
code: errorCodes.sharedPollGetSignature,
|
|
1752
2130
|
message: e !== void 0 ? e.toString() : ""
|
|
1753
2131
|
}
|
|
2132
|
+
}, () => {
|
|
2133
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed()) {
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
self2._sharedPollSignatureRefreshTimeout = setTimeout(() => self2._sharedPollRefreshSignature(), backoff(self2._sharedPollSignatureRefreshAttempts++, 5e3, 3e4));
|
|
1754
2137
|
});
|
|
1755
|
-
self2._sharedPollSignatureRefreshTimeout = setTimeout(() => self2._sharedPollRefreshSignature(), backoff(self2._sharedPollSignatureRefreshAttempts++, 5e3, 3e4));
|
|
1756
2138
|
});
|
|
1757
2139
|
}
|
|
1758
2140
|
// _sharedPollReplayTrack runs after subscribe completes — on initial
|
|
@@ -1801,8 +2183,8 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1801
2183
|
}
|
|
1802
2184
|
}
|
|
1803
2185
|
if (replayBatches.length > 0) {
|
|
1804
|
-
this._sendTrackRequest(replayBatches, allUntrackedInReplay).catch((
|
|
1805
|
-
this._handleTrackError(
|
|
2186
|
+
this._sendTrackRequest(replayBatches, allUntrackedInReplay).catch((e) => {
|
|
2187
|
+
this._handleTrackError(e.error, e.keys);
|
|
1806
2188
|
});
|
|
1807
2189
|
}
|
|
1808
2190
|
const uncoveredKeys = [];
|
|
@@ -1821,16 +2203,20 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1821
2203
|
return;
|
|
1822
2204
|
}
|
|
1823
2205
|
const self2 = this;
|
|
1824
|
-
|
|
1825
|
-
|
|
2206
|
+
const generation = this._refreshGeneration;
|
|
2207
|
+
this._callAsync(() => this._sharedPollGetSignature({ keys: uncoveredKeys })).then((result) => {
|
|
2208
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed())
|
|
1826
2209
|
return;
|
|
1827
2210
|
self2._clearSharedPollReplayRetry();
|
|
1828
2211
|
const returnedKeys = new Set(result.keys);
|
|
1829
2212
|
const revokedKeys = [];
|
|
1830
2213
|
for (const key of uncoveredKeys) {
|
|
1831
|
-
if (!returnedKeys.has(key)) {
|
|
2214
|
+
if (!returnedKeys.has(key) && this._sharedPollTrackedItems.has(key)) {
|
|
1832
2215
|
self2._sharedPollTrackedItems.delete(key);
|
|
1833
2216
|
revokedKeys.push(key);
|
|
2217
|
+
if (!self2._isSubscribed()) {
|
|
2218
|
+
continue;
|
|
2219
|
+
}
|
|
1834
2220
|
self2.emit("update", {
|
|
1835
2221
|
channel: self2.channel,
|
|
1836
2222
|
key,
|
|
@@ -1853,46 +2239,60 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1853
2239
|
}
|
|
1854
2240
|
if (items.length === 0)
|
|
1855
2241
|
return;
|
|
1856
|
-
self2.
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
});
|
|
1860
|
-
self2._sendTrackRequest([{ items, signature: result.signature }]).catch((err) => {
|
|
1861
|
-
self2._handleTrackError(err);
|
|
2242
|
+
self2._addSharedPollSignature(result.keys, result.signature);
|
|
2243
|
+
self2._sendTrackRequest([{ items, signature: result.signature }]).catch((e) => {
|
|
2244
|
+
self2._handleTrackError(e.error, e.keys);
|
|
1862
2245
|
});
|
|
1863
2246
|
}).catch((e) => {
|
|
1864
|
-
self2.
|
|
2247
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed())
|
|
2248
|
+
return;
|
|
2249
|
+
self2._emitError({
|
|
1865
2250
|
type: "signatureRefresh",
|
|
1866
2251
|
channel: self2.channel,
|
|
1867
2252
|
error: {
|
|
1868
2253
|
code: errorCodes.sharedPollGetSignature,
|
|
1869
2254
|
message: e !== void 0 ? e.toString() : ""
|
|
1870
2255
|
}
|
|
2256
|
+
}, () => {
|
|
2257
|
+
if (generation !== self2._refreshGeneration || !self2._isSubscribed())
|
|
2258
|
+
return;
|
|
2259
|
+
self2._sharedPollReplayRetryTimeout = setTimeout(() => self2._sharedPollReplayTrack(), backoff(self2._sharedPollReplayRetryAttempts++, 5e3, 3e4));
|
|
1871
2260
|
});
|
|
1872
|
-
self2._sharedPollReplayRetryTimeout = setTimeout(() => self2._sharedPollReplayTrack(), backoff(self2._sharedPollReplayRetryAttempts++, 5e3, 3e4));
|
|
1873
2261
|
});
|
|
1874
2262
|
}
|
|
1875
2263
|
// ============ Keyed Subscription Methods ============
|
|
1876
2264
|
/** Entry point for map subscriptions */
|
|
1877
2265
|
_mapSubscribe() {
|
|
1878
2266
|
this._debug("starting map subscribe on", this.channel);
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
this.
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
this.
|
|
1889
|
-
this.
|
|
1890
|
-
|
|
1891
|
-
|
|
2267
|
+
const flow = ++this._subscribeFlow;
|
|
2268
|
+
const start = () => {
|
|
2269
|
+
this._mapStateBuffer = [];
|
|
2270
|
+
this._mapStreamBuffer = [];
|
|
2271
|
+
this._mapCursor = "";
|
|
2272
|
+
const recovering = this._recover && this._offset !== null && this._epoch !== null;
|
|
2273
|
+
if (!recovering) {
|
|
2274
|
+
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2275
|
+
}
|
|
2276
|
+
this._mapFlowRecovering = recovering;
|
|
2277
|
+
this._mapFlowOffset = recovering ? this._offset : 0;
|
|
2278
|
+
this._mapFlowEpoch = recovering ? this._epoch : null;
|
|
2279
|
+
this._mapFlowPositionResets = this._mapPositionResets;
|
|
2280
|
+
this._mapPhase = MapPhase.State;
|
|
2281
|
+
if (recovering) {
|
|
2282
|
+
this._debug("map subscribe: recovering from position, skipping to stream phase");
|
|
2283
|
+
this._mapPhase = MapPhase.Stream;
|
|
2284
|
+
this._fetchStream(flow);
|
|
2285
|
+
} else {
|
|
2286
|
+
this._fetchSnapshot(flow);
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
1892
2289
|
if (this._canSubscribeWithoutGettingToken()) {
|
|
1893
|
-
|
|
2290
|
+
start();
|
|
1894
2291
|
} else {
|
|
1895
2292
|
this._getSubscriptionToken().then((token) => {
|
|
2293
|
+
if (flow !== this._subscribeFlow) {
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
1896
2296
|
if (!this._isSubscribing()) {
|
|
1897
2297
|
this._inflight = false;
|
|
1898
2298
|
return;
|
|
@@ -1903,171 +2303,212 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
1903
2303
|
return;
|
|
1904
2304
|
}
|
|
1905
2305
|
this._token = token;
|
|
1906
|
-
|
|
1907
|
-
}).catch((e) =>
|
|
2306
|
+
start();
|
|
2307
|
+
}).catch((e) => {
|
|
2308
|
+
this._handleTokenError(e, flow);
|
|
2309
|
+
});
|
|
1908
2310
|
}
|
|
1909
2311
|
}
|
|
1910
2312
|
/** Fetch a page of snapshot data */
|
|
1911
|
-
_fetchSnapshot(cursor) {
|
|
2313
|
+
_fetchSnapshot(flow, cursor) {
|
|
1912
2314
|
if (!this._isSubscribing() || !this._isTransportOpen()) {
|
|
1913
2315
|
this._inflight = false;
|
|
1914
2316
|
return;
|
|
1915
2317
|
}
|
|
1916
2318
|
const cmd = this._buildMapSubscribeCommand(MapPhase.State, cursor);
|
|
1917
2319
|
this._debug("map subscribe: fetching snapshot page", cursor ? `cursor=${cursor}` : "initial");
|
|
2320
|
+
this._subscribeCallFlow = flow;
|
|
1918
2321
|
this._centrifuge._call(cmd).then((resolveCtx) => {
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2322
|
+
try {
|
|
2323
|
+
if (this._settleSubscribeCall(flow, true)) {
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
if (flow === this._subscribeFlow) {
|
|
2327
|
+
this._handleMapStateResponse(flow, resolveCtx.reply.subscribe);
|
|
2328
|
+
}
|
|
2329
|
+
} catch (err) {
|
|
2330
|
+
this._centrifuge._dispatchFailed(err);
|
|
2331
|
+
} finally {
|
|
2332
|
+
if (resolveCtx.next) {
|
|
2333
|
+
resolveCtx.next();
|
|
2334
|
+
}
|
|
1923
2335
|
}
|
|
1924
2336
|
}, (rejectCtx) => {
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2337
|
+
try {
|
|
2338
|
+
if (this._settleSubscribeCall(flow, false, rejectCtx.error)) {
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
if (flow === this._subscribeFlow) {
|
|
2342
|
+
this._handleMapSubscribeError(rejectCtx.error);
|
|
2343
|
+
}
|
|
2344
|
+
} catch (err) {
|
|
2345
|
+
this._centrifuge._rejectionFailed(err, rejectCtx);
|
|
2346
|
+
} finally {
|
|
2347
|
+
if (rejectCtx.next) {
|
|
2348
|
+
rejectCtx.next();
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
/** Process snapshot response */
|
|
2354
|
+
_handleMapStateResponse(flow, result) {
|
|
2355
|
+
if (!this._isSubscribing()) {
|
|
2356
|
+
this._inflight = false;
|
|
1935
2357
|
return;
|
|
1936
2358
|
}
|
|
1937
2359
|
if (!result.phase) {
|
|
1938
2360
|
this._debug("map subscribe: server forced LIVE transition during state pagination");
|
|
1939
|
-
this._handleMapLiveResponse(result);
|
|
2361
|
+
this._handleMapLiveResponse(flow, result);
|
|
1940
2362
|
return;
|
|
1941
2363
|
}
|
|
1942
|
-
if (!this.
|
|
1943
|
-
this.
|
|
1944
|
-
this.
|
|
2364
|
+
if (!this._mapFlowEpoch && result.epoch) {
|
|
2365
|
+
this._mapFlowEpoch = result.epoch;
|
|
2366
|
+
this._mapFlowOffset = toOffset(result.offset);
|
|
1945
2367
|
}
|
|
1946
|
-
if (this.
|
|
2368
|
+
if (this._mapFlowEpoch && result.epoch && this._mapFlowEpoch !== result.epoch) {
|
|
1947
2369
|
this._debug("map subscribe: epoch changed during snapshot pagination, restarting");
|
|
1948
|
-
this.
|
|
1949
|
-
this.
|
|
1950
|
-
this._epoch = null;
|
|
1951
|
-
this._offset = null;
|
|
1952
|
-
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
1953
|
-
this._fetchSnapshot();
|
|
2370
|
+
this._restartMapFlow();
|
|
2371
|
+
this._fetchSnapshot(flow);
|
|
1954
2372
|
return;
|
|
1955
2373
|
}
|
|
1956
2374
|
if (result.state && result.state.length > 0) {
|
|
1957
|
-
|
|
1958
|
-
this._seedDeltaTracking(pub);
|
|
1959
|
-
this._mapStateBuffer.push(this._getMapUpdateContext(pub));
|
|
1960
|
-
}
|
|
2375
|
+
this._mapStateBuffer.push(...result.state);
|
|
1961
2376
|
}
|
|
1962
2377
|
if (result.cursor) {
|
|
1963
2378
|
this._mapCursor = result.cursor;
|
|
1964
|
-
this._fetchSnapshot(this._mapCursor);
|
|
2379
|
+
this._fetchSnapshot(flow, this._mapCursor);
|
|
1965
2380
|
return;
|
|
1966
2381
|
}
|
|
1967
|
-
this._transitionFromSnapshot();
|
|
2382
|
+
this._transitionFromSnapshot(flow);
|
|
2383
|
+
}
|
|
2384
|
+
// The stream epoch changed during the flow: what it collected and the stored
|
|
2385
|
+
// position are outdated. The flow continues from a snapshot.
|
|
2386
|
+
_restartMapFlow() {
|
|
2387
|
+
this._mapStateBuffer = [];
|
|
2388
|
+
this._mapStreamBuffer = [];
|
|
2389
|
+
this._mapCursor = "";
|
|
2390
|
+
this._mapFlowRecovering = false;
|
|
2391
|
+
this._mapFlowOffset = 0;
|
|
2392
|
+
this._mapFlowEpoch = null;
|
|
2393
|
+
this._epoch = null;
|
|
2394
|
+
this._offset = null;
|
|
2395
|
+
this._recover = false;
|
|
2396
|
+
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2397
|
+
this._mapPhase = MapPhase.State;
|
|
1968
2398
|
}
|
|
1969
2399
|
/** Transition from STATE to STREAM phase after snapshot pagination completes */
|
|
1970
|
-
_transitionFromSnapshot() {
|
|
2400
|
+
_transitionFromSnapshot(flow) {
|
|
1971
2401
|
this._debug("map subscribe: snapshot complete, transitioning to stream phase");
|
|
1972
2402
|
this._mapPhase = MapPhase.Stream;
|
|
1973
|
-
this._fetchStream();
|
|
2403
|
+
this._fetchStream(flow);
|
|
1974
2404
|
}
|
|
1975
2405
|
/** Fetch stream data (offset-based catch-up) */
|
|
1976
|
-
_fetchStream() {
|
|
2406
|
+
_fetchStream(flow) {
|
|
1977
2407
|
if (!this._isSubscribing() || !this._isTransportOpen()) {
|
|
1978
2408
|
this._inflight = false;
|
|
1979
2409
|
return;
|
|
1980
2410
|
}
|
|
1981
2411
|
const cmd = this._buildMapSubscribeCommand(MapPhase.Stream);
|
|
1982
|
-
this._debug("map subscribe: fetching stream from offset", this.
|
|
2412
|
+
this._debug("map subscribe: fetching stream from offset", this._mapFlowOffset);
|
|
2413
|
+
this._subscribeCallFlow = flow;
|
|
1983
2414
|
this._centrifuge._call(cmd).then((resolveCtx) => {
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2415
|
+
try {
|
|
2416
|
+
if (this._settleSubscribeCall(flow, true)) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (flow === this._subscribeFlow) {
|
|
2420
|
+
this._handleMapStreamResponse(flow, resolveCtx.reply.subscribe);
|
|
2421
|
+
}
|
|
2422
|
+
} catch (err) {
|
|
2423
|
+
this._centrifuge._dispatchFailed(err);
|
|
2424
|
+
} finally {
|
|
2425
|
+
if (resolveCtx.next) {
|
|
2426
|
+
resolveCtx.next();
|
|
2427
|
+
}
|
|
1988
2428
|
}
|
|
1989
2429
|
}, (rejectCtx) => {
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2430
|
+
try {
|
|
2431
|
+
if (this._settleSubscribeCall(flow, false, rejectCtx.error)) {
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
if (flow === this._subscribeFlow) {
|
|
2435
|
+
this._handleMapSubscribeError(rejectCtx.error);
|
|
2436
|
+
}
|
|
2437
|
+
} catch (err) {
|
|
2438
|
+
this._centrifuge._rejectionFailed(err, rejectCtx);
|
|
2439
|
+
} finally {
|
|
2440
|
+
if (rejectCtx.next) {
|
|
2441
|
+
rejectCtx.next();
|
|
2442
|
+
}
|
|
1993
2443
|
}
|
|
1994
2444
|
});
|
|
1995
2445
|
}
|
|
1996
2446
|
/** Process stream response */
|
|
1997
|
-
_handleMapStreamResponse(result) {
|
|
2447
|
+
_handleMapStreamResponse(flow, result) {
|
|
1998
2448
|
if (!this._isSubscribing()) {
|
|
1999
2449
|
this._inflight = false;
|
|
2000
2450
|
return;
|
|
2001
2451
|
}
|
|
2002
2452
|
if (!result.phase) {
|
|
2003
2453
|
this._debug("map subscribe: server forced LIVE transition during stream");
|
|
2004
|
-
this._handleMapLiveResponse(result);
|
|
2454
|
+
this._handleMapLiveResponse(flow, result);
|
|
2005
2455
|
return;
|
|
2006
2456
|
}
|
|
2007
|
-
if (this.
|
|
2457
|
+
if (this._mapFlowEpoch && result.epoch && this._mapFlowEpoch !== result.epoch) {
|
|
2008
2458
|
this._debug("map subscribe: epoch changed during stream, restarting");
|
|
2009
|
-
this.
|
|
2010
|
-
this.
|
|
2011
|
-
this._epoch = null;
|
|
2012
|
-
this._offset = null;
|
|
2013
|
-
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2014
|
-
this._mapPhase = MapPhase.State;
|
|
2015
|
-
this._fetchSnapshot();
|
|
2459
|
+
this._restartMapFlow();
|
|
2460
|
+
this._fetchSnapshot(flow);
|
|
2016
2461
|
return;
|
|
2017
2462
|
}
|
|
2018
2463
|
if (result.publications && result.publications.length > 0) {
|
|
2019
|
-
|
|
2020
|
-
this._seedDeltaTracking(pub);
|
|
2021
|
-
this._mapStreamBuffer.push(this._getMapUpdateContext(pub));
|
|
2022
|
-
}
|
|
2464
|
+
this._mapStreamBuffer.push(...result.publications);
|
|
2023
2465
|
}
|
|
2024
2466
|
if (result.offset !== void 0) {
|
|
2025
|
-
this.
|
|
2467
|
+
this._mapFlowOffset = toOffset(result.offset);
|
|
2026
2468
|
}
|
|
2027
|
-
this._fetchStream();
|
|
2469
|
+
this._fetchStream(flow);
|
|
2028
2470
|
}
|
|
2029
2471
|
/** Process live response - complete the map subscription */
|
|
2030
|
-
_handleMapLiveResponse(result) {
|
|
2472
|
+
_handleMapLiveResponse(flow, result) {
|
|
2031
2473
|
if (!this._isSubscribing()) {
|
|
2032
2474
|
this._inflight = false;
|
|
2033
2475
|
return;
|
|
2034
2476
|
}
|
|
2035
2477
|
this._inflight = false;
|
|
2036
|
-
if (this.
|
|
2478
|
+
if (this._mapFlowEpoch && result.epoch && this._mapFlowEpoch !== result.epoch) {
|
|
2037
2479
|
this._debug("map subscribe: epoch changed during live transition, restarting");
|
|
2038
|
-
this.
|
|
2039
|
-
this._mapStreamBuffer = [];
|
|
2040
|
-
this._epoch = null;
|
|
2041
|
-
this._offset = null;
|
|
2042
|
-
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2480
|
+
this._restartMapFlow();
|
|
2043
2481
|
this._inflight = true;
|
|
2044
|
-
this.
|
|
2045
|
-
this._fetchSnapshot();
|
|
2482
|
+
this._fetchSnapshot(flow);
|
|
2046
2483
|
return;
|
|
2047
2484
|
}
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
}
|
|
2485
|
+
const deltaNegotiated = !!(this._delta && result.delta);
|
|
2486
|
+
const statePubs = result.state && result.state.length > 0 ? this._mapStateBuffer.concat(result.state) : this._mapStateBuffer;
|
|
2487
|
+
this._mapStateBuffer = statePubs.map((pub) => {
|
|
2488
|
+
this._seedDeltaTracking(pub, deltaNegotiated);
|
|
2489
|
+
return this._getMapUpdateContext(pub);
|
|
2490
|
+
});
|
|
2491
|
+
this._mapStreamBuffer = this._mapStreamBuffer.map((pub) => {
|
|
2492
|
+
this._seedDeltaTracking(pub, deltaNegotiated);
|
|
2493
|
+
return this._getMapUpdateContext(pub);
|
|
2494
|
+
});
|
|
2054
2495
|
if (result.publications && result.publications.length > 0) {
|
|
2055
2496
|
for (const pub of result.publications) {
|
|
2056
|
-
if (
|
|
2497
|
+
if (deltaNegotiated) {
|
|
2057
2498
|
const deltaKey = pub.key || "";
|
|
2058
|
-
const { newData, newPrevValue, isDelta, wireBytes, fullBytes } = this._centrifuge._codec.applyDeltaIfNeeded(pub, this._prevValueMap.get(deltaKey));
|
|
2059
|
-
pub.data = newData;
|
|
2060
|
-
this._deltaNumPubs++;
|
|
2061
|
-
this._deltaBytesReceived += wireBytes;
|
|
2062
|
-
this._deltaBytesDecoded += fullBytes;
|
|
2063
|
-
if (isDelta) {
|
|
2064
|
-
this._deltaNumDelta++;
|
|
2065
|
-
} else {
|
|
2066
|
-
this._deltaNumFull++;
|
|
2067
|
-
}
|
|
2068
2499
|
if (pub.removed) {
|
|
2069
2500
|
this._prevValueMap.delete(deltaKey);
|
|
2070
2501
|
} else {
|
|
2502
|
+
const { newData, newPrevValue, isDelta, wireBytes, fullBytes } = this._centrifuge._codec.applyDeltaIfNeeded(pub, this._prevValueMap.get(deltaKey));
|
|
2503
|
+
pub.data = newData;
|
|
2504
|
+
this._deltaNumPubs++;
|
|
2505
|
+
this._deltaBytesReceived += wireBytes;
|
|
2506
|
+
this._deltaBytesDecoded += fullBytes;
|
|
2507
|
+
if (isDelta) {
|
|
2508
|
+
this._deltaNumDelta++;
|
|
2509
|
+
} else {
|
|
2510
|
+
this._deltaNumFull++;
|
|
2511
|
+
}
|
|
2071
2512
|
this._prevValueMap.set(deltaKey, newPrevValue);
|
|
2072
2513
|
}
|
|
2073
2514
|
} else if (this._delta && pub.key) {
|
|
@@ -2080,13 +2521,13 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2080
2521
|
this._mapStreamBuffer.push(this._getMapUpdateContext(pub));
|
|
2081
2522
|
}
|
|
2082
2523
|
}
|
|
2083
|
-
|
|
2084
|
-
|
|
2524
|
+
const offset = toOffset(result.offset);
|
|
2525
|
+
const epoch = result.epoch || "";
|
|
2085
2526
|
this._clearSubscribingState();
|
|
2086
2527
|
if (result.id) {
|
|
2087
2528
|
this._id = result.id;
|
|
2088
2529
|
}
|
|
2089
|
-
|
|
2530
|
+
const recover = result.recoverable === true;
|
|
2090
2531
|
if (result.delta) {
|
|
2091
2532
|
this._delta_negotiated = true;
|
|
2092
2533
|
} else {
|
|
@@ -2095,36 +2536,78 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2095
2536
|
if (result.publish_debounce) {
|
|
2096
2537
|
this._debounceMs = result.publish_debounce;
|
|
2097
2538
|
}
|
|
2098
|
-
this.
|
|
2539
|
+
let stateEntries = this._mapStateBuffer;
|
|
2540
|
+
let streamEntries = this._mapStreamBuffer;
|
|
2541
|
+
this._mapStateBuffer = [];
|
|
2542
|
+
this._mapStreamBuffer = [];
|
|
2543
|
+
this._mapPhase = null;
|
|
2099
2544
|
const ctx = this._centrifuge._getSubscribeContext(this.channel, result);
|
|
2100
|
-
ctx.state =
|
|
2545
|
+
ctx.state = stateEntries;
|
|
2546
|
+
const positionResets = this._mapFlowPositionResets;
|
|
2547
|
+
if (this._mapPositionResets === positionResets) {
|
|
2548
|
+
this._recover = recover;
|
|
2549
|
+
this._epoch = epoch;
|
|
2550
|
+
if (!ctx.recovered) {
|
|
2551
|
+
this._recover = false;
|
|
2552
|
+
this._offset = null;
|
|
2553
|
+
this._epoch = null;
|
|
2554
|
+
} else if (streamEntries.length > 0 && streamEntries[0].offset !== void 0) {
|
|
2555
|
+
this._offset = streamEntries[0].offset - 1;
|
|
2556
|
+
} else {
|
|
2557
|
+
this._offset = offset;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
this._setState(SubscriptionState.Subscribed);
|
|
2561
|
+
if (!this._isSubscribed()) {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2101
2564
|
this.emit("subscribed", ctx);
|
|
2565
|
+
if (!this._isSubscribed()) {
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2102
2568
|
this._resolvePromises();
|
|
2103
2569
|
if (!ctx.recovered) {
|
|
2104
|
-
if (
|
|
2570
|
+
if (streamEntries.length > 0) {
|
|
2105
2571
|
const stateMap = /* @__PURE__ */ new Map();
|
|
2106
|
-
for (const entry of
|
|
2572
|
+
for (const entry of stateEntries) {
|
|
2107
2573
|
stateMap.set(entry.key, entry);
|
|
2108
2574
|
}
|
|
2109
|
-
for (const entry of
|
|
2575
|
+
for (const entry of streamEntries) {
|
|
2110
2576
|
if (entry.removed) {
|
|
2111
2577
|
stateMap.delete(entry.key);
|
|
2112
2578
|
} else {
|
|
2113
2579
|
stateMap.set(entry.key, entry);
|
|
2114
2580
|
}
|
|
2115
2581
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2582
|
+
stateEntries = Array.from(stateMap.values());
|
|
2583
|
+
streamEntries = [];
|
|
2118
2584
|
}
|
|
2119
|
-
this.
|
|
2585
|
+
if (this._mapPositionResets === positionResets) {
|
|
2586
|
+
this._recover = recover;
|
|
2587
|
+
this._offset = offset;
|
|
2588
|
+
this._epoch = epoch;
|
|
2589
|
+
}
|
|
2590
|
+
this.emit("sync", { entries: stateEntries });
|
|
2120
2591
|
}
|
|
2121
|
-
for (const
|
|
2122
|
-
this.
|
|
2123
|
-
|
|
2592
|
+
for (const entry of streamEntries) {
|
|
2593
|
+
if (!this._isSubscribed()) {
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
this.emit("publication", entry);
|
|
2597
|
+
if (!this._isSubscribed()) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
if (entry.offset !== void 0 && this._mapPositionResets === positionResets) {
|
|
2601
|
+
this._offset = entry.offset;
|
|
2602
|
+
}
|
|
2603
|
+
this.emit("update", entry);
|
|
2604
|
+
}
|
|
2605
|
+
if (!this._isSubscribed()) {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
if (this._mapPositionResets === positionResets) {
|
|
2609
|
+
this._offset = offset;
|
|
2124
2610
|
}
|
|
2125
|
-
this._mapStateBuffer = [];
|
|
2126
|
-
this._mapStreamBuffer = [];
|
|
2127
|
-
this._mapPhase = null;
|
|
2128
2611
|
if (result.expires === true) {
|
|
2129
2612
|
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
2130
2613
|
}
|
|
@@ -2135,6 +2618,9 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2135
2618
|
if (!this._isSubscribing()) {
|
|
2136
2619
|
return;
|
|
2137
2620
|
}
|
|
2621
|
+
if (error.code === errorCodes.connectionClosed) {
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2138
2624
|
if (error.code === errorCodes.timeout) {
|
|
2139
2625
|
this._centrifuge._disconnect(connectingCodes.subscribeTimeout, "subscribe timeout", true);
|
|
2140
2626
|
return;
|
|
@@ -2142,10 +2628,10 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2142
2628
|
this._mapStateBuffer = [];
|
|
2143
2629
|
this._mapStreamBuffer = [];
|
|
2144
2630
|
this._mapPhase = null;
|
|
2145
|
-
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2146
2631
|
if (error.code === 112) {
|
|
2147
2632
|
if (this._mapUnrecoverableStrategy === "from_scratch") {
|
|
2148
2633
|
this._debug("map subscribe: unrecoverable position, restarting from scratch");
|
|
2634
|
+
this._prevValueMap = /* @__PURE__ */ new Map();
|
|
2149
2635
|
this._offset = null;
|
|
2150
2636
|
this._epoch = null;
|
|
2151
2637
|
this._recover = false;
|
|
@@ -2175,9 +2661,9 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2175
2661
|
req.limit = this._mapPageSize;
|
|
2176
2662
|
if (cursor)
|
|
2177
2663
|
req.cursor = cursor;
|
|
2178
|
-
if (this.
|
|
2179
|
-
req.offset = this.
|
|
2180
|
-
req.epoch = this.
|
|
2664
|
+
if (this._mapFlowEpoch) {
|
|
2665
|
+
req.offset = this._mapFlowOffset;
|
|
2666
|
+
req.epoch = this._mapFlowEpoch;
|
|
2181
2667
|
} else {
|
|
2182
2668
|
if (this._data)
|
|
2183
2669
|
req.data = this._data;
|
|
@@ -2186,9 +2672,9 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2186
2672
|
if (phase === MapPhase.Stream) {
|
|
2187
2673
|
if (this._mapPageSize > 0)
|
|
2188
2674
|
req.limit = this._mapPageSize;
|
|
2189
|
-
req.offset = this.
|
|
2190
|
-
req.epoch = this.
|
|
2191
|
-
if (this.
|
|
2675
|
+
req.offset = this._mapFlowOffset;
|
|
2676
|
+
req.epoch = this._mapFlowEpoch;
|
|
2677
|
+
if (this._mapFlowRecovering) {
|
|
2192
2678
|
req.recover = true;
|
|
2193
2679
|
if (this._mapStreamBuffer.length === 0) {
|
|
2194
2680
|
if (this._data)
|
|
@@ -2209,7 +2695,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2209
2695
|
ctx.removed = true;
|
|
2210
2696
|
}
|
|
2211
2697
|
if (pub.offset !== void 0) {
|
|
2212
|
-
ctx.offset = pub.offset;
|
|
2698
|
+
ctx.offset = toOffset(pub.offset);
|
|
2213
2699
|
}
|
|
2214
2700
|
if (pub.info) {
|
|
2215
2701
|
ctx.info = this._centrifuge._getJoinLeaveContext(pub.info);
|
|
@@ -2230,7 +2716,7 @@ var BaseSubscription = class extends EventEmitter {
|
|
|
2230
2716
|
ctx.removed = true;
|
|
2231
2717
|
}
|
|
2232
2718
|
if (pub.version !== void 0) {
|
|
2233
|
-
ctx.version = pub.version;
|
|
2719
|
+
ctx.version = toOffset(pub.version);
|
|
2234
2720
|
}
|
|
2235
2721
|
return ctx;
|
|
2236
2722
|
}
|
|
@@ -2307,19 +2793,17 @@ var SharedPollSubscription = class extends BaseSubscription {
|
|
|
2307
2793
|
items = keysOrItems;
|
|
2308
2794
|
}
|
|
2309
2795
|
for (const item of items) {
|
|
2796
|
+
const version = toOffset(item.version);
|
|
2310
2797
|
const existing = this._sharedPollTrackedItems.get(item.key);
|
|
2311
|
-
if (existing === void 0 ||
|
|
2312
|
-
this._sharedPollTrackedItems.set(item.key,
|
|
2798
|
+
if (existing === void 0 || version > existing) {
|
|
2799
|
+
this._sharedPollTrackedItems.set(item.key, version);
|
|
2313
2800
|
}
|
|
2314
2801
|
}
|
|
2315
2802
|
if (sig !== void 0) {
|
|
2316
|
-
this.
|
|
2317
|
-
|
|
2318
|
-
signature: sig
|
|
2319
|
-
|
|
2320
|
-
if (this._isSubscribed()) {
|
|
2321
|
-
this._sendTrackRequest([{ items, signature: sig }]).catch((err) => {
|
|
2322
|
-
this._handleTrackError(err);
|
|
2803
|
+
this._addSharedPollSignature(items.map((i) => i.key), sig);
|
|
2804
|
+
if (this._isSubscribed() && !this._sharedPollReplayPending) {
|
|
2805
|
+
this._sendTrackRequest([{ items, signature: sig }]).catch((e) => {
|
|
2806
|
+
this._handleTrackError(e.error, e.keys);
|
|
2323
2807
|
});
|
|
2324
2808
|
}
|
|
2325
2809
|
return;
|
|
@@ -2332,19 +2816,23 @@ var SharedPollSubscription = class extends BaseSubscription {
|
|
|
2332
2816
|
});
|
|
2333
2817
|
return;
|
|
2334
2818
|
}
|
|
2335
|
-
if (!this._isSubscribed()) {
|
|
2819
|
+
if (!this._isSubscribed() || this._sharedPollReplayPending) {
|
|
2336
2820
|
return;
|
|
2337
2821
|
}
|
|
2338
2822
|
const keys = items.map((i) => i.key);
|
|
2339
|
-
|
|
2340
|
-
|
|
2823
|
+
const generation = this._refreshGeneration;
|
|
2824
|
+
this._callAsync(() => this._sharedPollGetSignature({ keys })).then((result) => {
|
|
2825
|
+
if (generation !== this._refreshGeneration || !this._isSubscribed())
|
|
2341
2826
|
return;
|
|
2342
2827
|
const returnedKeys = new Set(result.keys);
|
|
2343
2828
|
const revokedKeys = [];
|
|
2344
2829
|
for (const key of keys) {
|
|
2345
|
-
if (!returnedKeys.has(key)) {
|
|
2830
|
+
if (!returnedKeys.has(key) && this._sharedPollTrackedItems.has(key)) {
|
|
2346
2831
|
this._sharedPollTrackedItems.delete(key);
|
|
2347
2832
|
revokedKeys.push(key);
|
|
2833
|
+
if (!this._isSubscribed()) {
|
|
2834
|
+
continue;
|
|
2835
|
+
}
|
|
2348
2836
|
this.emit("update", {
|
|
2349
2837
|
channel: this.channel,
|
|
2350
2838
|
key,
|
|
@@ -2358,6 +2846,7 @@ var SharedPollSubscription = class extends BaseSubscription {
|
|
|
2358
2846
|
this.emit("error", { type: "untrack", channel: this.channel, error: err });
|
|
2359
2847
|
});
|
|
2360
2848
|
}
|
|
2849
|
+
this._sharedPollReplayRetryAttempts = 0;
|
|
2361
2850
|
const authorizedItems = [];
|
|
2362
2851
|
for (const key of result.keys) {
|
|
2363
2852
|
const version = this._sharedPollTrackedItems.get(key);
|
|
@@ -2367,18 +2856,21 @@ var SharedPollSubscription = class extends BaseSubscription {
|
|
|
2367
2856
|
}
|
|
2368
2857
|
if (authorizedItems.length === 0)
|
|
2369
2858
|
return;
|
|
2370
|
-
this.
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
});
|
|
2374
|
-
this._sendTrackRequest([{ items: authorizedItems, signature: result.signature }]).catch((err) => {
|
|
2375
|
-
this._handleTrackError(err);
|
|
2859
|
+
this._addSharedPollSignature(result.keys, result.signature);
|
|
2860
|
+
this._sendTrackRequest([{ items: authorizedItems, signature: result.signature }]).catch((e) => {
|
|
2861
|
+
this._handleTrackError(e.error, e.keys);
|
|
2376
2862
|
});
|
|
2377
2863
|
}).catch((e) => {
|
|
2378
|
-
this.
|
|
2864
|
+
if (generation !== this._refreshGeneration || !this._isSubscribed())
|
|
2865
|
+
return;
|
|
2866
|
+
this._emitError({
|
|
2379
2867
|
type: "track",
|
|
2380
2868
|
channel: this.channel,
|
|
2381
2869
|
error: { code: errorCodes.sharedPollGetSignature, message: e !== void 0 ? e.toString() : "getSignature failed" }
|
|
2870
|
+
}, () => {
|
|
2871
|
+
if (generation !== this._refreshGeneration || !this._isSubscribed())
|
|
2872
|
+
return;
|
|
2873
|
+
this._scheduleSharedPollReplayRetry();
|
|
2382
2874
|
});
|
|
2383
2875
|
});
|
|
2384
2876
|
}
|
|
@@ -2444,7 +2936,9 @@ var SockjsTransport = class {
|
|
|
2444
2936
|
};
|
|
2445
2937
|
}
|
|
2446
2938
|
close() {
|
|
2447
|
-
this._transport
|
|
2939
|
+
if (this._transport !== null) {
|
|
2940
|
+
this._transport.close();
|
|
2941
|
+
}
|
|
2448
2942
|
}
|
|
2449
2943
|
send(data) {
|
|
2450
2944
|
this._transport.send(data);
|
|
@@ -2495,18 +2989,89 @@ var WebsocketTransport = class {
|
|
|
2495
2989
|
};
|
|
2496
2990
|
}
|
|
2497
2991
|
close() {
|
|
2498
|
-
this._transport
|
|
2992
|
+
if (this._transport !== null) {
|
|
2993
|
+
this._transport.close();
|
|
2994
|
+
}
|
|
2499
2995
|
}
|
|
2500
2996
|
send(data) {
|
|
2501
2997
|
this._transport.send(data);
|
|
2502
2998
|
}
|
|
2503
2999
|
};
|
|
3000
|
+
var maxRetainedSize = 64 * 1024;
|
|
3001
|
+
var ReplyStreamBuffer = class {
|
|
3002
|
+
constructor() {
|
|
3003
|
+
this._buf = new Uint8Array(0);
|
|
3004
|
+
this._start = 0;
|
|
3005
|
+
this._end = 0;
|
|
3006
|
+
}
|
|
3007
|
+
push(chunk) {
|
|
3008
|
+
const size = this._end - this._start;
|
|
3009
|
+
if (this._end + chunk.length > this._buf.length) {
|
|
3010
|
+
if (size + chunk.length <= this._buf.length / 2) {
|
|
3011
|
+
this._buf.copyWithin(0, this._start, this._end);
|
|
3012
|
+
} else {
|
|
3013
|
+
const buf = new Uint8Array(Math.max(this._buf.length * 2, size + chunk.length));
|
|
3014
|
+
buf.set(this._buf.subarray(this._start, this._end));
|
|
3015
|
+
this._buf = buf;
|
|
3016
|
+
}
|
|
3017
|
+
this._start = 0;
|
|
3018
|
+
this._end = size;
|
|
3019
|
+
}
|
|
3020
|
+
this._buf.set(chunk, this._end);
|
|
3021
|
+
this._end += chunk.length;
|
|
3022
|
+
}
|
|
3023
|
+
// Calls onReply with a copy of each complete reply buffered, in order.
|
|
3024
|
+
// decodeReply tells whether the data starts with a complete reply, and where it ends.
|
|
3025
|
+
drain(decodeReply, onReply) {
|
|
3026
|
+
while (this._start < this._end) {
|
|
3027
|
+
const result = decodeReply(this._buf.subarray(this._start, this._end));
|
|
3028
|
+
if (!result.ok) {
|
|
3029
|
+
break;
|
|
3030
|
+
}
|
|
3031
|
+
const end = this._start + result.pos;
|
|
3032
|
+
const reply = this._buf.slice(this._start, end);
|
|
3033
|
+
this._start = end;
|
|
3034
|
+
onReply(reply);
|
|
3035
|
+
}
|
|
3036
|
+
const size = this._end - this._start;
|
|
3037
|
+
if (this._buf.length > maxRetainedSize && size <= this._buf.length / 4) {
|
|
3038
|
+
this._buf = this._buf.slice(this._start, this._end);
|
|
3039
|
+
this._start = 0;
|
|
3040
|
+
this._end = size;
|
|
3041
|
+
} else if (size === 0) {
|
|
3042
|
+
this._start = 0;
|
|
3043
|
+
this._end = 0;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
var LineStreamBuffer = class {
|
|
3048
|
+
constructor() {
|
|
3049
|
+
this._decoder = new TextDecoder();
|
|
3050
|
+
this._parts = [];
|
|
3051
|
+
}
|
|
3052
|
+
// Calls onLine with each complete line, in order, without its '\n'.
|
|
3053
|
+
push(chunk, onLine) {
|
|
3054
|
+
const text = this._decoder.decode(chunk, { stream: true });
|
|
3055
|
+
let start = 0;
|
|
3056
|
+
let end = text.indexOf("\n");
|
|
3057
|
+
while (end !== -1) {
|
|
3058
|
+
this._parts.push(text.substring(start, end));
|
|
3059
|
+
const line = this._parts.join("");
|
|
3060
|
+
this._parts = [];
|
|
3061
|
+
start = end + 1;
|
|
3062
|
+
onLine(line);
|
|
3063
|
+
end = text.indexOf("\n", start);
|
|
3064
|
+
}
|
|
3065
|
+
if (start < text.length) {
|
|
3066
|
+
this._parts.push(text.substring(start));
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
};
|
|
2504
3070
|
var HttpStreamTransport = class {
|
|
2505
3071
|
constructor(endpoint, options) {
|
|
2506
3072
|
this.endpoint = endpoint;
|
|
2507
3073
|
this.options = options;
|
|
2508
3074
|
this._abortController = null;
|
|
2509
|
-
this._utf8decoder = new TextDecoder();
|
|
2510
3075
|
this._protocol = "json";
|
|
2511
3076
|
}
|
|
2512
3077
|
name() {
|
|
@@ -2528,9 +3093,8 @@ var HttpStreamTransport = class {
|
|
|
2528
3093
|
const fetchFunc = self2.options.fetch;
|
|
2529
3094
|
fetchFunc(endpoint, options).then(self2._handleErrors).then((response) => {
|
|
2530
3095
|
eventTarget.dispatchEvent(new Event("open"));
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
let protoStreamBuf = new Uint8Array();
|
|
3096
|
+
const jsonStreamBuf = new LineStreamBuffer();
|
|
3097
|
+
const protoStreamBuf = new ReplyStreamBuffer();
|
|
2534
3098
|
const reader = response.body.getReader();
|
|
2535
3099
|
return new self2.options.readableStream({
|
|
2536
3100
|
start(controller) {
|
|
@@ -2543,32 +3107,10 @@ var HttpStreamTransport = class {
|
|
|
2543
3107
|
}
|
|
2544
3108
|
try {
|
|
2545
3109
|
if (self2._protocol === "json") {
|
|
2546
|
-
jsonStreamBuf
|
|
2547
|
-
while (jsonStreamPos < jsonStreamBuf.length) {
|
|
2548
|
-
if (jsonStreamBuf[jsonStreamPos] === "\n") {
|
|
2549
|
-
const line = jsonStreamBuf.substring(0, jsonStreamPos);
|
|
2550
|
-
eventTarget.dispatchEvent(new MessageEvent("message", { data: line }));
|
|
2551
|
-
jsonStreamBuf = jsonStreamBuf.substring(jsonStreamPos + 1);
|
|
2552
|
-
jsonStreamPos = 0;
|
|
2553
|
-
} else {
|
|
2554
|
-
++jsonStreamPos;
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
3110
|
+
jsonStreamBuf.push(value, (line) => eventTarget.dispatchEvent(new MessageEvent("message", { data: line })));
|
|
2557
3111
|
} else {
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
mergedArray.set(value, protoStreamBuf.length);
|
|
2561
|
-
protoStreamBuf = mergedArray;
|
|
2562
|
-
while (true) {
|
|
2563
|
-
const result = self2.options.decoder.decodeReply(protoStreamBuf);
|
|
2564
|
-
if (result.ok) {
|
|
2565
|
-
const data = protoStreamBuf.slice(0, result.pos);
|
|
2566
|
-
eventTarget.dispatchEvent(new MessageEvent("message", { data }));
|
|
2567
|
-
protoStreamBuf = protoStreamBuf.slice(result.pos);
|
|
2568
|
-
continue;
|
|
2569
|
-
}
|
|
2570
|
-
break;
|
|
2571
|
-
}
|
|
3112
|
+
protoStreamBuf.push(value);
|
|
3113
|
+
protoStreamBuf.drain((data) => self2.options.decoder.decodeReply(data), (reply) => eventTarget.dispatchEvent(new MessageEvent("message", { data: reply })));
|
|
2572
3114
|
}
|
|
2573
3115
|
} catch (error) {
|
|
2574
3116
|
eventTarget.dispatchEvent(new Event("error", { detail: error }));
|
|
@@ -2642,7 +3184,9 @@ var HttpStreamTransport = class {
|
|
|
2642
3184
|
});
|
|
2643
3185
|
}
|
|
2644
3186
|
close() {
|
|
2645
|
-
this._abortController
|
|
3187
|
+
if (this._abortController !== null) {
|
|
3188
|
+
this._abortController.abort();
|
|
3189
|
+
}
|
|
2646
3190
|
}
|
|
2647
3191
|
send(data, session, node) {
|
|
2648
3192
|
let headers;
|
|
@@ -2669,9 +3213,18 @@ var HttpStreamTransport = class {
|
|
|
2669
3213
|
headers,
|
|
2670
3214
|
body,
|
|
2671
3215
|
mode: "cors",
|
|
2672
|
-
credentials: "same-origin"
|
|
3216
|
+
credentials: "same-origin",
|
|
3217
|
+
// Closing the transport aborts requests still pending, e.g. hung in an
|
|
3218
|
+
// intermediary, which would otherwise keep connections of the per-host pool.
|
|
3219
|
+
signal: this._abortController.signal
|
|
2673
3220
|
};
|
|
2674
|
-
fetchFunc(this.options.emulationEndpoint, fetchOptions)
|
|
3221
|
+
fetchFunc(this.options.emulationEndpoint, fetchOptions).then((response) => {
|
|
3222
|
+
if (response && (response.status === 404 || response.status >= 500)) {
|
|
3223
|
+
this.close();
|
|
3224
|
+
}
|
|
3225
|
+
}, () => {
|
|
3226
|
+
this.close();
|
|
3227
|
+
});
|
|
2675
3228
|
}
|
|
2676
3229
|
};
|
|
2677
3230
|
var SseTransport = class {
|
|
@@ -2681,6 +3234,7 @@ var SseTransport = class {
|
|
|
2681
3234
|
this._protocol = "json";
|
|
2682
3235
|
this._transport = null;
|
|
2683
3236
|
this._onClose = null;
|
|
3237
|
+
this._abortController = null;
|
|
2684
3238
|
}
|
|
2685
3239
|
name() {
|
|
2686
3240
|
return "sse";
|
|
@@ -2695,6 +3249,9 @@ var SseTransport = class {
|
|
|
2695
3249
|
return this.options.eventsource !== null && this.options.fetch !== null;
|
|
2696
3250
|
}
|
|
2697
3251
|
initialize(_protocol, callbacks, initialData) {
|
|
3252
|
+
if (typeof AbortController !== "undefined") {
|
|
3253
|
+
this._abortController = new AbortController();
|
|
3254
|
+
}
|
|
2698
3255
|
let url;
|
|
2699
3256
|
if (globalThis && globalThis.document && globalThis.document.baseURI) {
|
|
2700
3257
|
url = new URL(this.endpoint, globalThis.document.baseURI);
|
|
@@ -2728,7 +3285,12 @@ var SseTransport = class {
|
|
|
2728
3285
|
};
|
|
2729
3286
|
}
|
|
2730
3287
|
close() {
|
|
2731
|
-
this.
|
|
3288
|
+
if (this._abortController !== null) {
|
|
3289
|
+
this._abortController.abort();
|
|
3290
|
+
}
|
|
3291
|
+
if (this._transport !== null) {
|
|
3292
|
+
this._transport.close();
|
|
3293
|
+
}
|
|
2732
3294
|
if (this._onClose !== null) {
|
|
2733
3295
|
this._onClose();
|
|
2734
3296
|
}
|
|
@@ -2744,14 +3306,25 @@ var SseTransport = class {
|
|
|
2744
3306
|
};
|
|
2745
3307
|
const body = JSON.stringify(req);
|
|
2746
3308
|
const fetchFunc = this.options.fetch;
|
|
3309
|
+
const signal = this._abortController !== null ? this._abortController.signal : void 0;
|
|
2747
3310
|
const fetchOptions = {
|
|
2748
3311
|
method: "POST",
|
|
2749
3312
|
headers,
|
|
2750
3313
|
body,
|
|
2751
3314
|
mode: "cors",
|
|
2752
|
-
credentials: "same-origin"
|
|
3315
|
+
credentials: "same-origin",
|
|
3316
|
+
signal
|
|
2753
3317
|
};
|
|
2754
|
-
fetchFunc(this.options.emulationEndpoint, fetchOptions)
|
|
3318
|
+
fetchFunc(this.options.emulationEndpoint, fetchOptions).then((response) => {
|
|
3319
|
+
if (response && (response.status === 404 || response.status >= 500)) {
|
|
3320
|
+
this.close();
|
|
3321
|
+
}
|
|
3322
|
+
}, () => {
|
|
3323
|
+
if (signal && signal.aborted) {
|
|
3324
|
+
return;
|
|
3325
|
+
}
|
|
3326
|
+
this.close();
|
|
3327
|
+
});
|
|
2755
3328
|
}
|
|
2756
3329
|
};
|
|
2757
3330
|
var WebtransportTransport = class {
|
|
@@ -2761,7 +3334,6 @@ var WebtransportTransport = class {
|
|
|
2761
3334
|
this._transport = null;
|
|
2762
3335
|
this._stream = null;
|
|
2763
3336
|
this._writer = null;
|
|
2764
|
-
this._utf8decoder = new TextDecoder();
|
|
2765
3337
|
this._protocol = "json";
|
|
2766
3338
|
}
|
|
2767
3339
|
name() {
|
|
@@ -2832,40 +3404,17 @@ var WebtransportTransport = class {
|
|
|
2832
3404
|
_startReading(eventTarget) {
|
|
2833
3405
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2834
3406
|
const reader = this._stream.readable.getReader();
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
let protoStreamBuf = new Uint8Array();
|
|
3407
|
+
const jsonStreamBuf = new LineStreamBuffer();
|
|
3408
|
+
const protoStreamBuf = new ReplyStreamBuffer();
|
|
2838
3409
|
try {
|
|
2839
3410
|
while (true) {
|
|
2840
3411
|
const { done, value } = yield reader.read();
|
|
2841
3412
|
if (value && value.length > 0) {
|
|
2842
3413
|
if (this._protocol === "json") {
|
|
2843
|
-
jsonStreamBuf
|
|
2844
|
-
while (jsonStreamPos < jsonStreamBuf.length) {
|
|
2845
|
-
if (jsonStreamBuf[jsonStreamPos] === "\n") {
|
|
2846
|
-
const line = jsonStreamBuf.substring(0, jsonStreamPos);
|
|
2847
|
-
eventTarget.dispatchEvent(new MessageEvent("message", { data: line }));
|
|
2848
|
-
jsonStreamBuf = jsonStreamBuf.substring(jsonStreamPos + 1);
|
|
2849
|
-
jsonStreamPos = 0;
|
|
2850
|
-
} else {
|
|
2851
|
-
++jsonStreamPos;
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
3414
|
+
jsonStreamBuf.push(value, (line) => eventTarget.dispatchEvent(new MessageEvent("message", { data: line })));
|
|
2854
3415
|
} else {
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
mergedArray.set(value, protoStreamBuf.length);
|
|
2858
|
-
protoStreamBuf = mergedArray;
|
|
2859
|
-
while (true) {
|
|
2860
|
-
const result = this.options.decoder.decodeReply(protoStreamBuf);
|
|
2861
|
-
if (result.ok) {
|
|
2862
|
-
const data = protoStreamBuf.slice(0, result.pos);
|
|
2863
|
-
eventTarget.dispatchEvent(new MessageEvent("message", { data }));
|
|
2864
|
-
protoStreamBuf = protoStreamBuf.slice(result.pos);
|
|
2865
|
-
continue;
|
|
2866
|
-
}
|
|
2867
|
-
break;
|
|
2868
|
-
}
|
|
3416
|
+
protoStreamBuf.push(value);
|
|
3417
|
+
protoStreamBuf.drain((data) => this.options.decoder.decodeReply(data), (reply) => eventTarget.dispatchEvent(new MessageEvent("message", { data: reply })));
|
|
2869
3418
|
}
|
|
2870
3419
|
}
|
|
2871
3420
|
if (done) {
|
|
@@ -3179,7 +3728,7 @@ var JsonCodec = class {
|
|
|
3179
3728
|
return commands.map((c) => JSON.stringify(c)).join("\n");
|
|
3180
3729
|
}
|
|
3181
3730
|
decodeReplies(data) {
|
|
3182
|
-
return data.
|
|
3731
|
+
return data.split("\n").filter((r) => r.trim() !== "").map((r) => JSON.parse(r));
|
|
3183
3732
|
}
|
|
3184
3733
|
applyDeltaIfNeeded(pub, prevValue) {
|
|
3185
3734
|
let newData, newPrevValue, wireBytes;
|
|
@@ -3220,8 +3769,19 @@ var defaults = {
|
|
|
3220
3769
|
maxReconnectDelay: 2e4,
|
|
3221
3770
|
timeout: 5e3,
|
|
3222
3771
|
maxServerPingDelay: 1e4,
|
|
3223
|
-
networkEventTarget: null
|
|
3772
|
+
networkEventTarget: null,
|
|
3773
|
+
pageEventTarget: null
|
|
3224
3774
|
};
|
|
3775
|
+
var websocketNotFound = "WebSocket constructor not found, make sure it is available globally or passed as a dependency in Centrifuge options";
|
|
3776
|
+
var lateCallTimer = 1e3;
|
|
3777
|
+
var lateCallGrace = 1e3;
|
|
3778
|
+
function errorMessage(err) {
|
|
3779
|
+
try {
|
|
3780
|
+
return err instanceof Error ? err.message : String(err);
|
|
3781
|
+
} catch (e) {
|
|
3782
|
+
return "value without a string form";
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3225
3785
|
var UnauthorizedError = class extends Error {
|
|
3226
3786
|
constructor(message) {
|
|
3227
3787
|
super(message);
|
|
@@ -3233,8 +3793,12 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3233
3793
|
constructor(endpoint, options) {
|
|
3234
3794
|
super();
|
|
3235
3795
|
this._reconnectTimeout = null;
|
|
3796
|
+
this._connectTimeout = null;
|
|
3797
|
+
this._removedUnsubscribes = {};
|
|
3798
|
+
this._pendingServerSubOffsets = {};
|
|
3236
3799
|
this._refreshTimeout = null;
|
|
3237
3800
|
this._serverPingTimeout = null;
|
|
3801
|
+
this._handlerFailures = 0;
|
|
3238
3802
|
this.state = State.Disconnected;
|
|
3239
3803
|
this._transportIsOpen = false;
|
|
3240
3804
|
this._endpoint = endpoint;
|
|
@@ -3250,6 +3814,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3250
3814
|
this._codec = new JsonCodec();
|
|
3251
3815
|
this._reconnecting = false;
|
|
3252
3816
|
this._reconnectTimeout = null;
|
|
3817
|
+
this._connectTimeout = null;
|
|
3253
3818
|
this._reconnectAttempts = 0;
|
|
3254
3819
|
this._client = null;
|
|
3255
3820
|
this._session = "";
|
|
@@ -3271,7 +3836,13 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3271
3836
|
this._promises = {};
|
|
3272
3837
|
this._promiseId = 0;
|
|
3273
3838
|
this._debugEnabled = false;
|
|
3274
|
-
this.
|
|
3839
|
+
this._networkEvents = null;
|
|
3840
|
+
this._pageEvents = null;
|
|
3841
|
+
this._pageCached = false;
|
|
3842
|
+
this._stateTransitions = 0;
|
|
3843
|
+
this._disconnects = 0;
|
|
3844
|
+
this._abortedTransportId = 0;
|
|
3845
|
+
this._reconnectAttemptsResetPending = false;
|
|
3275
3846
|
this._config = Object.assign(Object.assign({}, defaults), options);
|
|
3276
3847
|
this._configure();
|
|
3277
3848
|
if (this._debugEnabled) {
|
|
@@ -3422,10 +3993,15 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3422
3993
|
if (!sub) {
|
|
3423
3994
|
return;
|
|
3424
3995
|
}
|
|
3996
|
+
if (this._subs[sub.channel] !== sub) {
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
3999
|
+
delete this._subs[sub.channel];
|
|
4000
|
+
sub._removed = true;
|
|
3425
4001
|
if (sub.state !== SubscriptionState.Unsubscribed) {
|
|
3426
4002
|
sub.unsubscribe();
|
|
3427
4003
|
}
|
|
3428
|
-
this.
|
|
4004
|
+
this._recordRemovedUnsubscribe(sub);
|
|
3429
4005
|
}
|
|
3430
4006
|
/** Remove a map subscription. */
|
|
3431
4007
|
removeMapSubscription(sub) {
|
|
@@ -3469,13 +4045,15 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3469
4045
|
return;
|
|
3470
4046
|
default:
|
|
3471
4047
|
return new Promise((resolve, reject) => {
|
|
4048
|
+
const id = this._nextPromiseId();
|
|
3472
4049
|
const ctx = { resolve, reject };
|
|
3473
4050
|
if (timeout) {
|
|
3474
4051
|
ctx.timeout = setTimeout(() => {
|
|
4052
|
+
delete this._promises[id];
|
|
3475
4053
|
reject({ code: errorCodes.timeout, message: "timeout" });
|
|
3476
4054
|
}, timeout);
|
|
3477
4055
|
}
|
|
3478
|
-
this._promises[
|
|
4056
|
+
this._promises[id] = ctx;
|
|
3479
4057
|
});
|
|
3480
4058
|
}
|
|
3481
4059
|
});
|
|
@@ -3491,6 +4069,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3491
4069
|
return;
|
|
3492
4070
|
}
|
|
3493
4071
|
this._debug("connect called");
|
|
4072
|
+
this._validateTransportConfig();
|
|
3494
4073
|
this._reconnectAttempts = 0;
|
|
3495
4074
|
this._startConnecting();
|
|
3496
4075
|
}
|
|
@@ -3501,6 +4080,9 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3501
4080
|
/** setToken allows setting connection token. Or resetting used token to be empty. */
|
|
3502
4081
|
setToken(token) {
|
|
3503
4082
|
this._token = token;
|
|
4083
|
+
if (this._config.getToken === null) {
|
|
4084
|
+
this._refreshRequired = false;
|
|
4085
|
+
}
|
|
3504
4086
|
}
|
|
3505
4087
|
/** setData allows setting connection data. This only affects the next connection attempt,
|
|
3506
4088
|
* not the current one. Note that if getData callback is configured, it will override
|
|
@@ -3605,7 +4187,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3605
4187
|
return {
|
|
3606
4188
|
publications,
|
|
3607
4189
|
epoch: result.epoch || "",
|
|
3608
|
-
offset: result.offset
|
|
4190
|
+
offset: toOffset(result.offset)
|
|
3609
4191
|
};
|
|
3610
4192
|
});
|
|
3611
4193
|
}
|
|
@@ -3721,15 +4303,29 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3721
4303
|
throw new Error("unsupported url configuration type: only string or array of objects are supported");
|
|
3722
4304
|
}
|
|
3723
4305
|
}
|
|
4306
|
+
// Returns the number of the transition made, or 0 if the client already was in newState.
|
|
3724
4307
|
_setState(newState) {
|
|
3725
4308
|
if (this.state !== newState) {
|
|
3726
4309
|
this._reconnecting = false;
|
|
3727
4310
|
const oldState = this.state;
|
|
3728
4311
|
this.state = newState;
|
|
4312
|
+
const transition = ++this._stateTransitions;
|
|
4313
|
+
if (newState === State.Connecting) {
|
|
4314
|
+
this._setNetworkEvents();
|
|
4315
|
+
this._setPageEvents();
|
|
4316
|
+
} else if (newState === State.Disconnected) {
|
|
4317
|
+
this._clearNetworkEvents();
|
|
4318
|
+
this._clearPageEvents();
|
|
4319
|
+
}
|
|
3729
4320
|
this.emit("state", { newState, oldState });
|
|
3730
|
-
return
|
|
4321
|
+
return transition;
|
|
3731
4322
|
}
|
|
3732
|
-
return
|
|
4323
|
+
return 0;
|
|
4324
|
+
}
|
|
4325
|
+
// Whether an event handler made another state transition after the given one
|
|
4326
|
+
// (as returned by _setState), so that the given one is outdated.
|
|
4327
|
+
_transitionSuperseded(transition) {
|
|
4328
|
+
return transition !== 0 && this._stateTransitions !== transition;
|
|
3733
4329
|
}
|
|
3734
4330
|
_isDisconnected() {
|
|
3735
4331
|
return this.state === State.Disconnected;
|
|
@@ -3744,7 +4340,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3744
4340
|
return ++this._commandId;
|
|
3745
4341
|
}
|
|
3746
4342
|
_setNetworkEvents() {
|
|
3747
|
-
if (this.
|
|
4343
|
+
if (this._networkEvents !== null) {
|
|
3748
4344
|
return;
|
|
3749
4345
|
}
|
|
3750
4346
|
let eventTarget = null;
|
|
@@ -3754,33 +4350,120 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3754
4350
|
eventTarget = globalThis;
|
|
3755
4351
|
}
|
|
3756
4352
|
if (eventTarget) {
|
|
3757
|
-
|
|
4353
|
+
const onOffline = () => {
|
|
3758
4354
|
this._debug("offline event triggered");
|
|
3759
4355
|
if (this.state === State.Connected || this.state === State.Connecting) {
|
|
3760
|
-
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
3761
4356
|
this._deviceWentOffline = true;
|
|
4357
|
+
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
3762
4358
|
}
|
|
3763
|
-
}
|
|
3764
|
-
|
|
4359
|
+
};
|
|
4360
|
+
const onOnline = () => {
|
|
3765
4361
|
this._debug("online event triggered");
|
|
4362
|
+
const wentOffline = this._deviceWentOffline;
|
|
4363
|
+
this._deviceWentOffline = false;
|
|
3766
4364
|
if (this.state !== State.Connecting) {
|
|
3767
4365
|
return;
|
|
3768
4366
|
}
|
|
3769
|
-
if (
|
|
3770
|
-
this.
|
|
3771
|
-
this.
|
|
4367
|
+
if (wentOffline && !this._transportClosed) {
|
|
4368
|
+
this._abortedTransportId = this._transportId;
|
|
4369
|
+
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
3772
4370
|
}
|
|
3773
4371
|
this._clearReconnectTimeout();
|
|
3774
4372
|
this._startReconnecting();
|
|
3775
|
-
}
|
|
3776
|
-
|
|
4373
|
+
};
|
|
4374
|
+
eventTarget.addEventListener("offline", onOffline);
|
|
4375
|
+
eventTarget.addEventListener("online", onOnline);
|
|
4376
|
+
this._networkEvents = { target: eventTarget, onOffline, onOnline };
|
|
3777
4377
|
}
|
|
3778
4378
|
}
|
|
4379
|
+
// The online listener drives reconnect, so listeners stay until the client is
|
|
4380
|
+
// disconnected; removing them then lets a disconnected client be garbage-collected.
|
|
4381
|
+
_clearNetworkEvents() {
|
|
4382
|
+
if (this._networkEvents === null) {
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
const { target, onOffline, onOnline } = this._networkEvents;
|
|
4386
|
+
if (!isFunction(target.removeEventListener)) {
|
|
4387
|
+
this._debug("network event target has no removeEventListener, listeners kept");
|
|
4388
|
+
return;
|
|
4389
|
+
}
|
|
4390
|
+
target.removeEventListener("offline", onOffline);
|
|
4391
|
+
target.removeEventListener("online", onOnline);
|
|
4392
|
+
this._networkEvents = null;
|
|
4393
|
+
}
|
|
4394
|
+
// A page entering the back/forward cache is frozen, but the browser may keep its
|
|
4395
|
+
// socket open: the server then sees a client that stopped answering pings. So the
|
|
4396
|
+
// transport is closed when the page is hidden into the cache, and the client
|
|
4397
|
+
// reconnects as soon as the page is restored from it. The reconnect is scheduled
|
|
4398
|
+
// as usual too: its timer does not run in a frozen page, and it still reconnects
|
|
4399
|
+
// a page which was not frozen, or was restored without a pageshow event.
|
|
4400
|
+
_setPageEvents() {
|
|
4401
|
+
if (this._pageEvents !== null) {
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
4404
|
+
let eventTarget = null;
|
|
4405
|
+
if (this._config.pageEventTarget !== null) {
|
|
4406
|
+
eventTarget = this._config.pageEventTarget;
|
|
4407
|
+
} else if (typeof globalThis.addEventListener !== "undefined") {
|
|
4408
|
+
eventTarget = globalThis;
|
|
4409
|
+
}
|
|
4410
|
+
if (!eventTarget) {
|
|
4411
|
+
return;
|
|
4412
|
+
}
|
|
4413
|
+
const onPageHide = (e) => {
|
|
4414
|
+
if (!e.persisted) {
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
4417
|
+
this._debug("page entered back/forward cache");
|
|
4418
|
+
if (this.state === State.Connected || this.state === State.Connecting) {
|
|
4419
|
+
this._pageCached = true;
|
|
4420
|
+
this._disconnect(connectingCodes.transportClosed, "page entered back/forward cache", true);
|
|
4421
|
+
}
|
|
4422
|
+
};
|
|
4423
|
+
const onPageShow = (e) => {
|
|
4424
|
+
if (!e.persisted) {
|
|
4425
|
+
return;
|
|
4426
|
+
}
|
|
4427
|
+
this._debug("page restored from back/forward cache");
|
|
4428
|
+
const cached = this._pageCached;
|
|
4429
|
+
this._pageCached = false;
|
|
4430
|
+
if (!cached || this.state !== State.Connecting) {
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
this._clearReconnectTimeout();
|
|
4434
|
+
this._startReconnecting();
|
|
4435
|
+
};
|
|
4436
|
+
eventTarget.addEventListener("pagehide", onPageHide);
|
|
4437
|
+
eventTarget.addEventListener("pageshow", onPageShow);
|
|
4438
|
+
this._pageEvents = { target: eventTarget, onPageHide, onPageShow };
|
|
4439
|
+
}
|
|
4440
|
+
// Removed once the client is disconnected, like the network listeners.
|
|
4441
|
+
_clearPageEvents() {
|
|
4442
|
+
if (this._pageEvents === null) {
|
|
4443
|
+
return;
|
|
4444
|
+
}
|
|
4445
|
+
const { target, onPageHide, onPageShow } = this._pageEvents;
|
|
4446
|
+
if (!isFunction(target.removeEventListener)) {
|
|
4447
|
+
this._debug("page event target has no removeEventListener, listeners kept");
|
|
4448
|
+
return;
|
|
4449
|
+
}
|
|
4450
|
+
target.removeEventListener("pagehide", onPageHide);
|
|
4451
|
+
target.removeEventListener("pageshow", onPageShow);
|
|
4452
|
+
this._pageEvents = null;
|
|
4453
|
+
}
|
|
3779
4454
|
_getReconnectDelay() {
|
|
3780
4455
|
const delay = backoff(this._reconnectAttempts, this._config.minReconnectDelay, this._config.maxReconnectDelay);
|
|
3781
4456
|
this._reconnectAttempts += 1;
|
|
3782
4457
|
return delay;
|
|
3783
4458
|
}
|
|
4459
|
+
// See _connectResponse: an emulation connection resets the backoff once it
|
|
4460
|
+
// proved usable.
|
|
4461
|
+
_resetReconnectAttemptsIfPending() {
|
|
4462
|
+
if (this._reconnectAttemptsResetPending) {
|
|
4463
|
+
this._reconnectAttemptsResetPending = false;
|
|
4464
|
+
this._reconnectAttempts = 0;
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
3784
4467
|
_clearOutgoingRequests() {
|
|
3785
4468
|
for (const id in this._callbacks) {
|
|
3786
4469
|
if (this._callbacks.hasOwnProperty(id)) {
|
|
@@ -3809,7 +4492,8 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3809
4492
|
}
|
|
3810
4493
|
}
|
|
3811
4494
|
for (const channel in this._serverSubs) {
|
|
3812
|
-
if (this._serverSubs.hasOwnProperty(channel)) {
|
|
4495
|
+
if (this._serverSubs.hasOwnProperty(channel) && this._serverSubs[channel].subscribed) {
|
|
4496
|
+
this._serverSubs[channel].subscribed = false;
|
|
3813
4497
|
this.emit("subscribing", { channel });
|
|
3814
4498
|
}
|
|
3815
4499
|
}
|
|
@@ -3850,7 +4534,10 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3850
4534
|
this._currentTransportIndex = 0;
|
|
3851
4535
|
}
|
|
3852
4536
|
}
|
|
3853
|
-
|
|
4537
|
+
// Transport dependencies from the config, or else from globals. An absent one
|
|
4538
|
+
// stays null, not undefined: supported() of sockjs, sse and http_stream only
|
|
4539
|
+
// checks `!== null`.
|
|
4540
|
+
_resolveTransportDeps() {
|
|
3854
4541
|
let websocket;
|
|
3855
4542
|
if (this._config.websocket !== null) {
|
|
3856
4543
|
websocket = this._config.websocket;
|
|
@@ -3891,101 +4578,105 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3891
4578
|
readableStream = globalThis.ReadableStream;
|
|
3892
4579
|
}
|
|
3893
4580
|
}
|
|
4581
|
+
return { websocket, sockjs, eventsource, fetchFunc, readableStream };
|
|
4582
|
+
}
|
|
4583
|
+
// Constructs a transport, without checking that it is supported.
|
|
4584
|
+
_createTransport(transportName, endpoint, deps) {
|
|
4585
|
+
switch (transportName) {
|
|
4586
|
+
case "websocket":
|
|
4587
|
+
return new WebsocketTransport(endpoint, {
|
|
4588
|
+
websocket: deps.websocket
|
|
4589
|
+
});
|
|
4590
|
+
case "webtransport":
|
|
4591
|
+
return new WebtransportTransport(endpoint, {
|
|
4592
|
+
webtransport: globalThis.WebTransport,
|
|
4593
|
+
decoder: this._codec,
|
|
4594
|
+
encoder: this._codec
|
|
4595
|
+
});
|
|
4596
|
+
case "http_stream":
|
|
4597
|
+
return new HttpStreamTransport(endpoint, {
|
|
4598
|
+
fetch: deps.fetchFunc,
|
|
4599
|
+
readableStream: deps.readableStream,
|
|
4600
|
+
emulationEndpoint: this._config.emulationEndpoint,
|
|
4601
|
+
decoder: this._codec,
|
|
4602
|
+
encoder: this._codec
|
|
4603
|
+
});
|
|
4604
|
+
case "sse":
|
|
4605
|
+
return new SseTransport(endpoint, {
|
|
4606
|
+
eventsource: deps.eventsource,
|
|
4607
|
+
fetch: deps.fetchFunc,
|
|
4608
|
+
emulationEndpoint: this._config.emulationEndpoint
|
|
4609
|
+
});
|
|
4610
|
+
case "sockjs":
|
|
4611
|
+
return new SockjsTransport(endpoint, {
|
|
4612
|
+
sockjs: deps.sockjs,
|
|
4613
|
+
sockjsOptions: this._config.sockjsOptions
|
|
4614
|
+
});
|
|
4615
|
+
default:
|
|
4616
|
+
throw new Error("unknown transport " + transportName);
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
// Throws for a configuration no retry can fix. Called from connect() before
|
|
4620
|
+
// the state changes, so the client stays disconnected, and before a token or
|
|
4621
|
+
// data is loaded, so it fails the same way with getToken or getData.
|
|
4622
|
+
_validateTransportConfig() {
|
|
4623
|
+
const deps = this._resolveTransportDeps();
|
|
3894
4624
|
if (!this._emulation) {
|
|
3895
4625
|
if (startsWith(this._endpoint, "http")) {
|
|
3896
4626
|
throw new Error("Provide explicit transport endpoints configuration in case of using HTTP (i.e. using array of TransportEndpoint instead of a single string), or use ws(s):// scheme in an endpoint if you aimed using WebSocket transport");
|
|
3897
|
-
}
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4627
|
+
}
|
|
4628
|
+
if (!this._createTransport("websocket", this._endpoint, deps).supported()) {
|
|
4629
|
+
throw new Error(websocketNotFound);
|
|
4630
|
+
}
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
for (const transportConfig of this._transports) {
|
|
4634
|
+
if (this._createTransport(transportConfig.transport, transportConfig.endpoint, deps).supported()) {
|
|
4635
|
+
return;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
throw new Error("no supported transport found");
|
|
4639
|
+
}
|
|
4640
|
+
_initializeTransport() {
|
|
4641
|
+
const deps = this._resolveTransportDeps();
|
|
4642
|
+
let selected = null;
|
|
4643
|
+
if (!this._emulation) {
|
|
4644
|
+
this._debug("client will use websocket");
|
|
4645
|
+
const candidate = this._createTransport("websocket", this._endpoint, deps);
|
|
4646
|
+
if (candidate.supported()) {
|
|
4647
|
+
selected = candidate;
|
|
3905
4648
|
}
|
|
3906
4649
|
} else {
|
|
3907
4650
|
if (this._currentTransportIndex >= this._transports.length) {
|
|
3908
4651
|
this._triedAllTransports = true;
|
|
3909
4652
|
this._currentTransportIndex = 0;
|
|
3910
4653
|
}
|
|
3911
|
-
let count = 0;
|
|
3912
|
-
while (true) {
|
|
3913
|
-
if (count >= this._transports.length) {
|
|
3914
|
-
throw new Error("no supported transport found");
|
|
3915
|
-
}
|
|
4654
|
+
for (let count = 0; count < this._transports.length; count++) {
|
|
3916
4655
|
const transportConfig = this._transports[this._currentTransportIndex];
|
|
3917
|
-
|
|
3918
|
-
const
|
|
3919
|
-
if (
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
websocket
|
|
3923
|
-
});
|
|
3924
|
-
if (!this._transport.supported()) {
|
|
3925
|
-
this._debug("websocket transport not available");
|
|
3926
|
-
this._advanceTransportIndex();
|
|
3927
|
-
count++;
|
|
3928
|
-
continue;
|
|
3929
|
-
}
|
|
3930
|
-
} else if (transportName === "webtransport") {
|
|
3931
|
-
this._debug("trying webtransport transport");
|
|
3932
|
-
this._transport = new WebtransportTransport(transportEndpoint, {
|
|
3933
|
-
webtransport: globalThis.WebTransport,
|
|
3934
|
-
decoder: this._codec,
|
|
3935
|
-
encoder: this._codec
|
|
3936
|
-
});
|
|
3937
|
-
if (!this._transport.supported()) {
|
|
3938
|
-
this._debug("webtransport transport not available");
|
|
3939
|
-
this._advanceTransportIndex();
|
|
3940
|
-
count++;
|
|
3941
|
-
continue;
|
|
3942
|
-
}
|
|
3943
|
-
} else if (transportName === "http_stream") {
|
|
3944
|
-
this._debug("trying http_stream transport");
|
|
3945
|
-
this._transport = new HttpStreamTransport(transportEndpoint, {
|
|
3946
|
-
fetch: fetchFunc,
|
|
3947
|
-
readableStream,
|
|
3948
|
-
emulationEndpoint: this._config.emulationEndpoint,
|
|
3949
|
-
decoder: this._codec,
|
|
3950
|
-
encoder: this._codec
|
|
3951
|
-
});
|
|
3952
|
-
if (!this._transport.supported()) {
|
|
3953
|
-
this._debug("http_stream transport not available");
|
|
3954
|
-
this._advanceTransportIndex();
|
|
3955
|
-
count++;
|
|
3956
|
-
continue;
|
|
3957
|
-
}
|
|
3958
|
-
} else if (transportName === "sse") {
|
|
3959
|
-
this._debug("trying sse transport");
|
|
3960
|
-
this._transport = new SseTransport(transportEndpoint, {
|
|
3961
|
-
eventsource,
|
|
3962
|
-
fetch: fetchFunc,
|
|
3963
|
-
emulationEndpoint: this._config.emulationEndpoint
|
|
3964
|
-
});
|
|
3965
|
-
if (!this._transport.supported()) {
|
|
3966
|
-
this._debug("sse transport not available");
|
|
3967
|
-
this._advanceTransportIndex();
|
|
3968
|
-
count++;
|
|
3969
|
-
continue;
|
|
3970
|
-
}
|
|
3971
|
-
} else if (transportName === "sockjs") {
|
|
3972
|
-
this._debug("trying sockjs");
|
|
3973
|
-
this._transport = new SockjsTransport(transportEndpoint, {
|
|
3974
|
-
sockjs,
|
|
3975
|
-
sockjsOptions: this._config.sockjsOptions
|
|
3976
|
-
});
|
|
3977
|
-
if (!this._transport.supported()) {
|
|
3978
|
-
this._debug("sockjs transport not available");
|
|
3979
|
-
this._advanceTransportIndex();
|
|
3980
|
-
count++;
|
|
3981
|
-
continue;
|
|
3982
|
-
}
|
|
3983
|
-
} else {
|
|
3984
|
-
throw new Error("unknown transport " + transportName);
|
|
4656
|
+
this._debug("trying " + transportConfig.transport + " transport");
|
|
4657
|
+
const candidate = this._createTransport(transportConfig.transport, transportConfig.endpoint, deps);
|
|
4658
|
+
if (candidate.supported()) {
|
|
4659
|
+
selected = candidate;
|
|
4660
|
+
break;
|
|
3985
4661
|
}
|
|
3986
|
-
|
|
4662
|
+
this._debug(transportConfig.transport + " transport not available");
|
|
4663
|
+
this._advanceTransportIndex();
|
|
3987
4664
|
}
|
|
3988
4665
|
}
|
|
4666
|
+
if (selected === null) {
|
|
4667
|
+
const message = this._emulation ? "no supported transport found" : websocketNotFound;
|
|
4668
|
+
this._debug(message);
|
|
4669
|
+
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
4670
|
+
this.emit("error", {
|
|
4671
|
+
type: "transport",
|
|
4672
|
+
error: {
|
|
4673
|
+
code: errorCodes.transportClosed,
|
|
4674
|
+
message
|
|
4675
|
+
}
|
|
4676
|
+
});
|
|
4677
|
+
return;
|
|
4678
|
+
}
|
|
4679
|
+
this._transport = selected;
|
|
3989
4680
|
const self2 = this;
|
|
3990
4681
|
const transport = this._transport;
|
|
3991
4682
|
const transportId = this._nextTransportId();
|
|
@@ -3996,24 +4687,48 @@ var Centrifuge = class extends EventEmitter {
|
|
|
3996
4687
|
const connectCommand = self2._sendConnect(true);
|
|
3997
4688
|
initialCommands.push(connectCommand);
|
|
3998
4689
|
}
|
|
3999
|
-
this._setNetworkEvents();
|
|
4000
|
-
const initialData = this._codec.encodeCommands(initialCommands);
|
|
4001
4690
|
this._transportClosed = false;
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4691
|
+
const failTransport = function(reason) {
|
|
4692
|
+
if (self2._transportId != transportId) {
|
|
4693
|
+
self2._debug("failure of non-actual transport", reason);
|
|
4694
|
+
return;
|
|
4695
|
+
}
|
|
4696
|
+
self2._debug(transport.name(), "transport failed:", reason);
|
|
4697
|
+
if (self2._emulation && !self2._transportWasOpen) {
|
|
4698
|
+
self2._advanceTransportIndex();
|
|
4699
|
+
}
|
|
4700
|
+
self2._abortedTransportId = transportId;
|
|
4701
|
+
self2._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
4702
|
+
self2.emit("error", {
|
|
4703
|
+
type: "transport",
|
|
4704
|
+
error: {
|
|
4705
|
+
code: errorCodes.transportClosed,
|
|
4706
|
+
message: reason
|
|
4707
|
+
},
|
|
4708
|
+
transport: transport.name()
|
|
4709
|
+
});
|
|
4710
|
+
};
|
|
4711
|
+
this._clearConnectTimeout();
|
|
4712
|
+
const connectDue = Date.now() + this._config.timeout;
|
|
4713
|
+
this._connectTimeout = setTimeout(function() {
|
|
4714
|
+
const late = Date.now() - connectDue >= lateCallTimer;
|
|
4715
|
+
self2._connectTimeout = setTimeout(function() {
|
|
4716
|
+
self2._connectTimeout = null;
|
|
4717
|
+
failTransport("connect timeout");
|
|
4718
|
+
}, late ? lateCallGrace : 0);
|
|
4005
4719
|
}, this._config.timeout);
|
|
4006
|
-
|
|
4720
|
+
const callbacks = {
|
|
4007
4721
|
onOpen: function() {
|
|
4008
|
-
if (connectTimeout) {
|
|
4009
|
-
clearTimeout(connectTimeout);
|
|
4010
|
-
connectTimeout = null;
|
|
4011
|
-
}
|
|
4012
4722
|
if (self2._transportId != transportId) {
|
|
4013
4723
|
self2._debug("open callback from non-actual transport");
|
|
4014
|
-
|
|
4724
|
+
try {
|
|
4725
|
+
transport.close();
|
|
4726
|
+
} catch (e) {
|
|
4727
|
+
self2._debug("error closing transport", e);
|
|
4728
|
+
}
|
|
4015
4729
|
return;
|
|
4016
4730
|
}
|
|
4731
|
+
self2._clearConnectTimeout();
|
|
4017
4732
|
wasOpen = true;
|
|
4018
4733
|
self2._debug(transport.subName(), "transport open");
|
|
4019
4734
|
if (transport.emulation()) {
|
|
@@ -4022,9 +4737,14 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4022
4737
|
self2._transportIsOpen = true;
|
|
4023
4738
|
self2._transportWasOpen = true;
|
|
4024
4739
|
self2.startBatching();
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4740
|
+
try {
|
|
4741
|
+
self2._sendConnect(false);
|
|
4742
|
+
self2._sendSubscribeCommands();
|
|
4743
|
+
} catch (e) {
|
|
4744
|
+
self2._reportDispatchError(e);
|
|
4745
|
+
} finally {
|
|
4746
|
+
self2.stopBatching();
|
|
4747
|
+
}
|
|
4028
4748
|
self2.emit("__centrifuge_debug:connect_frame_sent", {});
|
|
4029
4749
|
},
|
|
4030
4750
|
onError: function(e) {
|
|
@@ -4034,15 +4754,16 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4034
4754
|
}
|
|
4035
4755
|
self2._debug("transport level error", e);
|
|
4036
4756
|
},
|
|
4037
|
-
onClose: function(closeEvent) {
|
|
4038
|
-
if (connectTimeout) {
|
|
4039
|
-
clearTimeout(connectTimeout);
|
|
4040
|
-
connectTimeout = null;
|
|
4041
|
-
}
|
|
4757
|
+
onClose: function handleClose(closeEvent, dispatched) {
|
|
4042
4758
|
if (self2._transportId != transportId) {
|
|
4043
4759
|
self2._debug("close callback from non-actual transport");
|
|
4044
4760
|
return;
|
|
4045
4761
|
}
|
|
4762
|
+
if (!dispatched && (transport.emulation() || transport.name() === "webtransport")) {
|
|
4763
|
+
self2._dispatchPromise.then(() => handleClose(closeEvent, true));
|
|
4764
|
+
return;
|
|
4765
|
+
}
|
|
4766
|
+
self2._clearConnectTimeout();
|
|
4046
4767
|
self2._debug(transport.subName(), "transport closed");
|
|
4047
4768
|
self2._transportClosed = true;
|
|
4048
4769
|
self2._transportIsOpen = false;
|
|
@@ -4079,38 +4800,76 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4079
4800
|
} else {
|
|
4080
4801
|
self2._transportWasOpen = true;
|
|
4081
4802
|
}
|
|
4803
|
+
const teardown = function() {
|
|
4804
|
+
self2._reconnecting = false;
|
|
4805
|
+
self2._disconnect(code, reason, needReconnect);
|
|
4806
|
+
};
|
|
4082
4807
|
if (self2._isConnecting() && !wasOpen) {
|
|
4083
|
-
self2.
|
|
4808
|
+
self2._abortedTransportId = transportId;
|
|
4809
|
+
self2._emitError({
|
|
4084
4810
|
type: "transport",
|
|
4085
4811
|
error: {
|
|
4086
4812
|
code: errorCodes.transportClosed,
|
|
4087
4813
|
message: "transport closed"
|
|
4088
4814
|
},
|
|
4089
4815
|
transport: transport.name()
|
|
4090
|
-
});
|
|
4816
|
+
}, teardown);
|
|
4817
|
+
} else {
|
|
4818
|
+
teardown();
|
|
4091
4819
|
}
|
|
4092
|
-
self2._reconnecting = false;
|
|
4093
|
-
self2._disconnect(code, reason, needReconnect);
|
|
4094
4820
|
},
|
|
4095
4821
|
onMessage: function(data) {
|
|
4096
|
-
self2.
|
|
4822
|
+
if (self2._transportId != transportId) {
|
|
4823
|
+
self2._debug("message from non-actual transport");
|
|
4824
|
+
return;
|
|
4825
|
+
}
|
|
4826
|
+
self2._dataReceived(data, transportId);
|
|
4097
4827
|
}
|
|
4098
|
-
}
|
|
4828
|
+
};
|
|
4829
|
+
const onInitializeError = function(e) {
|
|
4830
|
+
self2._debug("error initializing transport", e);
|
|
4831
|
+
failTransport(e instanceof Error ? e.message : String(e));
|
|
4832
|
+
};
|
|
4833
|
+
let initialized;
|
|
4834
|
+
try {
|
|
4835
|
+
initialized = transport.initialize(this._codecName(), callbacks, this._codec.encodeCommands(initialCommands));
|
|
4836
|
+
} catch (e) {
|
|
4837
|
+
onInitializeError(e);
|
|
4838
|
+
return;
|
|
4839
|
+
}
|
|
4840
|
+
if (initialized && isFunction(initialized.then)) {
|
|
4841
|
+
initialized.then(null, onInitializeError);
|
|
4842
|
+
}
|
|
4099
4843
|
self2.emit("__centrifuge_debug:transport_initialized", {});
|
|
4100
4844
|
}
|
|
4101
4845
|
_sendConnect(skipSending) {
|
|
4102
4846
|
const connectCommand = this._constructConnectCommand();
|
|
4103
4847
|
const self2 = this;
|
|
4848
|
+
const transportId = this._transportId;
|
|
4849
|
+
const token = this._token;
|
|
4104
4850
|
this._call(connectCommand, skipSending).then((resolveCtx) => {
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4851
|
+
try {
|
|
4852
|
+
self2._connectResponse(resolveCtx.reply.connect);
|
|
4853
|
+
} catch (err) {
|
|
4854
|
+
self2._dispatchFailed(err);
|
|
4855
|
+
} finally {
|
|
4856
|
+
if (resolveCtx.next) {
|
|
4857
|
+
resolveCtx.next();
|
|
4858
|
+
}
|
|
4109
4859
|
}
|
|
4110
4860
|
}, (rejectCtx) => {
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4861
|
+
try {
|
|
4862
|
+
if (transportId === self2._abortedTransportId) {
|
|
4863
|
+
self2._debug("connect command of a transport aborted by the client rejected");
|
|
4864
|
+
} else {
|
|
4865
|
+
self2._connectError(rejectCtx.error, self2._transportId !== transportId, rejectCtx.next !== void 0, token);
|
|
4866
|
+
}
|
|
4867
|
+
} catch (err) {
|
|
4868
|
+
self2._rejectionFailed(err, rejectCtx);
|
|
4869
|
+
} finally {
|
|
4870
|
+
if (rejectCtx.next) {
|
|
4871
|
+
rejectCtx.next();
|
|
4872
|
+
}
|
|
4114
4873
|
}
|
|
4115
4874
|
});
|
|
4116
4875
|
return connectCommand;
|
|
@@ -4130,17 +4889,21 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4130
4889
|
return;
|
|
4131
4890
|
}
|
|
4132
4891
|
this._reconnecting = true;
|
|
4892
|
+
const disconnects = this._disconnects;
|
|
4133
4893
|
const emptyToken = this._token === "";
|
|
4134
4894
|
const needTokenRefresh = this._refreshRequired || emptyToken && this._config.getToken !== null;
|
|
4895
|
+
let tokenResolved = false;
|
|
4896
|
+
let dataResolved = false;
|
|
4135
4897
|
if (!needTokenRefresh) {
|
|
4136
4898
|
if (this._config.getData) {
|
|
4137
|
-
this.
|
|
4138
|
-
|
|
4899
|
+
this._getData().then((data) => {
|
|
4900
|
+
dataResolved = true;
|
|
4901
|
+
if (this._attemptAborted(disconnects)) {
|
|
4139
4902
|
return;
|
|
4140
4903
|
}
|
|
4141
4904
|
this._data = data;
|
|
4142
4905
|
this._initializeTransport();
|
|
4143
|
-
}).catch((e) => this._handleGetDataError(e));
|
|
4906
|
+
}).catch((e) => this._handleGetDataError(e, disconnects, dataResolved));
|
|
4144
4907
|
} else {
|
|
4145
4908
|
this._initializeTransport();
|
|
4146
4909
|
}
|
|
@@ -4148,7 +4911,8 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4148
4911
|
}
|
|
4149
4912
|
const self2 = this;
|
|
4150
4913
|
this._getToken().then(function(token) {
|
|
4151
|
-
|
|
4914
|
+
tokenResolved = true;
|
|
4915
|
+
if (self2._attemptAborted(disconnects)) {
|
|
4152
4916
|
return;
|
|
4153
4917
|
}
|
|
4154
4918
|
if (token == null || token == void 0) {
|
|
@@ -4158,73 +4922,116 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4158
4922
|
self2._token = token;
|
|
4159
4923
|
self2._debug("connection token refreshed");
|
|
4160
4924
|
if (self2._config.getData) {
|
|
4161
|
-
self2.
|
|
4162
|
-
|
|
4925
|
+
self2._getData().then(function(data) {
|
|
4926
|
+
dataResolved = true;
|
|
4927
|
+
if (self2._attemptAborted(disconnects)) {
|
|
4163
4928
|
return;
|
|
4164
4929
|
}
|
|
4165
4930
|
self2._data = data;
|
|
4166
4931
|
self2._initializeTransport();
|
|
4167
|
-
}).catch((e) => self2._handleGetDataError(e));
|
|
4932
|
+
}).catch((e) => self2._handleGetDataError(e, disconnects, dataResolved));
|
|
4168
4933
|
} else {
|
|
4169
4934
|
self2._initializeTransport();
|
|
4170
4935
|
}
|
|
4171
4936
|
}).catch(function(e) {
|
|
4172
|
-
if (
|
|
4937
|
+
if (self2._attemptAborted(disconnects)) {
|
|
4938
|
+
if (tokenResolved) {
|
|
4939
|
+
self2._reportDispatchError(e);
|
|
4940
|
+
}
|
|
4173
4941
|
return;
|
|
4174
4942
|
}
|
|
4175
4943
|
if (e instanceof UnauthorizedError) {
|
|
4176
4944
|
self2._failUnauthorized();
|
|
4177
4945
|
return;
|
|
4178
4946
|
}
|
|
4179
|
-
self2.
|
|
4947
|
+
self2._emitError({
|
|
4180
4948
|
"type": "connectToken",
|
|
4181
4949
|
"error": {
|
|
4182
4950
|
code: errorCodes.clientConnectToken,
|
|
4183
4951
|
message: e !== void 0 ? e.toString() : ""
|
|
4184
4952
|
}
|
|
4953
|
+
}, () => {
|
|
4954
|
+
const delay = self2._getReconnectDelay();
|
|
4955
|
+
self2._debug("error on getting connection token, reconnect after " + delay + " milliseconds", e);
|
|
4956
|
+
self2._reconnecting = false;
|
|
4957
|
+
self2._reconnectTimeout = setTimeout(() => {
|
|
4958
|
+
self2._startReconnecting();
|
|
4959
|
+
}, delay);
|
|
4185
4960
|
});
|
|
4186
|
-
const delay = self2._getReconnectDelay();
|
|
4187
|
-
self2._debug("error on getting connection token, reconnect after " + delay + " milliseconds", e);
|
|
4188
|
-
self2._reconnecting = false;
|
|
4189
|
-
self2._reconnectTimeout = setTimeout(() => {
|
|
4190
|
-
self2._startReconnecting();
|
|
4191
|
-
}, delay);
|
|
4192
4961
|
});
|
|
4193
4962
|
}
|
|
4194
|
-
|
|
4963
|
+
// Emits an error event, then runs the continuation: not when a handler disconnected
|
|
4964
|
+
// the client (or threw, which disconnects it), maybe to connect again, whose new
|
|
4965
|
+
// attempt the continuation must not touch.
|
|
4966
|
+
_emitError(ctx, continuation) {
|
|
4967
|
+
const disconnects = this._disconnects;
|
|
4968
|
+
try {
|
|
4969
|
+
this.emit("error", ctx);
|
|
4970
|
+
} finally {
|
|
4971
|
+
if (this._disconnects === disconnects) {
|
|
4972
|
+
continuation();
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
_attemptAborted(disconnects) {
|
|
4977
|
+
return !this._isConnecting() || this._disconnects !== disconnects;
|
|
4978
|
+
}
|
|
4979
|
+
// resolved: the data promise resolved, so the error was thrown by its continuation.
|
|
4980
|
+
_handleGetDataError(e, disconnects, resolved) {
|
|
4981
|
+
if (this._attemptAborted(disconnects)) {
|
|
4982
|
+
if (resolved) {
|
|
4983
|
+
this._reportDispatchError(e);
|
|
4984
|
+
}
|
|
4985
|
+
return;
|
|
4986
|
+
}
|
|
4195
4987
|
if (e instanceof UnauthorizedError) {
|
|
4196
4988
|
this._failUnauthorized();
|
|
4197
4989
|
return;
|
|
4198
4990
|
}
|
|
4199
|
-
this.
|
|
4991
|
+
this._emitError({
|
|
4200
4992
|
type: "connectData",
|
|
4201
4993
|
error: {
|
|
4202
4994
|
code: errorCodes.badConfiguration,
|
|
4203
4995
|
message: (e === null || e === void 0 ? void 0 : e.toString()) || ""
|
|
4204
4996
|
}
|
|
4997
|
+
}, () => {
|
|
4998
|
+
const delay = this._getReconnectDelay();
|
|
4999
|
+
this._debug("error on getting connect data, reconnect after " + delay + " milliseconds", e);
|
|
5000
|
+
this._reconnecting = false;
|
|
5001
|
+
this._reconnectTimeout = setTimeout(() => {
|
|
5002
|
+
this._startReconnecting();
|
|
5003
|
+
}, delay);
|
|
4205
5004
|
});
|
|
4206
|
-
const delay = this._getReconnectDelay();
|
|
4207
|
-
this._debug("error on getting connect data, reconnect after " + delay + " milliseconds", e);
|
|
4208
|
-
this._reconnecting = false;
|
|
4209
|
-
this._reconnectTimeout = setTimeout(() => {
|
|
4210
|
-
this._startReconnecting();
|
|
4211
|
-
}, delay);
|
|
4212
5005
|
}
|
|
4213
|
-
|
|
5006
|
+
// stale: the error belongs to a transport that was already closed. It is still
|
|
5007
|
+
// reported, but must not tear down the current connection attempt.
|
|
5008
|
+
// replied: the server returned the error in a reply, so the transport works.
|
|
5009
|
+
// token: the token the connect command carried.
|
|
5010
|
+
_connectError(err, stale, replied, token) {
|
|
4214
5011
|
if (this.state !== State.Connecting) {
|
|
4215
5012
|
return;
|
|
4216
5013
|
}
|
|
4217
|
-
if (err.code === 109) {
|
|
5014
|
+
if (err.code === 109 && token === this._token) {
|
|
4218
5015
|
this._refreshRequired = true;
|
|
4219
5016
|
}
|
|
4220
5017
|
if (err.code < 100 || err.temporary === true || err.code === 109) {
|
|
4221
|
-
this.
|
|
5018
|
+
this._emitError({
|
|
4222
5019
|
"type": "connect",
|
|
4223
5020
|
"error": err
|
|
5021
|
+
}, () => {
|
|
5022
|
+
if (stale) {
|
|
5023
|
+
return;
|
|
5024
|
+
}
|
|
5025
|
+
if (replied) {
|
|
5026
|
+
this._transportWasOpen = true;
|
|
5027
|
+
}
|
|
5028
|
+
if (!replied && this._emulation && !this._transportWasOpen) {
|
|
5029
|
+
this._advanceTransportIndex();
|
|
5030
|
+
}
|
|
5031
|
+
this._debug("closing transport due to connect error");
|
|
5032
|
+
this._disconnect(err.code, err.message, true);
|
|
4224
5033
|
});
|
|
4225
|
-
|
|
4226
|
-
this._disconnect(err.code, err.message, true);
|
|
4227
|
-
} else {
|
|
5034
|
+
} else if (!stale) {
|
|
4228
5035
|
this._disconnect(err.code, err.message, false);
|
|
4229
5036
|
}
|
|
4230
5037
|
}
|
|
@@ -4236,10 +5043,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4236
5043
|
if (this._emulation && !this._transportWasOpen && !this._triedAllTransports) {
|
|
4237
5044
|
isInitialHandshake = true;
|
|
4238
5045
|
}
|
|
4239
|
-
|
|
4240
|
-
if (isInitialHandshake) {
|
|
4241
|
-
delay = 0;
|
|
4242
|
-
}
|
|
5046
|
+
const delay = isInitialHandshake ? 0 : this._getReconnectDelay();
|
|
4243
5047
|
this._debug("reconnect after " + delay + " milliseconds");
|
|
4244
5048
|
this._clearReconnectTimeout();
|
|
4245
5049
|
this._reconnectTimeout = setTimeout(() => {
|
|
@@ -4271,8 +5075,10 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4271
5075
|
const sub = {
|
|
4272
5076
|
"recover": true
|
|
4273
5077
|
};
|
|
4274
|
-
|
|
4275
|
-
|
|
5078
|
+
const pending = this._pendingServerSubOffsets[channel];
|
|
5079
|
+
const offset = pending !== void 0 && pending !== null ? pending : this._serverSubs[channel].offset;
|
|
5080
|
+
if (offset) {
|
|
5081
|
+
sub["offset"] = offset;
|
|
4276
5082
|
}
|
|
4277
5083
|
if (this._serverSubs[channel].epoch) {
|
|
4278
5084
|
sub["epoch"] = this._serverSubs[channel].epoch;
|
|
@@ -4314,10 +5120,12 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4314
5120
|
return Promise.resolve();
|
|
4315
5121
|
}
|
|
4316
5122
|
return new Promise((res, rej) => {
|
|
4317
|
-
const
|
|
5123
|
+
const id = this._nextPromiseId();
|
|
5124
|
+
const timeout = setTimeout(() => {
|
|
5125
|
+
delete this._promises[id];
|
|
4318
5126
|
rej({ code: errorCodes.timeout, message: "timeout" });
|
|
4319
5127
|
}, this._config.timeout);
|
|
4320
|
-
this._promises[
|
|
5128
|
+
this._promises[id] = {
|
|
4321
5129
|
timeout,
|
|
4322
5130
|
resolve: res,
|
|
4323
5131
|
reject: rej
|
|
@@ -4328,9 +5136,13 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4328
5136
|
return new Promise((resolve, reject) => {
|
|
4329
5137
|
this._call(cmd, false).then((resolveCtx) => {
|
|
4330
5138
|
var _a;
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
5139
|
+
try {
|
|
5140
|
+
resolve(resultCB(resolveCtx.reply));
|
|
5141
|
+
} catch (e) {
|
|
5142
|
+
reject(e);
|
|
5143
|
+
} finally {
|
|
5144
|
+
(_a = resolveCtx.next) === null || _a === void 0 ? void 0 : _a.call(resolveCtx);
|
|
5145
|
+
}
|
|
4334
5146
|
}, (rejectCtx) => {
|
|
4335
5147
|
var _a;
|
|
4336
5148
|
reject(rejectCtx.error);
|
|
@@ -4338,25 +5150,36 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4338
5150
|
});
|
|
4339
5151
|
});
|
|
4340
5152
|
}
|
|
4341
|
-
_dataReceived(data) {
|
|
4342
|
-
|
|
5153
|
+
_dataReceived(data, transportId = this._transportId) {
|
|
5154
|
+
let replies;
|
|
5155
|
+
try {
|
|
5156
|
+
replies = this._codec.decodeReplies(data);
|
|
5157
|
+
} catch (e) {
|
|
5158
|
+
this._debug("error decoding received data", e);
|
|
5159
|
+
if (this._emulation && !this._transportWasOpen) {
|
|
5160
|
+
this._advanceTransportIndex();
|
|
5161
|
+
}
|
|
5162
|
+
this._disconnect(connectingCodes.transportClosed, "transport closed", true);
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5165
|
+
if (this._serverPing > 0 && replies.length > 0) {
|
|
4343
5166
|
this._waitServerPing();
|
|
4344
5167
|
}
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
this._dispatchPromise = new Promise((resolve) => {
|
|
4349
|
-
finishDispatch = resolve;
|
|
4350
|
-
});
|
|
4351
|
-
this._dispatchSynchronized(replies, finishDispatch);
|
|
4352
|
-
});
|
|
5168
|
+
this._dispatchPromise = this._dispatchPromise.then(() => new Promise((resolve) => {
|
|
5169
|
+
this._dispatchSynchronized(replies, resolve, transportId);
|
|
5170
|
+
}));
|
|
4353
5171
|
}
|
|
4354
|
-
_dispatchSynchronized(replies, finishDispatch) {
|
|
5172
|
+
_dispatchSynchronized(replies, finishDispatch, transportId) {
|
|
4355
5173
|
let p = Promise.resolve();
|
|
4356
5174
|
for (const i in replies) {
|
|
4357
5175
|
if (replies.hasOwnProperty(i)) {
|
|
4358
5176
|
p = p.then(() => {
|
|
5177
|
+
if (this._transportId !== transportId) {
|
|
5178
|
+
return;
|
|
5179
|
+
}
|
|
4359
5180
|
return this._dispatchReply(replies[i]);
|
|
5181
|
+
}).catch((err) => {
|
|
5182
|
+
this._dispatchFailed(err);
|
|
4360
5183
|
});
|
|
4361
5184
|
}
|
|
4362
5185
|
}
|
|
@@ -4364,6 +5187,52 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4364
5187
|
finishDispatch();
|
|
4365
5188
|
});
|
|
4366
5189
|
}
|
|
5190
|
+
/** @internal Every event reaches the application's handlers through here. */
|
|
5191
|
+
emit(event, ...args) {
|
|
5192
|
+
try {
|
|
5193
|
+
return EventEmitter.prototype.emit.call(this, event, ...args);
|
|
5194
|
+
} catch (e) {
|
|
5195
|
+
this._handlerFailed(e);
|
|
5196
|
+
return true;
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
// An exception escaping an application event handler. The SDK can't know what the
|
|
5200
|
+
// handler left undone, so it fails loudly instead of going on as if nothing
|
|
5201
|
+
// happened: the client disconnects without reconnecting, and the exception is
|
|
5202
|
+
// re-raised as an unhandled rejection. The SDK's own work continues as if the
|
|
5203
|
+
// handler had returned, as for a handler calling disconnect(), so no state change
|
|
5204
|
+
// is left half done.
|
|
5205
|
+
/** @internal */
|
|
5206
|
+
_handlerFailed(err) {
|
|
5207
|
+
this._handlerFailures++;
|
|
5208
|
+
this._reportDispatchError(err);
|
|
5209
|
+
this._disconnect(disconnectedCodes.badProtocol, `exception in event handler: ${errorMessage(err)}`, false);
|
|
5210
|
+
}
|
|
5211
|
+
// An exception while handling a reply, other than one of an event handler (see
|
|
5212
|
+
// _handlerFailed), may have left the client's state inconsistent. The client stops
|
|
5213
|
+
// in a state the application can see: it disconnects without reconnecting, so no
|
|
5214
|
+
// later reply is processed.
|
|
5215
|
+
_dispatchFailed(err) {
|
|
5216
|
+
this._reportDispatchError(err);
|
|
5217
|
+
this._disconnect(disconnectedCodes.badProtocol, `exception during message handling: ${errorMessage(err)}`, false);
|
|
5218
|
+
}
|
|
5219
|
+
// The exception is surfaced as an unhandled rejection, as before.
|
|
5220
|
+
_reportDispatchError(err) {
|
|
5221
|
+
this._debug("error dispatching reply", err);
|
|
5222
|
+
Promise.reject(err);
|
|
5223
|
+
}
|
|
5224
|
+
// An exception, other than one of an event handler, while handling a rejected
|
|
5225
|
+
// command. A rejection with next() carries a reply, and is handled as any reply
|
|
5226
|
+
// (see _dispatchFailed). A timeout or a teardown doesn't: stopping the client would
|
|
5227
|
+
// cancel the reconnect or retry the handling scheduled, so the exception is only
|
|
5228
|
+
// reported.
|
|
5229
|
+
_rejectionFailed(err, rejectCtx) {
|
|
5230
|
+
if (rejectCtx.next !== void 0) {
|
|
5231
|
+
this._dispatchFailed(err);
|
|
5232
|
+
} else {
|
|
5233
|
+
this._reportDispatchError(err);
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
4367
5236
|
_dispatchReply(reply) {
|
|
4368
5237
|
let next;
|
|
4369
5238
|
const p = new Promise((resolve) => {
|
|
@@ -4397,16 +5266,25 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4397
5266
|
}
|
|
4398
5267
|
_startConnecting() {
|
|
4399
5268
|
this._debug("start connecting");
|
|
4400
|
-
|
|
4401
|
-
|
|
5269
|
+
const transition = this.state !== State.Connecting ? this._stateTransitions + 1 : 0;
|
|
5270
|
+
try {
|
|
5271
|
+
this._setState(State.Connecting);
|
|
5272
|
+
if (transition && !this._transitionSuperseded(transition)) {
|
|
5273
|
+
this.emit("connecting", { code: connectingCodes.connectCalled, reason: "connect called" });
|
|
5274
|
+
}
|
|
5275
|
+
} finally {
|
|
5276
|
+
if (!this._transitionSuperseded(transition)) {
|
|
5277
|
+
this._client = null;
|
|
5278
|
+
this._startReconnecting();
|
|
5279
|
+
}
|
|
4402
5280
|
}
|
|
4403
|
-
this._client = null;
|
|
4404
|
-
this._startReconnecting();
|
|
4405
5281
|
}
|
|
4406
5282
|
_disconnect(code, reason, reconnect) {
|
|
4407
5283
|
if (code === disconnectedCodes.stateInvalidated) {
|
|
4408
|
-
this.
|
|
4409
|
-
|
|
5284
|
+
if (this._config.getToken !== null) {
|
|
5285
|
+
this._token = "";
|
|
5286
|
+
this._refreshRequired = true;
|
|
5287
|
+
}
|
|
4410
5288
|
for (const channel in this._subs) {
|
|
4411
5289
|
if (this._subs.hasOwnProperty(channel)) {
|
|
4412
5290
|
this._subs[channel]._invalidateState();
|
|
@@ -4420,47 +5298,70 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4420
5298
|
}
|
|
4421
5299
|
}
|
|
4422
5300
|
if (this._isDisconnected()) {
|
|
5301
|
+
if (!reconnect) {
|
|
5302
|
+
this._rejectPromises({ code: errorCodes.clientDisconnected, message: "disconnected" });
|
|
5303
|
+
}
|
|
4423
5304
|
return;
|
|
4424
5305
|
}
|
|
4425
|
-
this._transportIsOpen = false;
|
|
4426
5306
|
const previousState = this.state;
|
|
5307
|
+
const newState = reconnect ? State.Connecting : State.Disconnected;
|
|
5308
|
+
this._transportIsOpen = false;
|
|
4427
5309
|
this._reconnecting = false;
|
|
4428
|
-
const ctx = {
|
|
4429
|
-
code,
|
|
4430
|
-
reason
|
|
4431
|
-
};
|
|
4432
|
-
let needEvent;
|
|
4433
|
-
if (reconnect) {
|
|
4434
|
-
needEvent = this._setState(State.Connecting);
|
|
4435
|
-
} else {
|
|
4436
|
-
needEvent = this._setState(State.Disconnected);
|
|
4437
|
-
this._rejectPromises({ code: errorCodes.clientDisconnected, message: "disconnected" });
|
|
4438
|
-
}
|
|
4439
|
-
this._clearOutgoingRequests();
|
|
4440
5310
|
if (previousState === State.Connecting) {
|
|
4441
5311
|
this._clearReconnectTimeout();
|
|
4442
5312
|
}
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
if (needEvent) {
|
|
4447
|
-
if (this._isConnecting()) {
|
|
4448
|
-
this.emit("connecting", ctx);
|
|
4449
|
-
} else {
|
|
4450
|
-
this.emit("disconnected", ctx);
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
5313
|
+
this._clearConnectTimeout();
|
|
5314
|
+
this._nextTransportId();
|
|
5315
|
+
this._disconnects++;
|
|
4453
5316
|
if (this._transport) {
|
|
4454
5317
|
this._debug("closing existing transport");
|
|
4455
5318
|
const transport = this._transport;
|
|
4456
5319
|
this._transport = null;
|
|
4457
|
-
|
|
5320
|
+
try {
|
|
5321
|
+
transport.close();
|
|
5322
|
+
} catch (e) {
|
|
5323
|
+
this._debug("error closing transport", e);
|
|
5324
|
+
}
|
|
4458
5325
|
this._transportClosed = true;
|
|
4459
|
-
this._nextTransportId();
|
|
4460
5326
|
} else {
|
|
4461
5327
|
this._debug("no transport to close");
|
|
4462
5328
|
}
|
|
4463
|
-
this.
|
|
5329
|
+
this._reconnectAttemptsResetPending = false;
|
|
5330
|
+
this._commands = [];
|
|
5331
|
+
this._clearOutgoingRequests();
|
|
5332
|
+
if (!reconnect) {
|
|
5333
|
+
this._rejectPromises({ code: errorCodes.clientDisconnected, message: "disconnected" });
|
|
5334
|
+
this._deviceWentOffline = false;
|
|
5335
|
+
this._pageCached = false;
|
|
5336
|
+
}
|
|
5337
|
+
const ctx = {
|
|
5338
|
+
code,
|
|
5339
|
+
reason
|
|
5340
|
+
};
|
|
5341
|
+
const transition = this.state !== newState ? this._stateTransitions + 1 : 0;
|
|
5342
|
+
try {
|
|
5343
|
+
try {
|
|
5344
|
+
this._setState(newState);
|
|
5345
|
+
} finally {
|
|
5346
|
+
if (!reconnect && !this._transitionSuperseded(transition)) {
|
|
5347
|
+
this._rejectPromises({ code: errorCodes.clientDisconnected, message: "disconnected" });
|
|
5348
|
+
}
|
|
5349
|
+
if (previousState === State.Connected) {
|
|
5350
|
+
this._clearConnectedState();
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
if (transition && !this._transitionSuperseded(transition)) {
|
|
5354
|
+
if (reconnect) {
|
|
5355
|
+
this.emit("connecting", ctx);
|
|
5356
|
+
} else {
|
|
5357
|
+
this.emit("disconnected", ctx);
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
} finally {
|
|
5361
|
+
if (reconnect && !this._transitionSuperseded(transition)) {
|
|
5362
|
+
this._scheduleReconnect();
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
4464
5365
|
}
|
|
4465
5366
|
_failUnauthorized() {
|
|
4466
5367
|
this._disconnect(disconnectedCodes.unauthorized, "unauthorized", false);
|
|
@@ -4468,21 +5369,39 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4468
5369
|
_getToken() {
|
|
4469
5370
|
this._debug("get connection token");
|
|
4470
5371
|
if (!this._config.getToken) {
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
5372
|
+
try {
|
|
5373
|
+
this.emit("error", {
|
|
5374
|
+
type: "configuration",
|
|
5375
|
+
error: {
|
|
5376
|
+
code: errorCodes.badConfiguration,
|
|
5377
|
+
message: "token expired but no getToken function set in the configuration"
|
|
5378
|
+
}
|
|
5379
|
+
});
|
|
5380
|
+
} catch (e) {
|
|
5381
|
+
this._reportDispatchError(e);
|
|
5382
|
+
}
|
|
4478
5383
|
return Promise.reject(new UnauthorizedError(""));
|
|
4479
5384
|
}
|
|
4480
|
-
|
|
5385
|
+
try {
|
|
5386
|
+
return Promise.resolve(this._config.getToken({}));
|
|
5387
|
+
} catch (e) {
|
|
5388
|
+
return Promise.reject(e);
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
// See _getToken.
|
|
5392
|
+
_getData() {
|
|
5393
|
+
try {
|
|
5394
|
+
return Promise.resolve(this._config.getData());
|
|
5395
|
+
} catch (e) {
|
|
5396
|
+
return Promise.reject(e);
|
|
5397
|
+
}
|
|
4481
5398
|
}
|
|
4482
5399
|
_refresh() {
|
|
4483
5400
|
const clientId = this._client;
|
|
4484
5401
|
const self2 = this;
|
|
5402
|
+
let tokenResolved = false;
|
|
4485
5403
|
this._getToken().then(function(token) {
|
|
5404
|
+
tokenResolved = true;
|
|
4486
5405
|
if (clientId !== self2._client) {
|
|
4487
5406
|
return;
|
|
4488
5407
|
}
|
|
@@ -4499,42 +5418,58 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4499
5418
|
refresh: { token: self2._token }
|
|
4500
5419
|
};
|
|
4501
5420
|
self2._call(cmd, false).then((resolveCtx) => {
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
5421
|
+
try {
|
|
5422
|
+
self2._refreshResponse(resolveCtx.reply.refresh);
|
|
5423
|
+
} catch (err) {
|
|
5424
|
+
self2._dispatchFailed(err);
|
|
5425
|
+
} finally {
|
|
5426
|
+
if (resolveCtx.next) {
|
|
5427
|
+
resolveCtx.next();
|
|
5428
|
+
}
|
|
4506
5429
|
}
|
|
4507
5430
|
}, (rejectCtx) => {
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
5431
|
+
try {
|
|
5432
|
+
if (clientId === self2._client) {
|
|
5433
|
+
self2._refreshError(rejectCtx.error);
|
|
5434
|
+
}
|
|
5435
|
+
} catch (err) {
|
|
5436
|
+
self2._rejectionFailed(err, rejectCtx);
|
|
5437
|
+
} finally {
|
|
5438
|
+
if (rejectCtx.next) {
|
|
5439
|
+
rejectCtx.next();
|
|
5440
|
+
}
|
|
4511
5441
|
}
|
|
4512
5442
|
});
|
|
4513
5443
|
}).catch(function(e) {
|
|
4514
|
-
if (!self2._isConnected()) {
|
|
5444
|
+
if (clientId !== self2._client || !self2._isConnected()) {
|
|
5445
|
+
if (tokenResolved) {
|
|
5446
|
+
self2._reportDispatchError(e);
|
|
5447
|
+
}
|
|
4515
5448
|
return;
|
|
4516
5449
|
}
|
|
4517
5450
|
if (e instanceof UnauthorizedError) {
|
|
4518
5451
|
self2._failUnauthorized();
|
|
4519
5452
|
return;
|
|
4520
5453
|
}
|
|
4521
|
-
self2.
|
|
5454
|
+
self2._emitError({
|
|
4522
5455
|
type: "refreshToken",
|
|
4523
5456
|
error: {
|
|
4524
5457
|
code: errorCodes.clientRefreshToken,
|
|
4525
5458
|
message: e !== void 0 ? e.toString() : ""
|
|
4526
5459
|
}
|
|
5460
|
+
}, () => {
|
|
5461
|
+
self2._refreshTimeout = setTimeout(() => self2._refresh(), self2._getRefreshRetryDelay());
|
|
4527
5462
|
});
|
|
4528
|
-
self2._refreshTimeout = setTimeout(() => self2._refresh(), self2._getRefreshRetryDelay());
|
|
4529
5463
|
});
|
|
4530
5464
|
}
|
|
4531
5465
|
_refreshError(err) {
|
|
4532
5466
|
if (err.code < 100 || err.temporary === true) {
|
|
4533
|
-
this.
|
|
5467
|
+
this._emitError({
|
|
4534
5468
|
type: "refresh",
|
|
4535
5469
|
error: err
|
|
5470
|
+
}, () => {
|
|
5471
|
+
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
4536
5472
|
});
|
|
4537
|
-
this._refreshTimeout = setTimeout(() => this._refresh(), this._getRefreshRetryDelay());
|
|
4538
5473
|
} else {
|
|
4539
5474
|
this._disconnect(err.code, err.message, false);
|
|
4540
5475
|
}
|
|
@@ -4548,15 +5483,33 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4548
5483
|
this._refreshTimeout = null;
|
|
4549
5484
|
}
|
|
4550
5485
|
if (result.expires) {
|
|
4551
|
-
|
|
5486
|
+
if (result.client) {
|
|
5487
|
+
this._client = result.client;
|
|
5488
|
+
}
|
|
4552
5489
|
this._refreshTimeout = setTimeout(() => this._refresh(), ttlMilliseconds(result.ttl));
|
|
4553
5490
|
}
|
|
4554
5491
|
}
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
5492
|
+
// A new subscription of the channel waits for the unsubscribe of a removed one, and
|
|
5493
|
+
// of those removed before it still unsubscribing (see _setSubscribing of a
|
|
5494
|
+
// subscription): a subscription removed without sending an unsubscribe doesn't
|
|
5495
|
+
// drop one still pending.
|
|
5496
|
+
_recordRemovedUnsubscribe(sub) {
|
|
5497
|
+
const channel = sub.channel;
|
|
5498
|
+
const previous = this._removedUnsubscribes[channel];
|
|
5499
|
+
const own = sub._unsubPromise;
|
|
5500
|
+
const unsubscribed = previous ? Promise.all([previous, own]).then(() => void 0) : own;
|
|
5501
|
+
this._removedUnsubscribes[channel] = unsubscribed;
|
|
5502
|
+
unsubscribed.then(() => {
|
|
5503
|
+
if (this._removedUnsubscribes[channel] === unsubscribed) {
|
|
5504
|
+
delete this._removedUnsubscribes[channel];
|
|
5505
|
+
}
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5508
|
+
// Resolves once a subscription of the channel removed from the client has
|
|
5509
|
+
// unsubscribed (see BaseSubscription._setSubscribing).
|
|
5510
|
+
// @ts-ignore – this is used by a subscription in subscription.ts.
|
|
5511
|
+
_removedSubscriptionUnsubscribed(channel) {
|
|
5512
|
+
return this._removedUnsubscribes[channel] || Promise.resolve();
|
|
4560
5513
|
}
|
|
4561
5514
|
_unsubscribe(sub) {
|
|
4562
5515
|
if (!this._transportIsOpen) {
|
|
@@ -4567,6 +5520,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4567
5520
|
};
|
|
4568
5521
|
const cmd = { unsubscribe: req };
|
|
4569
5522
|
const self2 = this;
|
|
5523
|
+
const transportId = this._transportId;
|
|
4570
5524
|
const unsubscribePromise = new Promise((resolve, _) => {
|
|
4571
5525
|
this._call(cmd, false).then((resolveCtx) => {
|
|
4572
5526
|
resolve();
|
|
@@ -4578,7 +5532,15 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4578
5532
|
if (rejectCtx.next) {
|
|
4579
5533
|
rejectCtx.next();
|
|
4580
5534
|
}
|
|
4581
|
-
self2.
|
|
5535
|
+
if (self2._transportId !== transportId) {
|
|
5536
|
+
self2._debug("unsubscribe command of a closed transport rejected");
|
|
5537
|
+
return;
|
|
5538
|
+
}
|
|
5539
|
+
try {
|
|
5540
|
+
self2._disconnect(connectingCodes.unsubscribeError, "unsubscribe error", true);
|
|
5541
|
+
} catch (err) {
|
|
5542
|
+
self2._rejectionFailed(err, rejectCtx);
|
|
5543
|
+
}
|
|
4582
5544
|
});
|
|
4583
5545
|
});
|
|
4584
5546
|
return unsubscribePromise;
|
|
@@ -4604,6 +5566,17 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4604
5566
|
_isServerSub(channel) {
|
|
4605
5567
|
return this._serverSubs[channel] !== void 0;
|
|
4606
5568
|
}
|
|
5569
|
+
// The client-side subscription a push is for. The server allows one subscription
|
|
5570
|
+
// per channel: while a server-side subscription has the channel, its pushes
|
|
5571
|
+
// belong to it unless the client-side subscription is subscribed, e.g. when the
|
|
5572
|
+
// app kept an unsubscribed subscription object of that channel.
|
|
5573
|
+
_getSubForPush(channel, id) {
|
|
5574
|
+
const sub = this._getSub(channel, id);
|
|
5575
|
+
if (sub && channel && this._isServerSub(channel) && !sub._isSubscribed()) {
|
|
5576
|
+
return null;
|
|
5577
|
+
}
|
|
5578
|
+
return sub;
|
|
5579
|
+
}
|
|
4607
5580
|
_sendSubscribeCommands() {
|
|
4608
5581
|
const commands = [];
|
|
4609
5582
|
for (const channel in this._subs) {
|
|
@@ -4614,6 +5587,9 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4614
5587
|
if (sub._inflight === true) {
|
|
4615
5588
|
continue;
|
|
4616
5589
|
}
|
|
5590
|
+
if (sub._resubscribeTimeout !== null) {
|
|
5591
|
+
continue;
|
|
5592
|
+
}
|
|
4617
5593
|
if (sub.state === SubscriptionState.Subscribing) {
|
|
4618
5594
|
const cmd = sub._subscribe();
|
|
4619
5595
|
if (cmd) {
|
|
@@ -4626,13 +5602,22 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4626
5602
|
_connectResponse(result) {
|
|
4627
5603
|
this._transportIsOpen = true;
|
|
4628
5604
|
this._transportWasOpen = true;
|
|
4629
|
-
this.
|
|
5605
|
+
if (this._transport !== null && this._transport.emulation()) {
|
|
5606
|
+
this._reconnectAttemptsResetPending = true;
|
|
5607
|
+
} else {
|
|
5608
|
+
this._reconnectAttempts = 0;
|
|
5609
|
+
}
|
|
4630
5610
|
this._refreshRequired = false;
|
|
5611
|
+
this._deviceWentOffline = false;
|
|
5612
|
+
this._pageCached = false;
|
|
4631
5613
|
if (this._isConnected()) {
|
|
4632
5614
|
return;
|
|
4633
5615
|
}
|
|
4634
5616
|
this._client = result.client;
|
|
4635
|
-
this._setState(State.Connected);
|
|
5617
|
+
const transition = this._setState(State.Connected);
|
|
5618
|
+
if (this._transitionSuperseded(transition)) {
|
|
5619
|
+
return;
|
|
5620
|
+
}
|
|
4636
5621
|
if (this._refreshTimeout) {
|
|
4637
5622
|
clearTimeout(this._refreshTimeout);
|
|
4638
5623
|
}
|
|
@@ -4652,8 +5637,11 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4652
5637
|
ctx.data = result.data;
|
|
4653
5638
|
}
|
|
4654
5639
|
this.emit("connected", ctx);
|
|
5640
|
+
if (this._transitionSuperseded(transition)) {
|
|
5641
|
+
return;
|
|
5642
|
+
}
|
|
4655
5643
|
this._resolvePromises();
|
|
4656
|
-
this._processServerSubs(result.subs || {});
|
|
5644
|
+
this._processServerSubs(result.subs || {}, transition);
|
|
4657
5645
|
if (result.ping && result.ping > 0) {
|
|
4658
5646
|
this._serverPing = result.ping * 1e3;
|
|
4659
5647
|
this._sendPong = result.pong === true;
|
|
@@ -4662,16 +5650,20 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4662
5650
|
this._serverPing = 0;
|
|
4663
5651
|
}
|
|
4664
5652
|
}
|
|
4665
|
-
_processServerSubs(subs) {
|
|
5653
|
+
_processServerSubs(subs, transition) {
|
|
4666
5654
|
for (const channel in subs) {
|
|
4667
5655
|
if (!subs.hasOwnProperty(channel)) {
|
|
4668
5656
|
continue;
|
|
4669
5657
|
}
|
|
5658
|
+
if (this._transitionSuperseded(transition)) {
|
|
5659
|
+
continue;
|
|
5660
|
+
}
|
|
4670
5661
|
const sub = subs[channel];
|
|
4671
5662
|
this._serverSubs[channel] = {
|
|
4672
|
-
"offset": sub.offset,
|
|
5663
|
+
"offset": toOffset(sub.offset),
|
|
4673
5664
|
"epoch": sub.epoch,
|
|
4674
|
-
"recoverable": sub.recoverable || false
|
|
5665
|
+
"recoverable": sub.recoverable || false,
|
|
5666
|
+
"subscribed": true
|
|
4675
5667
|
};
|
|
4676
5668
|
const subCtx = this._getSubscribeContext(channel, sub);
|
|
4677
5669
|
this.emit("subscribed", subCtx);
|
|
@@ -4682,13 +5674,9 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4682
5674
|
}
|
|
4683
5675
|
const sub = subs[channel];
|
|
4684
5676
|
if (sub.recovered) {
|
|
4685
|
-
const pubs = sub.publications;
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
if (pubs.hasOwnProperty(i)) {
|
|
4689
|
-
this._handlePublication(channel, pubs[i]);
|
|
4690
|
-
}
|
|
4691
|
-
}
|
|
5677
|
+
const pubs = sub.publications || [];
|
|
5678
|
+
for (let i = 0; i < pubs.length && !this._transitionSuperseded(transition); i++) {
|
|
5679
|
+
this._handlePublication(channel, pubs[i]);
|
|
4692
5680
|
}
|
|
4693
5681
|
}
|
|
4694
5682
|
}
|
|
@@ -4714,6 +5702,12 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4714
5702
|
this._reconnectTimeout = null;
|
|
4715
5703
|
}
|
|
4716
5704
|
}
|
|
5705
|
+
_clearConnectTimeout() {
|
|
5706
|
+
if (this._connectTimeout !== null) {
|
|
5707
|
+
clearTimeout(this._connectTimeout);
|
|
5708
|
+
this._connectTimeout = null;
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
4717
5711
|
_clearServerPingTimeout() {
|
|
4718
5712
|
if (this._serverPingTimeout !== null) {
|
|
4719
5713
|
clearTimeout(this._serverPingTimeout);
|
|
@@ -4728,12 +5722,20 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4728
5722
|
return;
|
|
4729
5723
|
}
|
|
4730
5724
|
this._clearServerPingTimeout();
|
|
5725
|
+
const delay = this._serverPing + this._config.maxServerPingDelay;
|
|
5726
|
+
const due = Date.now() + delay;
|
|
4731
5727
|
this._serverPingTimeout = setTimeout(() => {
|
|
4732
5728
|
if (!this._isConnected()) {
|
|
4733
5729
|
return;
|
|
4734
5730
|
}
|
|
4735
|
-
|
|
4736
|
-
|
|
5731
|
+
const late = Date.now() - due >= lateCallTimer;
|
|
5732
|
+
this._serverPingTimeout = setTimeout(() => {
|
|
5733
|
+
if (!this._isConnected()) {
|
|
5734
|
+
return;
|
|
5735
|
+
}
|
|
5736
|
+
this._disconnect(connectingCodes.noPing, "no ping", true);
|
|
5737
|
+
}, late ? lateCallGrace : 0);
|
|
5738
|
+
}, delay);
|
|
4737
5739
|
}
|
|
4738
5740
|
_getSubscribeContext(channel, result) {
|
|
4739
5741
|
const ctx = {
|
|
@@ -4762,7 +5764,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4762
5764
|
}
|
|
4763
5765
|
let offset = 0;
|
|
4764
5766
|
if ("offset" in result) {
|
|
4765
|
-
offset = result.offset;
|
|
5767
|
+
offset = toOffset(result.offset);
|
|
4766
5768
|
}
|
|
4767
5769
|
if (ctx.positioned || ctx.recoverable) {
|
|
4768
5770
|
ctx.streamPosition = {
|
|
@@ -4779,6 +5781,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4779
5781
|
return ctx;
|
|
4780
5782
|
}
|
|
4781
5783
|
_handleReply(reply, next) {
|
|
5784
|
+
this._resetReconnectAttemptsIfPending();
|
|
4782
5785
|
const id = reply.id;
|
|
4783
5786
|
if (!(id in this._callbacks)) {
|
|
4784
5787
|
next();
|
|
@@ -4804,7 +5807,7 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4804
5807
|
}
|
|
4805
5808
|
}
|
|
4806
5809
|
_handleJoin(channel, join, id) {
|
|
4807
|
-
const sub = this.
|
|
5810
|
+
const sub = this._getSubForPush(channel, id);
|
|
4808
5811
|
if (!sub) {
|
|
4809
5812
|
if (channel && this._isServerSub(channel)) {
|
|
4810
5813
|
const ctx = { channel, info: this._getJoinLeaveContext(join.info) };
|
|
@@ -4812,10 +5815,14 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4812
5815
|
}
|
|
4813
5816
|
return;
|
|
4814
5817
|
}
|
|
5818
|
+
if (!sub._isSubscribed()) {
|
|
5819
|
+
this._debug("join for a subscription that is not subscribed", channel);
|
|
5820
|
+
return;
|
|
5821
|
+
}
|
|
4815
5822
|
sub._handleJoin(join);
|
|
4816
5823
|
}
|
|
4817
5824
|
_handleLeave(channel, leave, id) {
|
|
4818
|
-
const sub = this.
|
|
5825
|
+
const sub = this._getSubForPush(channel, id);
|
|
4819
5826
|
if (!sub) {
|
|
4820
5827
|
if (channel && this._isServerSub(channel)) {
|
|
4821
5828
|
const ctx = { channel, info: this._getJoinLeaveContext(leave.info) };
|
|
@@ -4823,17 +5830,25 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4823
5830
|
}
|
|
4824
5831
|
return;
|
|
4825
5832
|
}
|
|
5833
|
+
if (!sub._isSubscribed()) {
|
|
5834
|
+
this._debug("leave for a subscription that is not subscribed", channel);
|
|
5835
|
+
return;
|
|
5836
|
+
}
|
|
4826
5837
|
sub._handleLeave(leave);
|
|
4827
5838
|
}
|
|
4828
5839
|
_handleUnsubscribe(channel, unsubscribe) {
|
|
4829
|
-
const sub = this.
|
|
4830
|
-
if (!sub
|
|
4831
|
-
if (this._isServerSub(channel)) {
|
|
5840
|
+
const sub = this._getSubForPush(channel, 0);
|
|
5841
|
+
if (!sub) {
|
|
5842
|
+
if (channel && this._isServerSub(channel)) {
|
|
4832
5843
|
delete this._serverSubs[channel];
|
|
4833
5844
|
this.emit("unsubscribed", { channel });
|
|
4834
5845
|
}
|
|
4835
5846
|
return;
|
|
4836
5847
|
}
|
|
5848
|
+
if (!sub._isSubscribed()) {
|
|
5849
|
+
this._debug("unsubscribe push for a previous subscription", channel);
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
4837
5852
|
if (unsubscribe.code < 2500) {
|
|
4838
5853
|
sub._setUnsubscribed(unsubscribe.code, unsubscribe.reason, false);
|
|
4839
5854
|
} else {
|
|
@@ -4845,13 +5860,15 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4845
5860
|
}
|
|
4846
5861
|
_handleSubscribe(channel, sub) {
|
|
4847
5862
|
this._serverSubs[channel] = {
|
|
4848
|
-
"offset": sub.offset,
|
|
5863
|
+
"offset": toOffset(sub.offset),
|
|
4849
5864
|
"epoch": sub.epoch,
|
|
4850
|
-
"recoverable": sub.recoverable || false
|
|
5865
|
+
"recoverable": sub.recoverable || false,
|
|
5866
|
+
"subscribed": true
|
|
4851
5867
|
};
|
|
4852
5868
|
this.emit("subscribed", this._getSubscribeContext(channel, sub));
|
|
4853
5869
|
}
|
|
4854
5870
|
_handleDisconnect(disconnect) {
|
|
5871
|
+
this._transportWasOpen = true;
|
|
4855
5872
|
const code = disconnect.code;
|
|
4856
5873
|
let reconnect = true;
|
|
4857
5874
|
if (code >= 3500 && code < 4e3 || code >= 4500 && code < 5e3) {
|
|
@@ -4864,8 +5881,8 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4864
5881
|
channel,
|
|
4865
5882
|
data: pub.data
|
|
4866
5883
|
};
|
|
4867
|
-
if (pub.offset) {
|
|
4868
|
-
ctx.offset = pub.offset;
|
|
5884
|
+
if (hasOffset(pub.offset)) {
|
|
5885
|
+
ctx.offset = toOffset(pub.offset);
|
|
4869
5886
|
}
|
|
4870
5887
|
if (pub.info) {
|
|
4871
5888
|
ctx.info = this._getJoinLeaveContext(pub.info);
|
|
@@ -4891,23 +5908,40 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4891
5908
|
return info;
|
|
4892
5909
|
}
|
|
4893
5910
|
_handlePublication(channel, pub, id) {
|
|
4894
|
-
const sub = this.
|
|
5911
|
+
const sub = this._getSubForPush(channel, id);
|
|
4895
5912
|
if (!sub) {
|
|
4896
5913
|
if (channel && this._isServerSub(channel)) {
|
|
4897
5914
|
const ctx = this._getPublicationContext(channel, pub);
|
|
4898
|
-
this.
|
|
4899
|
-
|
|
4900
|
-
|
|
5915
|
+
this._pendingServerSubOffsets[channel] = hasOffset(pub.offset) ? toOffset(pub.offset) : null;
|
|
5916
|
+
const failures = this._handlerFailures;
|
|
5917
|
+
try {
|
|
5918
|
+
this.emit("publication", ctx);
|
|
5919
|
+
} finally {
|
|
5920
|
+
delete this._pendingServerSubOffsets[channel];
|
|
5921
|
+
}
|
|
5922
|
+
if (this._handlerFailures !== failures) {
|
|
5923
|
+
return;
|
|
5924
|
+
}
|
|
5925
|
+
if (hasOffset(pub.offset)) {
|
|
5926
|
+
this._serverSubs[channel].offset = toOffset(pub.offset);
|
|
5927
|
+
}
|
|
5928
|
+
if (pub.epoch) {
|
|
5929
|
+
this._serverSubs[channel].epoch = pub.epoch;
|
|
4901
5930
|
}
|
|
4902
5931
|
}
|
|
4903
5932
|
return;
|
|
4904
5933
|
}
|
|
5934
|
+
if (!sub._isSubscribed()) {
|
|
5935
|
+
this._debug("publication for a subscription that is not subscribed", channel);
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
4905
5938
|
sub._handlePublication(pub);
|
|
4906
5939
|
}
|
|
4907
5940
|
_handleMessage(message) {
|
|
4908
5941
|
this.emit("message", { data: message.data });
|
|
4909
5942
|
}
|
|
4910
5943
|
_handleServerPing(next) {
|
|
5944
|
+
this._resetReconnectAttemptsIfPending();
|
|
4911
5945
|
if (this._sendPong) {
|
|
4912
5946
|
const cmd = {};
|
|
4913
5947
|
this._transportSendCommands([cmd]);
|
|
@@ -4955,11 +5989,15 @@ var Centrifuge = class extends EventEmitter {
|
|
|
4955
5989
|
errback,
|
|
4956
5990
|
timeout: null
|
|
4957
5991
|
};
|
|
5992
|
+
const due = Date.now() + this._config.timeout;
|
|
4958
5993
|
this._callbacks[id].timeout = setTimeout(() => {
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
5994
|
+
const late = Date.now() - due >= lateCallTimer;
|
|
5995
|
+
this._callbacks[id].timeout = setTimeout(() => {
|
|
5996
|
+
delete this._callbacks[id];
|
|
5997
|
+
if (isFunction(errback)) {
|
|
5998
|
+
errback({ error: this._createErrorObject(errorCodes.timeout, "timeout") });
|
|
5999
|
+
}
|
|
6000
|
+
}, late ? lateCallGrace : 0);
|
|
4963
6001
|
}, this._config.timeout);
|
|
4964
6002
|
}
|
|
4965
6003
|
_addCommand(command) {
|