cross-tab-worker-databus 0.20.86 → 0.20.88
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 +26 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-SDOV3UHG.js → chunk-4UOLOWOD.js} +175 -33
- package/dist/{chunk-SDOV3UHG.js.map → chunk-4UOLOWOD.js.map} +3 -3
- package/dist/cjs/centrifuge.cjs +174 -32
- package/dist/cjs/centrifuge.cjs.map +3 -3
- package/dist/cjs/hooks.cjs +2 -2
- package/dist/cjs/hooks.cjs.map +2 -2
- package/dist/cjs/index.cjs +281 -52
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/vue.cjs +1 -1
- package/dist/cjs/vue.cjs.map +2 -2
- package/dist/core/data-bus.d.ts +34 -5
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/hooks.d.ts +2 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +2 -2
- package/dist/hooks.js.map +2 -2
- package/dist/index.js +108 -21
- package/dist/index.js.map +2 -2
- package/dist/vue.d.ts +2 -1
- package/dist/vue.d.ts.map +1 -1
- package/dist/vue.js +1 -1
- package/dist/vue.js.map +2 -2
- package/dist/websocket.d.ts +24 -2
- package/dist/websocket.d.ts.map +1 -1
- package/docs/api.md +15 -10
- package/docs/architecture.md +9 -4
- package/docs/roadmap.md +14 -1
- package/docs/transports.md +19 -2
- package/docs/zh/api.md +15 -10
- package/docs/zh/architecture.md +9 -4
- package/docs/zh/roadmap.md +14 -1
- package/docs/zh/transports.md +14 -2
- package/package.json +3 -3
package/dist/cjs/centrifuge.cjs
CHANGED
|
@@ -2331,7 +2331,7 @@ var DedupManager = class {
|
|
|
2331
2331
|
};
|
|
2332
2332
|
|
|
2333
2333
|
// src/core/version.ts
|
|
2334
|
-
var SDK_VERSION = true ? "0.20.
|
|
2334
|
+
var SDK_VERSION = true ? "0.20.88" : "";
|
|
2335
2335
|
|
|
2336
2336
|
// src/core/data-bus.ts
|
|
2337
2337
|
var DEFAULT_REPLAY_MAX_PER_TOPIC = 100;
|
|
@@ -2356,6 +2356,11 @@ var CrossTabDataBus = class {
|
|
|
2356
2356
|
started = false;
|
|
2357
2357
|
stopping = false;
|
|
2358
2358
|
transportReady = false;
|
|
2359
|
+
// Whether the installed transport has reported `connected` at least once
|
|
2360
|
+
// since the current open began. A clean `disconnected` after this point is
|
|
2361
|
+
// a lost working connection, not the pre-connect window of a worker-style
|
|
2362
|
+
// backend whose start() resolves before it reports the connection.
|
|
2363
|
+
transportHasConnected = false;
|
|
2359
2364
|
// Last transport failure, retained so ready() can surface it to callers who
|
|
2360
2365
|
// never awaited start() directly. Cleared on the next successful start.
|
|
2361
2366
|
lastError = null;
|
|
@@ -2394,6 +2399,19 @@ var CrossTabDataBus = class {
|
|
|
2394
2399
|
// a transport reopen succeeds so traces can correlate repeated failures.
|
|
2395
2400
|
recoveryAttempt = 0;
|
|
2396
2401
|
recoveryExhausted = false;
|
|
2402
|
+
// Gate that holds transport operations issued after a runtime `error` until
|
|
2403
|
+
// the scheduled recovery attempt has actually run. Without it, a dead
|
|
2404
|
+
// transport still has `transportReady === true` during the cooldown, so
|
|
2405
|
+
// publishes/subscribes would be written to the failed connection and lost.
|
|
2406
|
+
recoveryGate = null;
|
|
2407
|
+
recoveryGateRelease = null;
|
|
2408
|
+
recoveryTimer = null;
|
|
2409
|
+
recoveryTimerToken = 0;
|
|
2410
|
+
// Once an automatic attempt fails, an explicit transport operation may
|
|
2411
|
+
// recover immediately instead of waiting for the next paced attempt. The
|
|
2412
|
+
// gate still stays closed so the operation cannot reach the failed
|
|
2413
|
+
// transport; it is released by the successful on-demand reopen.
|
|
2414
|
+
recoveryDemandAllowed = false;
|
|
2397
2415
|
/** Monotonic generation incremented on every successful transport open.
|
|
2398
2416
|
* Stays in lockstep with `lastSuccessAt` so callers can detect that the
|
|
2399
2417
|
* transport has been reopened even if the timestamp window is short. */
|
|
@@ -2507,10 +2525,7 @@ var CrossTabDataBus = class {
|
|
|
2507
2525
|
this.suspendTransport();
|
|
2508
2526
|
},
|
|
2509
2527
|
onResume: () => {
|
|
2510
|
-
this.
|
|
2511
|
-
this.trace.start();
|
|
2512
|
-
this.startDedupSweep();
|
|
2513
|
-
this.replayManager.start();
|
|
2528
|
+
this.resumeSuspendedResources();
|
|
2514
2529
|
this.resumeTransport();
|
|
2515
2530
|
},
|
|
2516
2531
|
onDiagnostic: (event) => {
|
|
@@ -2540,7 +2555,11 @@ var CrossTabDataBus = class {
|
|
|
2540
2555
|
if (!transportDown) return Promise.resolve();
|
|
2541
2556
|
this.activeConfig = config;
|
|
2542
2557
|
this.resetFailureState();
|
|
2543
|
-
|
|
2558
|
+
const resumingFromSuspend = this.suspended;
|
|
2559
|
+
if (resumingFromSuspend) this.resumeSuspendedResources();
|
|
2560
|
+
const opening2 = this.reopenTransport();
|
|
2561
|
+
if (resumingFromSuspend) this.cluster.start();
|
|
2562
|
+
return opening2;
|
|
2544
2563
|
}
|
|
2545
2564
|
this.started = true;
|
|
2546
2565
|
this.stopping = false;
|
|
@@ -2612,8 +2631,38 @@ var CrossTabDataBus = class {
|
|
|
2612
2631
|
this.queuedStart = queued;
|
|
2613
2632
|
return queued;
|
|
2614
2633
|
}
|
|
2634
|
+
/** Release every operation waiting on the scheduled recovery attempt. */
|
|
2635
|
+
releaseRecoveryGate() {
|
|
2636
|
+
const release = this.recoveryGateRelease;
|
|
2637
|
+
this.recoveryGate = null;
|
|
2638
|
+
this.recoveryGateRelease = null;
|
|
2639
|
+
this.recoveryDemandAllowed = false;
|
|
2640
|
+
release?.();
|
|
2641
|
+
}
|
|
2642
|
+
/** Cancel a pending automatic retry when an explicit lifecycle transition
|
|
2643
|
+
* supersedes it. The released gate re-enters runTransport(), which then
|
|
2644
|
+
* follows the newest start/stop/suspend intent. */
|
|
2645
|
+
cancelScheduledRecovery() {
|
|
2646
|
+
this.recoveryTimerToken += 1;
|
|
2647
|
+
if (this.recoveryTimer !== null) {
|
|
2648
|
+
clearTimeout(this.recoveryTimer);
|
|
2649
|
+
this.recoveryTimer = null;
|
|
2650
|
+
}
|
|
2651
|
+
this.releaseRecoveryGate();
|
|
2652
|
+
}
|
|
2653
|
+
/** Keep the recovery gate closed after a failed attempt while allowing the
|
|
2654
|
+
* next explicit transport operation to start an immediate on-demand reopen.
|
|
2655
|
+
* If no gate/successor retry remains, release any waiters. */
|
|
2656
|
+
allowDemandRecovery() {
|
|
2657
|
+
if (this.recoveryGate !== null && this.started && !this.stopping && !this.suspended && this.status === WORKER_STATUS.ERROR) {
|
|
2658
|
+
this.recoveryDemandAllowed = true;
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
this.releaseRecoveryGate();
|
|
2662
|
+
}
|
|
2615
2663
|
/** Reset failure and recovery diagnostics for a new explicit start session. */
|
|
2616
2664
|
resetFailureState() {
|
|
2665
|
+
this.cancelScheduledRecovery();
|
|
2617
2666
|
this.lastError = null;
|
|
2618
2667
|
this.lastErrorAt = null;
|
|
2619
2668
|
this.lastFailure = null;
|
|
@@ -2633,22 +2682,27 @@ var CrossTabDataBus = class {
|
|
|
2633
2682
|
this.transportReady = false;
|
|
2634
2683
|
const chainedPendingStop = this.pendingStop;
|
|
2635
2684
|
const isCurrentLifecycle = () => lifecycleEpoch === this.lifecycleEpoch;
|
|
2685
|
+
let startupInProgress = true;
|
|
2636
2686
|
return before.catch(() => void 0).then(() => {
|
|
2637
2687
|
if (!isCurrentLifecycle() || this.stopping || this.suspended) return;
|
|
2638
2688
|
if (this.pendingStop === chainedPendingStop) this.pendingStop = null;
|
|
2689
|
+
this.transportHasConnected = false;
|
|
2639
2690
|
return Promise.resolve(
|
|
2640
2691
|
this.transport.start(config, {
|
|
2641
2692
|
onMessage: (message) => {
|
|
2642
2693
|
if (isCurrentLifecycle()) this.handleTransportMessage(message);
|
|
2643
2694
|
},
|
|
2644
2695
|
onStatus: (status) => {
|
|
2645
|
-
if (isCurrentLifecycle())
|
|
2696
|
+
if (isCurrentLifecycle()) {
|
|
2697
|
+
this.updateStatus(status, status !== WORKER_STATUS.ERROR || !startupInProgress);
|
|
2698
|
+
}
|
|
2646
2699
|
},
|
|
2647
2700
|
onError: (error) => {
|
|
2648
2701
|
if (isCurrentLifecycle()) this.reportError(error);
|
|
2649
2702
|
}
|
|
2650
2703
|
})
|
|
2651
2704
|
).then(() => {
|
|
2705
|
+
startupInProgress = false;
|
|
2652
2706
|
if (!isCurrentLifecycle()) return;
|
|
2653
2707
|
if (this.status === WORKER_STATUS.ERROR) {
|
|
2654
2708
|
throw new Error("Transport failed during startup.");
|
|
@@ -2657,24 +2711,26 @@ var CrossTabDataBus = class {
|
|
|
2657
2711
|
this.recoveryGeneration += 1;
|
|
2658
2712
|
this.lastSuccessAt = this.now();
|
|
2659
2713
|
this.transportReady = true;
|
|
2714
|
+
this.releaseRecoveryGate();
|
|
2660
2715
|
}
|
|
2661
2716
|
});
|
|
2662
2717
|
}).catch((error) => {
|
|
2663
2718
|
if (!isCurrentLifecycle()) throw error;
|
|
2719
|
+
startupInProgress = false;
|
|
2664
2720
|
if (stopClusterOnFailure) this.started = false;
|
|
2665
2721
|
if (!this.pendingStop) {
|
|
2666
2722
|
this.pendingStop = this.createStopPromise();
|
|
2667
2723
|
}
|
|
2668
|
-
this.updateStatus(WORKER_STATUS.ERROR);
|
|
2669
|
-
this.reportError(error);
|
|
2670
|
-
this.lastError = error;
|
|
2671
|
-
this.lastErrorAt = this.now();
|
|
2672
2724
|
this.transportReady = false;
|
|
2673
2725
|
if (stopClusterOnFailure) {
|
|
2674
2726
|
this.stopping = true;
|
|
2675
2727
|
this.cluster.stop();
|
|
2676
2728
|
this.stopping = false;
|
|
2677
2729
|
}
|
|
2730
|
+
this.recordError(error);
|
|
2731
|
+
this.startPromise = null;
|
|
2732
|
+
this.updateStatus(WORKER_STATUS.ERROR);
|
|
2733
|
+
this.notifyError(error);
|
|
2678
2734
|
throw error;
|
|
2679
2735
|
});
|
|
2680
2736
|
}
|
|
@@ -2683,7 +2739,10 @@ var CrossTabDataBus = class {
|
|
|
2683
2739
|
* Returns a rejected promise when the transport has failed and no start is in
|
|
2684
2740
|
* flight — the caller can retry by calling start() or ready() again. While an
|
|
2685
2741
|
* explicit stop() is settling, this rejects unless a restart is queued behind
|
|
2686
|
-
* it; false readiness during teardown is never reported.
|
|
2742
|
+
* it; false readiness during teardown is never reported. While the tab is
|
|
2743
|
+
* BFCache-suspended (pagehide without a following pageshow), this also
|
|
2744
|
+
* rejects: the suspended start promise is the transport-stop gate, not a
|
|
2745
|
+
* readiness signal.
|
|
2687
2746
|
*/
|
|
2688
2747
|
ready() {
|
|
2689
2748
|
if (this.queuedStart) return this.getQueuedStartReady();
|
|
@@ -2692,6 +2751,11 @@ var CrossTabDataBus = class {
|
|
|
2692
2751
|
"CrossTabDataBus is stopping; ready() cannot report readiness until stop() resolves. Wait for stop() to resolve, then call start() before awaiting ready()."
|
|
2693
2752
|
));
|
|
2694
2753
|
}
|
|
2754
|
+
if (this.suspended) {
|
|
2755
|
+
return Promise.reject(new Error(
|
|
2756
|
+
"CrossTabDataBus is suspended; ready() cannot report readiness until pageshow resumes the transport."
|
|
2757
|
+
));
|
|
2758
|
+
}
|
|
2695
2759
|
if (!this.started && !this.hasInitialConfig && this.lastError !== null) {
|
|
2696
2760
|
return Promise.reject(this.lastError);
|
|
2697
2761
|
}
|
|
@@ -2827,11 +2891,15 @@ var CrossTabDataBus = class {
|
|
|
2827
2891
|
getStatus() {
|
|
2828
2892
|
return this.status;
|
|
2829
2893
|
}
|
|
2830
|
-
/** Return the current automatic transport recovery state. `hasError` means a transport error is currently retained. */
|
|
2831
2894
|
/** Return the current automatic transport recovery state plus diagnostics.
|
|
2832
|
-
* `
|
|
2833
|
-
*
|
|
2834
|
-
*
|
|
2895
|
+
* `hasError`/`errorMessage`/`errorAt` describe the most recent retained
|
|
2896
|
+
* *transport* failure — from a transport open or a runtime `onError`. They
|
|
2897
|
+
* share the lifetime of the unified `lastFailure` ledger: a successful
|
|
2898
|
+
* recovery keeps the last failure visible, and only an explicit `start()`
|
|
2899
|
+
* clears it. `generation` increments on every successful transport open
|
|
2900
|
+
* (initial start and every recovery); `lastSuccessAt` is the timestamp of
|
|
2901
|
+
* the most recent successful open, or `null` until the transport reaches
|
|
2902
|
+
* `ready`. */
|
|
2835
2903
|
getRecoveryStats() {
|
|
2836
2904
|
const errorMessage = this.lastError instanceof Error ? this.lastError.message : this.lastError === null ? null : String(this.lastError);
|
|
2837
2905
|
return {
|
|
@@ -2858,7 +2926,7 @@ var CrossTabDataBus = class {
|
|
|
2858
2926
|
* unified failure ledger and recovery context that explains the verdict. */
|
|
2859
2927
|
getHealthSummary() {
|
|
2860
2928
|
const transport = this.transport;
|
|
2861
|
-
const transportDown =
|
|
2929
|
+
const transportDown = this.status !== WORKER_STATUS.CONNECTED;
|
|
2862
2930
|
const state = !this.started ? HEALTH_STATE.STOPPED : this.suspended ? HEALTH_STATE.SUSPENDED : transportDown ? this.recoveryExhausted ? HEALTH_STATE.DEGRADED : this.status === WORKER_STATUS.CONNECTING && this.recoveryAttempt === 0 ? HEALTH_STATE.STARTING : HEALTH_STATE.RECOVERING : HEALTH_STATE.HEALTHY;
|
|
2863
2931
|
return {
|
|
2864
2932
|
healthy: state === HEALTH_STATE.HEALTHY,
|
|
@@ -2949,6 +3017,7 @@ var CrossTabDataBus = class {
|
|
|
2949
3017
|
async performStop() {
|
|
2950
3018
|
this.lifecycleEpoch += 1;
|
|
2951
3019
|
this.stopping = true;
|
|
3020
|
+
this.cancelScheduledRecovery();
|
|
2952
3021
|
this.replayManager.suspend();
|
|
2953
3022
|
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.STOP });
|
|
2954
3023
|
this.trace.stop();
|
|
@@ -2961,6 +3030,8 @@ var CrossTabDataBus = class {
|
|
|
2961
3030
|
const pendingStop = this.pendingStop;
|
|
2962
3031
|
if (pendingStop) await pendingStop.catch(() => void 0);
|
|
2963
3032
|
else await this.transport.stop();
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
this.reportError(error);
|
|
2964
3035
|
} finally {
|
|
2965
3036
|
this.transportSubscribedTopics.clear();
|
|
2966
3037
|
this.resetDedup();
|
|
@@ -3023,13 +3094,15 @@ var CrossTabDataBus = class {
|
|
|
3023
3094
|
* Propagate a status change to the cluster, trace, and all registered
|
|
3024
3095
|
* status handlers. On reconnect, re-subscribe any topics assigned to us.
|
|
3025
3096
|
*/
|
|
3026
|
-
updateStatus(status) {
|
|
3097
|
+
updateStatus(status, notifyHandlers = true) {
|
|
3027
3098
|
const previousStatus = this.status;
|
|
3028
3099
|
this.status = status;
|
|
3100
|
+
if (status === WORKER_STATUS.CONNECTED) this.transportHasConnected = true;
|
|
3029
3101
|
if (previousStatus !== status) this.trace.event({ type: TRACE_EVENT_TYPE.STATUS, status });
|
|
3030
3102
|
this.cluster.setStatus(status);
|
|
3031
3103
|
if (status === WORKER_STATUS.DISCONNECTED || status === WORKER_STATUS.ERROR) this.transportSubscribedTopics.clear();
|
|
3032
3104
|
if (status === WORKER_STATUS.CONNECTED && previousStatus !== WORKER_STATUS.CONNECTED) {
|
|
3105
|
+
if (this.transportReady) this.releaseRecoveryGate();
|
|
3033
3106
|
for (const topic of this.cluster.getSnapshot().assignedTopics) this.subscribeTransport(topic);
|
|
3034
3107
|
}
|
|
3035
3108
|
if (status === WORKER_STATUS.ERROR && this.started && !this.stopping) {
|
|
@@ -3042,23 +3115,49 @@ var CrossTabDataBus = class {
|
|
|
3042
3115
|
this.recoveryExhausted = true;
|
|
3043
3116
|
this.trace.event({ type: TRACE_EVENT_TYPE.RELIABILITY, operation: RELIABILITY_OPERATION.TRANSPORT_RECOVERY, attempt: this.recoveryMaxAttempts, outcome: RECOVERY_OUTCOME.EXHAUSTED });
|
|
3044
3117
|
}
|
|
3118
|
+
this.releaseRecoveryGate();
|
|
3045
3119
|
return;
|
|
3046
3120
|
}
|
|
3047
3121
|
this.trace.event({ type: TRACE_EVENT_TYPE.RELIABILITY, operation: RELIABILITY_OPERATION.TRANSPORT_RECOVERY, attempt, outcome: RECOVERY_OUTCOME.SCHEDULED });
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3122
|
+
if (this.recoveryGate === null) {
|
|
3123
|
+
let release;
|
|
3124
|
+
this.recoveryGate = new Promise((resolve) => {
|
|
3125
|
+
release = resolve;
|
|
3126
|
+
});
|
|
3127
|
+
this.recoveryGateRelease = release;
|
|
3128
|
+
}
|
|
3129
|
+
this.recoveryDemandAllowed = false;
|
|
3130
|
+
const timerToken = ++this.recoveryTimerToken;
|
|
3131
|
+
this.recoveryTimer = setTimeout(() => {
|
|
3132
|
+
if (timerToken !== this.recoveryTimerToken) return;
|
|
3133
|
+
this.recoveryTimer = null;
|
|
3134
|
+
if (this.stopping || !this.started || this.suspended || this.status !== WORKER_STATUS.ERROR) {
|
|
3135
|
+
this.releaseRecoveryGate();
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
this.recoveryDemandAllowed = false;
|
|
3139
|
+
const opening = this.reopenTransport(attempt);
|
|
3140
|
+
void opening.then(
|
|
3141
|
+
() => this.releaseRecoveryGate(),
|
|
3142
|
+
() => this.allowDemandRecovery()
|
|
3143
|
+
);
|
|
3052
3144
|
}, this.recoveryCooldownMs);
|
|
3053
3145
|
}
|
|
3146
|
+
} else if (status === WORKER_STATUS.ERROR) {
|
|
3147
|
+
this.releaseRecoveryGate();
|
|
3054
3148
|
}
|
|
3055
|
-
this.invokeHandlers(this.statusHandlers, (handler) => handler(status));
|
|
3149
|
+
if (notifyHandlers) this.invokeHandlers(this.statusHandlers, (handler) => handler(status));
|
|
3056
3150
|
}
|
|
3057
|
-
|
|
3151
|
+
recordError(error, source = FAILURE_SOURCE.TRANSPORT) {
|
|
3152
|
+
const at = this.now();
|
|
3153
|
+
if (source === FAILURE_SOURCE.TRANSPORT) {
|
|
3154
|
+
this.lastError = error;
|
|
3155
|
+
this.lastErrorAt = at;
|
|
3156
|
+
}
|
|
3058
3157
|
this.lastFailure = {
|
|
3059
3158
|
source,
|
|
3060
3159
|
message: error instanceof Error ? error.message : String(error),
|
|
3061
|
-
at
|
|
3160
|
+
at
|
|
3062
3161
|
};
|
|
3063
3162
|
if (source === FAILURE_SOURCE.PERSISTENCE) {
|
|
3064
3163
|
this.persistenceFailureCount += 1;
|
|
@@ -3069,8 +3168,14 @@ var CrossTabDataBus = class {
|
|
|
3069
3168
|
type: TRACE_EVENT_TYPE.ERROR,
|
|
3070
3169
|
source: source === FAILURE_SOURCE.TRANSPORT ? TRACE_ERROR_SOURCE.TRANSPORT : TRACE_ERROR_SOURCE.OPERATION
|
|
3071
3170
|
});
|
|
3171
|
+
}
|
|
3172
|
+
notifyError(error) {
|
|
3072
3173
|
this.invokeHandlers(this.errorHandlers, (handler) => handler(error), INVOKE_LABEL.ERROR_HANDLER);
|
|
3073
3174
|
}
|
|
3175
|
+
reportError(error, source = FAILURE_SOURCE.TRANSPORT) {
|
|
3176
|
+
this.recordError(error, source);
|
|
3177
|
+
this.notifyError(error);
|
|
3178
|
+
}
|
|
3074
3179
|
/** Report a persistence failure to the trace and the unified failure ledger,
|
|
3075
3180
|
* unless it is a {@link PersistenceRetryCancelledError} cancellation from a
|
|
3076
3181
|
* lifecycle transition (teardown should stay quiet). */
|
|
@@ -3133,6 +3238,15 @@ var CrossTabDataBus = class {
|
|
|
3133
3238
|
}
|
|
3134
3239
|
}
|
|
3135
3240
|
}
|
|
3241
|
+
/** Resume the resources paused by a pagehide suspension. Both the native
|
|
3242
|
+
* pageshow path and explicit start() must run this so an explicit resume
|
|
3243
|
+
* cannot leave trace metrics and periodic cleanup timers permanently off. */
|
|
3244
|
+
resumeSuspendedResources() {
|
|
3245
|
+
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.RESUME });
|
|
3246
|
+
this.trace.start();
|
|
3247
|
+
this.startDedupSweep();
|
|
3248
|
+
this.replayManager.start();
|
|
3249
|
+
}
|
|
3136
3250
|
/**
|
|
3137
3251
|
* Suspend the transport when the tab goes hidden. Stops the transport and
|
|
3138
3252
|
* clears subscription state so it will be re-established on resume.
|
|
@@ -3141,11 +3255,15 @@ var CrossTabDataBus = class {
|
|
|
3141
3255
|
if (this.stopping) return;
|
|
3142
3256
|
this.lifecycleEpoch += 1;
|
|
3143
3257
|
this.suspended = true;
|
|
3258
|
+
this.cancelScheduledRecovery();
|
|
3144
3259
|
this.transportReady = false;
|
|
3145
3260
|
this.transportSubscribedTopics.clear();
|
|
3146
3261
|
this.updateStatus(WORKER_STATUS.DISCONNECTED);
|
|
3147
|
-
if (this.pendingStop)
|
|
3148
|
-
|
|
3262
|
+
if (this.pendingStop && (this.startPromise === null || this.startPromise === this.pendingStop)) {
|
|
3263
|
+
this.startPromise = this.pendingStop;
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
const pending = this.startPromise ?? this.pendingStop ?? Promise.resolve();
|
|
3149
3267
|
const stopping = pending.catch(() => void 0).then(() => this.transport.stop()).catch((error) => this.reportError(error));
|
|
3150
3268
|
this.startPromise = stopping;
|
|
3151
3269
|
this.pendingStop = stopping;
|
|
@@ -3210,7 +3328,24 @@ var CrossTabDataBus = class {
|
|
|
3210
3328
|
*/
|
|
3211
3329
|
runTransport(operation) {
|
|
3212
3330
|
if (this.suspended) return;
|
|
3213
|
-
if (this.
|
|
3331
|
+
if (this.recoveryGate && !this.stopping) {
|
|
3332
|
+
if (this.recoveryDemandAllowed && this.status === WORKER_STATUS.ERROR && !this.suspended) {
|
|
3333
|
+
this.recoveryDemandAllowed = false;
|
|
3334
|
+
const opening = this.reopenTransport();
|
|
3335
|
+
void opening.then(
|
|
3336
|
+
() => this.releaseRecoveryGate(),
|
|
3337
|
+
() => this.allowDemandRecovery()
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3340
|
+
const gate = this.recoveryGate;
|
|
3341
|
+
void gate.then(() => {
|
|
3342
|
+
if (this.stopping || this.suspended) return;
|
|
3343
|
+
this.runTransport(operation);
|
|
3344
|
+
});
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
const droppedAfterConnect = this.transportHasConnected && this.status === WORKER_STATUS.DISCONNECTED;
|
|
3348
|
+
if (this.transportReady && this.status !== WORKER_STATUS.ERROR && !droppedAfterConnect && !this.stopping) {
|
|
3214
3349
|
try {
|
|
3215
3350
|
void Promise.resolve(operation()).catch((error) => this.reportError(error));
|
|
3216
3351
|
} catch (error) {
|
|
@@ -3223,10 +3358,17 @@ var CrossTabDataBus = class {
|
|
|
3223
3358
|
ready = this.reopenTransport();
|
|
3224
3359
|
}
|
|
3225
3360
|
if (!ready || this.stopping) return;
|
|
3226
|
-
void ready.then(
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3361
|
+
void ready.then(
|
|
3362
|
+
() => {
|
|
3363
|
+
if (!this.started || this.stopping || this.suspended) return;
|
|
3364
|
+
return operation();
|
|
3365
|
+
},
|
|
3366
|
+
// The opening promise reports its own lifecycle failure through
|
|
3367
|
+
// openTransport(). Swallowing it here prevents a stale startup
|
|
3368
|
+
// rejection from being recorded again after an onStatus/onError
|
|
3369
|
+
// callback has already started and reset the ledger for a retry.
|
|
3370
|
+
() => void 0
|
|
3371
|
+
).catch((error) => this.reportError(error));
|
|
3230
3372
|
}
|
|
3231
3373
|
/**
|
|
3232
3374
|
* Publications started after teardown begins cannot reach any transport.
|