cross-tab-worker-databus 0.20.87 → 0.20.89

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.
@@ -1661,8 +1661,9 @@ var DataBusTraceReporter = class {
1661
1661
  /** Start the periodic metrics flush interval. No-op when mode is 'events'
1662
1662
  * (no metrics to emit), when disabled, or when already running. */
1663
1663
  start() {
1664
- if (!this.enabled || this.intervalHandle || this.mode === TRACE_MODE.EVENTS) return;
1664
+ if (!this.enabled) return;
1665
1665
  this.stopped = false;
1666
+ if (this.intervalHandle || this.mode === TRACE_MODE.EVENTS) return;
1666
1667
  this.intervalStartedAt = this.now();
1667
1668
  this.intervalHandle = setInterval(() => this.flush(), this.metricsIntervalMs);
1668
1669
  }
@@ -1675,6 +1676,7 @@ var DataBusTraceReporter = class {
1675
1676
  }
1676
1677
  stop() {
1677
1678
  this.stopped = true;
1679
+ this.pendingEvents = [];
1678
1680
  this.pause();
1679
1681
  }
1680
1682
  /** Synchronous sink state for diagnostics: whether delivery is async and how
@@ -1685,7 +1687,7 @@ var DataBusTraceReporter = class {
1685
1687
  }
1686
1688
  /** Record an instantaneous trace event (lifecycle, status, error, etc.). */
1687
1689
  event(event) {
1688
- if (!this.enabled || this.mode === TRACE_MODE.METRICS) return;
1690
+ if (!this.enabled || this.stopped || this.mode === TRACE_MODE.METRICS) return;
1689
1691
  this.emit({ ...event, timestamp: this.now() });
1690
1692
  }
1691
1693
  /** Synchronous snapshot of the current metrics window without resetting it.
@@ -1768,7 +1770,7 @@ var DataBusTraceReporter = class {
1768
1770
  * Extracted so the four record / flush methods share one guard expression
1769
1771
  * instead of repeating `!this.enabled || this.mode === 'events'` at each. */
1770
1772
  get metricsActive() {
1771
- return this.enabled && this.mode !== TRACE_MODE.EVENTS;
1773
+ return this.enabled && !this.stopped && this.mode !== TRACE_MODE.EVENTS;
1772
1774
  }
1773
1775
  /** Emit the accumulated metrics snapshot if the interval is active. */
1774
1776
  flush() {
@@ -2070,6 +2072,8 @@ var ReplayManager = class {
2070
2072
  * or stopped bus does not keep hammering the store) and stop the sweep. */
2071
2073
  suspend() {
2072
2074
  this.retryGeneration += 1;
2075
+ this.pendingReplayPersistence = [];
2076
+ this.retentionCutoff = null;
2073
2077
  this.stop();
2074
2078
  }
2075
2079
  /** Drop all in-memory buffers (used on full teardown). */
@@ -2168,19 +2172,20 @@ var ReplayManager = class {
2168
2172
  this.retentionCutoff = cutoff;
2169
2173
  }
2170
2174
  if (this.retentionCleanup) return;
2175
+ const generation = this.retryGeneration;
2171
2176
  this.retentionCleanup = (async () => {
2172
- while (this.retentionCutoff !== null) {
2177
+ while (this.retentionCutoff !== null && generation === this.retryGeneration) {
2173
2178
  const nextCutoff = this.retentionCutoff;
2174
2179
  this.retentionCutoff = null;
2175
2180
  try {
2176
2181
  await this.persistence.clearBefore(nextCutoff);
2177
2182
  } catch (error) {
2178
- this.onPersistenceError(error);
2183
+ if (generation === this.retryGeneration) this.onPersistenceError(error);
2179
2184
  }
2180
2185
  }
2181
2186
  })().finally(() => {
2182
2187
  this.retentionCleanup = null;
2183
- if (this.retentionCutoff !== null) {
2188
+ if (this.retentionCutoff !== null && generation === this.retryGeneration) {
2184
2189
  this.scheduleRetentionCleanup(this.retentionCutoff);
2185
2190
  }
2186
2191
  });
@@ -2197,7 +2202,9 @@ var ReplayManager = class {
2197
2202
  attempt += 1;
2198
2203
  try {
2199
2204
  if (generation !== this.retryGeneration) throw new PersistenceRetryCancelledError();
2200
- return await operation();
2205
+ const result = await operation();
2206
+ if (generation !== this.retryGeneration) throw new PersistenceRetryCancelledError();
2207
+ return result;
2201
2208
  } catch (error) {
2202
2209
  if (error instanceof PersistenceRetryCancelledError || generation !== this.retryGeneration) {
2203
2210
  throw new PersistenceRetryCancelledError();
@@ -2331,7 +2338,7 @@ var DedupManager = class {
2331
2338
  };
2332
2339
 
2333
2340
  // src/core/version.ts
2334
- var SDK_VERSION = true ? "0.20.87" : "";
2341
+ var SDK_VERSION = true ? "0.20.89" : "";
2335
2342
 
2336
2343
  // src/core/data-bus.ts
2337
2344
  var DEFAULT_REPLAY_MAX_PER_TOPIC = 100;
@@ -2525,10 +2532,7 @@ var CrossTabDataBus = class {
2525
2532
  this.suspendTransport();
2526
2533
  },
2527
2534
  onResume: () => {
2528
- this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.RESUME });
2529
- this.trace.start();
2530
- this.startDedupSweep();
2531
- this.replayManager.start();
2535
+ this.resumeSuspendedResources();
2532
2536
  this.resumeTransport();
2533
2537
  },
2534
2538
  onDiagnostic: (event) => {
@@ -2558,15 +2562,19 @@ var CrossTabDataBus = class {
2558
2562
  if (!transportDown) return Promise.resolve();
2559
2563
  this.activeConfig = config;
2560
2564
  this.resetFailureState();
2561
- return this.reopenTransport();
2565
+ const resumingFromSuspend = this.suspended;
2566
+ if (resumingFromSuspend) this.resumeSuspendedResources();
2567
+ const opening2 = this.reopenTransport();
2568
+ if (resumingFromSuspend) this.cluster.start();
2569
+ return opening2;
2562
2570
  }
2563
2571
  this.started = true;
2564
2572
  this.stopping = false;
2565
2573
  this.suspended = false;
2566
2574
  this.activeConfig = config;
2567
2575
  this.resetFailureState();
2568
- this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.START });
2569
2576
  this.trace.start();
2577
+ this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.START });
2570
2578
  this.startDedupSweep();
2571
2579
  this.replayManager.start();
2572
2580
  this.updateStatus(WORKER_STATUS.CONNECTING);
@@ -2681,6 +2689,7 @@ var CrossTabDataBus = class {
2681
2689
  this.transportReady = false;
2682
2690
  const chainedPendingStop = this.pendingStop;
2683
2691
  const isCurrentLifecycle = () => lifecycleEpoch === this.lifecycleEpoch;
2692
+ let startupInProgress = true;
2684
2693
  return before.catch(() => void 0).then(() => {
2685
2694
  if (!isCurrentLifecycle() || this.stopping || this.suspended) return;
2686
2695
  if (this.pendingStop === chainedPendingStop) this.pendingStop = null;
@@ -2691,13 +2700,16 @@ var CrossTabDataBus = class {
2691
2700
  if (isCurrentLifecycle()) this.handleTransportMessage(message);
2692
2701
  },
2693
2702
  onStatus: (status) => {
2694
- if (isCurrentLifecycle()) this.updateStatus(status);
2703
+ if (isCurrentLifecycle()) {
2704
+ this.updateStatus(status, status !== WORKER_STATUS.ERROR || !startupInProgress);
2705
+ }
2695
2706
  },
2696
2707
  onError: (error) => {
2697
2708
  if (isCurrentLifecycle()) this.reportError(error);
2698
2709
  }
2699
2710
  })
2700
2711
  ).then(() => {
2712
+ startupInProgress = false;
2701
2713
  if (!isCurrentLifecycle()) return;
2702
2714
  if (this.status === WORKER_STATUS.ERROR) {
2703
2715
  throw new Error("Transport failed during startup.");
@@ -2711,18 +2723,21 @@ var CrossTabDataBus = class {
2711
2723
  });
2712
2724
  }).catch((error) => {
2713
2725
  if (!isCurrentLifecycle()) throw error;
2726
+ startupInProgress = false;
2714
2727
  if (stopClusterOnFailure) this.started = false;
2715
2728
  if (!this.pendingStop) {
2716
2729
  this.pendingStop = this.createStopPromise();
2717
2730
  }
2718
2731
  this.transportReady = false;
2719
- this.updateStatus(WORKER_STATUS.ERROR);
2720
- this.reportError(error);
2721
2732
  if (stopClusterOnFailure) {
2722
2733
  this.stopping = true;
2723
2734
  this.cluster.stop();
2724
2735
  this.stopping = false;
2725
2736
  }
2737
+ this.recordError(error);
2738
+ this.startPromise = null;
2739
+ this.updateStatus(WORKER_STATUS.ERROR);
2740
+ this.notifyError(error);
2726
2741
  throw error;
2727
2742
  });
2728
2743
  }
@@ -2919,7 +2934,7 @@ var CrossTabDataBus = class {
2919
2934
  getHealthSummary() {
2920
2935
  const transport = this.transport;
2921
2936
  const transportDown = this.status !== WORKER_STATUS.CONNECTED;
2922
- 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;
2937
+ const state = !this.started || this.stopping ? 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;
2923
2938
  return {
2924
2939
  healthy: state === HEALTH_STATE.HEALTHY,
2925
2940
  state,
@@ -2990,7 +3005,7 @@ var CrossTabDataBus = class {
2990
3005
  this.canceledQueuedStartToken = this.queuedStartToken;
2991
3006
  this.queuedStart = null;
2992
3007
  }
2993
- if (this.stopPromise) return this.stopPromise;
3008
+ if (this.stopPromise && this.stopping) return this.stopPromise;
2994
3009
  if (!this.started && !this.startPromise && !this.pendingStop && !this.transportReady) {
2995
3010
  return Promise.resolve();
2996
3011
  }
@@ -3086,7 +3101,7 @@ var CrossTabDataBus = class {
3086
3101
  * Propagate a status change to the cluster, trace, and all registered
3087
3102
  * status handlers. On reconnect, re-subscribe any topics assigned to us.
3088
3103
  */
3089
- updateStatus(status) {
3104
+ updateStatus(status, notifyHandlers = true) {
3090
3105
  const previousStatus = this.status;
3091
3106
  this.status = status;
3092
3107
  if (status === WORKER_STATUS.CONNECTED) this.transportHasConnected = true;
@@ -3138,9 +3153,9 @@ var CrossTabDataBus = class {
3138
3153
  } else if (status === WORKER_STATUS.ERROR) {
3139
3154
  this.releaseRecoveryGate();
3140
3155
  }
3141
- this.invokeHandlers(this.statusHandlers, (handler) => handler(status));
3156
+ if (notifyHandlers) this.invokeHandlers(this.statusHandlers, (handler) => handler(status));
3142
3157
  }
3143
- reportError(error, source = FAILURE_SOURCE.TRANSPORT) {
3158
+ recordError(error, source = FAILURE_SOURCE.TRANSPORT) {
3144
3159
  const at = this.now();
3145
3160
  if (source === FAILURE_SOURCE.TRANSPORT) {
3146
3161
  this.lastError = error;
@@ -3160,8 +3175,14 @@ var CrossTabDataBus = class {
3160
3175
  type: TRACE_EVENT_TYPE.ERROR,
3161
3176
  source: source === FAILURE_SOURCE.TRANSPORT ? TRACE_ERROR_SOURCE.TRANSPORT : TRACE_ERROR_SOURCE.OPERATION
3162
3177
  });
3178
+ }
3179
+ notifyError(error) {
3163
3180
  this.invokeHandlers(this.errorHandlers, (handler) => handler(error), INVOKE_LABEL.ERROR_HANDLER);
3164
3181
  }
3182
+ reportError(error, source = FAILURE_SOURCE.TRANSPORT) {
3183
+ this.recordError(error, source);
3184
+ this.notifyError(error);
3185
+ }
3165
3186
  /** Report a persistence failure to the trace and the unified failure ledger,
3166
3187
  * unless it is a {@link PersistenceRetryCancelledError} cancellation from a
3167
3188
  * lifecycle transition (teardown should stay quiet). */
@@ -3224,6 +3245,15 @@ var CrossTabDataBus = class {
3224
3245
  }
3225
3246
  }
3226
3247
  }
3248
+ /** Resume the resources paused by a pagehide suspension. Both the native
3249
+ * pageshow path and explicit start() must run this so an explicit resume
3250
+ * cannot leave trace metrics and periodic cleanup timers permanently off. */
3251
+ resumeSuspendedResources() {
3252
+ this.trace.start();
3253
+ this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.RESUME });
3254
+ this.startDedupSweep();
3255
+ this.replayManager.start();
3256
+ }
3227
3257
  /**
3228
3258
  * Suspend the transport when the tab goes hidden. Stops the transport and
3229
3259
  * clears subscription state so it will be re-established on resume.
@@ -3236,8 +3266,11 @@ var CrossTabDataBus = class {
3236
3266
  this.transportReady = false;
3237
3267
  this.transportSubscribedTopics.clear();
3238
3268
  this.updateStatus(WORKER_STATUS.DISCONNECTED);
3239
- if (this.pendingStop) return;
3240
- const pending = this.startPromise ?? Promise.resolve();
3269
+ if (this.pendingStop && (this.startPromise === null || this.startPromise === this.pendingStop)) {
3270
+ this.startPromise = this.pendingStop;
3271
+ return;
3272
+ }
3273
+ const pending = this.startPromise ?? this.pendingStop ?? Promise.resolve();
3241
3274
  const stopping = pending.catch(() => void 0).then(() => this.transport.stop()).catch((error) => this.reportError(error));
3242
3275
  this.startPromise = stopping;
3243
3276
  this.pendingStop = stopping;
@@ -3332,10 +3365,17 @@ var CrossTabDataBus = class {
3332
3365
  ready = this.reopenTransport();
3333
3366
  }
3334
3367
  if (!ready || this.stopping) return;
3335
- void ready.then(() => {
3336
- if (!this.started || this.stopping || this.suspended) return;
3337
- return operation();
3338
- }).catch((error) => this.reportError(error));
3368
+ void ready.then(
3369
+ () => {
3370
+ if (!this.started || this.stopping || this.suspended) return;
3371
+ return operation();
3372
+ },
3373
+ // The opening promise reports its own lifecycle failure through
3374
+ // openTransport(). Swallowing it here prevents a stale startup
3375
+ // rejection from being recorded again after an onStatus/onError
3376
+ // callback has already started and reset the ledger for a retry.
3377
+ () => void 0
3378
+ ).catch((error) => this.reportError(error));
3339
3379
  }
3340
3380
  /**
3341
3381
  * Publications started after teardown begins cannot reach any transport.
@@ -3441,6 +3481,10 @@ var CentrifugeSession = class {
3441
3481
  transferable = false;
3442
3482
  tokenBridge = false;
3443
3483
  nextRequestId = 1;
3484
+ // Bumped on every initialize/stop. Async client callbacks capture the value
3485
+ // from the connection that created them so a stopped client cannot emit into
3486
+ // a later session instance (including the main-thread local fallback).
3487
+ lifecycle = 0;
3444
3488
  pendingTokenRequests = /* @__PURE__ */ new Map();
3445
3489
  /** Dispatch an incoming Worker message to the matching operation.
3446
3490
  * Unknown message types are ignored rather than thrown, so a future protocol
@@ -3476,6 +3520,7 @@ var CentrifugeSession = class {
3476
3520
  /** Create the Centrifuge client, wire up lifecycle listeners, and connect. */
3477
3521
  initialize(url, config, transferable, tokenBridge) {
3478
3522
  if (this.client) return;
3523
+ const lifecycle = ++this.lifecycle;
3479
3524
  this.transferable = transferable;
3480
3525
  this.tokenBridge = tokenBridge;
3481
3526
  const clientOptions = tokenBridge ? {
@@ -3489,12 +3534,23 @@ var CentrifugeSession = class {
3489
3534
  const client = new import_centrifuge.Centrifuge(url, clientOptions);
3490
3535
  this.client = client;
3491
3536
  client.on("state", (context) => {
3537
+ if (this.lifecycle !== lifecycle) return;
3492
3538
  this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: normalizeStatus(context.newState) });
3493
3539
  });
3494
- client.on("connected", () => this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: WORKER_STATUS.CONNECTED }));
3495
- client.on("disconnected", () => this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: WORKER_STATUS.DISCONNECTED }));
3496
- client.on("error", (context) => this.postError(context));
3540
+ client.on("connected", () => {
3541
+ if (this.lifecycle !== lifecycle) return;
3542
+ this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: WORKER_STATUS.CONNECTED });
3543
+ });
3544
+ client.on("disconnected", () => {
3545
+ if (this.lifecycle !== lifecycle) return;
3546
+ this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: WORKER_STATUS.DISCONNECTED });
3547
+ });
3548
+ client.on("error", (context) => {
3549
+ if (this.lifecycle !== lifecycle) return;
3550
+ this.postError(context);
3551
+ });
3497
3552
  client.on("publication", (context) => {
3553
+ if (this.lifecycle !== lifecycle) return;
3498
3554
  const topic = context.channel || getPayloadTopic(context.data);
3499
3555
  if (!topic || this.subscriptions.has(topic)) return;
3500
3556
  this.postPublication(topic, context.data);
@@ -3507,6 +3563,7 @@ var CentrifugeSession = class {
3507
3563
  * avoiding the removeAllListeners + re-on churn on every duplicate message. */
3508
3564
  subscribe(topic) {
3509
3565
  if (!this.client) return this.postError(new Error("Centrifuge client is not initialized."));
3566
+ const lifecycle = this.lifecycle;
3510
3567
  const existing = this.subscriptions.get(topic);
3511
3568
  if (existing) {
3512
3569
  existing.subscribe();
@@ -3519,10 +3576,16 @@ var CentrifugeSession = class {
3519
3576
  subscription.removeAllListeners("unsubscribed");
3520
3577
  this.subscriptions.set(topic, subscription);
3521
3578
  subscription.on("publication", (context) => {
3579
+ if (this.lifecycle !== lifecycle) return;
3522
3580
  this.postPublication(topic, context.data);
3523
3581
  });
3524
- subscription.on("error", (context) => this.postError(context));
3525
- subscription.on("unsubscribed", () => this.subscriptions.delete(topic));
3582
+ subscription.on("error", (context) => {
3583
+ if (this.lifecycle !== lifecycle) return;
3584
+ this.postError(context);
3585
+ });
3586
+ subscription.on("unsubscribed", () => {
3587
+ if (this.lifecycle === lifecycle) this.subscriptions.delete(topic);
3588
+ });
3526
3589
  subscription.subscribe();
3527
3590
  }
3528
3591
  /** Unsubscribe from a Centrifuge channel and clean up the local reference.
@@ -3540,13 +3603,16 @@ var CentrifugeSession = class {
3540
3603
  /** Publish a message to the Centrifuge channel. */
3541
3604
  publish(topic, data, messageId, timestamp) {
3542
3605
  if (!this.client) return this.postError(new Error("Centrifuge client is not initialized."));
3606
+ const lifecycle = this.lifecycle;
3543
3607
  const hasMetadata = messageId !== void 0 || timestamp !== void 0;
3544
3608
  const payload = hasMetadata ? {
3545
3609
  data,
3546
3610
  ...messageId === void 0 ? {} : { messageId },
3547
3611
  ...timestamp === void 0 ? {} : { timestamp }
3548
3612
  } : data;
3549
- void this.client.publish(topic, payload).catch((error) => this.postError(error));
3613
+ void this.client.publish(topic, payload).catch((error) => {
3614
+ if (this.lifecycle === lifecycle) this.postError(error);
3615
+ });
3550
3616
  }
3551
3617
  /** Forward a publication to the transport. Binary payloads take the
3552
3618
  * zero-copy `MESSAGE_BIN` path when `transferable` is enabled; everything
@@ -3569,6 +3635,7 @@ var CentrifugeSession = class {
3569
3635
  }
3570
3636
  /** Disconnect the client and clear all subscriptions. */
3571
3637
  stop() {
3638
+ this.lifecycle += 1;
3572
3639
  for (const [, pending] of this.pendingTokenRequests) {
3573
3640
  pending.reject(new Error("Centrifuge session stopped before the credential was resolved."));
3574
3641
  }
@@ -3582,9 +3649,14 @@ var CentrifugeSession = class {
3582
3649
  * Used as Centrifuge's `getToken` / `getChannelToken` when token bridging is
3583
3650
  * enabled; resolved or rejected by a matching TOKEN_RESPONSE / TOKEN_ERROR. */
3584
3651
  requestToken(kind, channel) {
3652
+ const lifecycle = this.lifecycle;
3585
3653
  const requestId = this.nextRequestId;
3586
3654
  this.nextRequestId += 1;
3587
3655
  return new Promise((resolve, reject) => {
3656
+ if (this.lifecycle !== lifecycle) {
3657
+ reject(new Error("Centrifuge session stopped before the credential was requested."));
3658
+ return;
3659
+ }
3588
3660
  this.pendingTokenRequests.set(requestId, { resolve, reject });
3589
3661
  this.post({
3590
3662
  type: CENTRIFUGE_OUTPUT_TYPE.TOKEN_REQUEST,
@@ -3805,10 +3877,24 @@ var CentrifugeWorkerTransport = class {
3805
3877
  * credentialProvider, then post the fresh token (or a serialized failure)
3806
3878
  * back to the session that issued the request. */
3807
3879
  resolveTokenRequest(requestId, kind, channel) {
3880
+ const generation = this.generation;
3881
+ const worker = this.worker;
3882
+ const port = this.port;
3883
+ const localSession = this.localSession;
3884
+ const isCurrentBackend = () => this.generation === generation && this.worker === worker && this.port === port && this.localSession === localSession;
3808
3885
  const provider = this.credentialProvider;
3809
- const value = kind === "channelToken" && provider?.getChannelToken ? provider.getChannelToken(channel ?? "") : provider?.getToken?.();
3886
+ let value;
3887
+ try {
3888
+ value = kind === "channelToken" && provider?.getChannelToken ? provider.getChannelToken(channel ?? "") : provider?.getToken?.();
3889
+ } catch (error) {
3890
+ if (isCurrentBackend()) {
3891
+ this.post({ type: CENTRIFUGE_INPUT_TYPE.TOKEN_ERROR, requestId, error: serializeError(error) });
3892
+ }
3893
+ return;
3894
+ }
3810
3895
  Promise.resolve(value).then(
3811
3896
  (token) => {
3897
+ if (!isCurrentBackend()) return;
3812
3898
  if (typeof token !== "string" || token.length === 0) {
3813
3899
  this.post({
3814
3900
  type: CENTRIFUGE_INPUT_TYPE.TOKEN_ERROR,
@@ -3820,6 +3906,7 @@ var CentrifugeWorkerTransport = class {
3820
3906
  this.post({ type: CENTRIFUGE_INPUT_TYPE.TOKEN_RESPONSE, requestId, token });
3821
3907
  },
3822
3908
  (error) => {
3909
+ if (!isCurrentBackend()) return;
3823
3910
  this.post({ type: CENTRIFUGE_INPUT_TYPE.TOKEN_ERROR, requestId, error: serializeError(error) });
3824
3911
  }
3825
3912
  );