cross-tab-worker-databus 0.20.88 → 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.
- package/CHANGELOG.md +13 -0
- package/dist/centrifuge-session.d.ts +1 -0
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +55 -8
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +38 -6
- package/dist/centrifuge.shared.worker.js.map +2 -2
- package/dist/centrifuge.worker.js +38 -6
- package/dist/centrifuge.worker.js.map +2 -2
- package/dist/{chunk-4UOLOWOD.js → chunk-77BQELW4.js} +20 -13
- package/dist/{chunk-4UOLOWOD.js.map → chunk-77BQELW4.js.map} +2 -2
- package/dist/cjs/centrifuge.cjs +73 -19
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +27 -14
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/replay-manager.d.ts.map +1 -1
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +2 -2
- package/dist/websocket.d.ts.map +1 -1
- package/docs/api.md +2 -2
- package/docs/architecture.md +4 -3
- package/docs/capabilities.md +1 -1
- package/docs/configuration.md +1 -1
- package/docs/roadmap.md +12 -1
- package/docs/zh/api.md +2 -2
- package/docs/zh/architecture.md +4 -3
- package/docs/zh/capabilities.md +1 -1
- package/docs/zh/configuration.md +1 -1
- package/docs/zh/roadmap.md +12 -1
- package/package.json +1 -1
package/dist/cjs/centrifuge.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
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;
|
|
@@ -2566,8 +2573,8 @@ var CrossTabDataBus = class {
|
|
|
2566
2573
|
this.suspended = false;
|
|
2567
2574
|
this.activeConfig = config;
|
|
2568
2575
|
this.resetFailureState();
|
|
2569
|
-
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.START });
|
|
2570
2576
|
this.trace.start();
|
|
2577
|
+
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.START });
|
|
2571
2578
|
this.startDedupSweep();
|
|
2572
2579
|
this.replayManager.start();
|
|
2573
2580
|
this.updateStatus(WORKER_STATUS.CONNECTING);
|
|
@@ -2927,7 +2934,7 @@ var CrossTabDataBus = class {
|
|
|
2927
2934
|
getHealthSummary() {
|
|
2928
2935
|
const transport = this.transport;
|
|
2929
2936
|
const transportDown = this.status !== WORKER_STATUS.CONNECTED;
|
|
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;
|
|
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;
|
|
2931
2938
|
return {
|
|
2932
2939
|
healthy: state === HEALTH_STATE.HEALTHY,
|
|
2933
2940
|
state,
|
|
@@ -2998,7 +3005,7 @@ var CrossTabDataBus = class {
|
|
|
2998
3005
|
this.canceledQueuedStartToken = this.queuedStartToken;
|
|
2999
3006
|
this.queuedStart = null;
|
|
3000
3007
|
}
|
|
3001
|
-
if (this.stopPromise) return this.stopPromise;
|
|
3008
|
+
if (this.stopPromise && this.stopping) return this.stopPromise;
|
|
3002
3009
|
if (!this.started && !this.startPromise && !this.pendingStop && !this.transportReady) {
|
|
3003
3010
|
return Promise.resolve();
|
|
3004
3011
|
}
|
|
@@ -3242,8 +3249,8 @@ var CrossTabDataBus = class {
|
|
|
3242
3249
|
* pageshow path and explicit start() must run this so an explicit resume
|
|
3243
3250
|
* cannot leave trace metrics and periodic cleanup timers permanently off. */
|
|
3244
3251
|
resumeSuspendedResources() {
|
|
3245
|
-
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.RESUME });
|
|
3246
3252
|
this.trace.start();
|
|
3253
|
+
this.trace.event({ type: TRACE_EVENT_TYPE.LIFECYCLE, action: TRACE_LIFECYCLE_ACTION.RESUME });
|
|
3247
3254
|
this.startDedupSweep();
|
|
3248
3255
|
this.replayManager.start();
|
|
3249
3256
|
}
|
|
@@ -3474,6 +3481,10 @@ var CentrifugeSession = class {
|
|
|
3474
3481
|
transferable = false;
|
|
3475
3482
|
tokenBridge = false;
|
|
3476
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;
|
|
3477
3488
|
pendingTokenRequests = /* @__PURE__ */ new Map();
|
|
3478
3489
|
/** Dispatch an incoming Worker message to the matching operation.
|
|
3479
3490
|
* Unknown message types are ignored rather than thrown, so a future protocol
|
|
@@ -3509,6 +3520,7 @@ var CentrifugeSession = class {
|
|
|
3509
3520
|
/** Create the Centrifuge client, wire up lifecycle listeners, and connect. */
|
|
3510
3521
|
initialize(url, config, transferable, tokenBridge) {
|
|
3511
3522
|
if (this.client) return;
|
|
3523
|
+
const lifecycle = ++this.lifecycle;
|
|
3512
3524
|
this.transferable = transferable;
|
|
3513
3525
|
this.tokenBridge = tokenBridge;
|
|
3514
3526
|
const clientOptions = tokenBridge ? {
|
|
@@ -3522,12 +3534,23 @@ var CentrifugeSession = class {
|
|
|
3522
3534
|
const client = new import_centrifuge.Centrifuge(url, clientOptions);
|
|
3523
3535
|
this.client = client;
|
|
3524
3536
|
client.on("state", (context) => {
|
|
3537
|
+
if (this.lifecycle !== lifecycle) return;
|
|
3525
3538
|
this.post({ type: CENTRIFUGE_OUTPUT_TYPE.STATUS, status: normalizeStatus(context.newState) });
|
|
3526
3539
|
});
|
|
3527
|
-
client.on("connected", () =>
|
|
3528
|
-
|
|
3529
|
-
|
|
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
|
+
});
|
|
3530
3552
|
client.on("publication", (context) => {
|
|
3553
|
+
if (this.lifecycle !== lifecycle) return;
|
|
3531
3554
|
const topic = context.channel || getPayloadTopic(context.data);
|
|
3532
3555
|
if (!topic || this.subscriptions.has(topic)) return;
|
|
3533
3556
|
this.postPublication(topic, context.data);
|
|
@@ -3540,6 +3563,7 @@ var CentrifugeSession = class {
|
|
|
3540
3563
|
* avoiding the removeAllListeners + re-on churn on every duplicate message. */
|
|
3541
3564
|
subscribe(topic) {
|
|
3542
3565
|
if (!this.client) return this.postError(new Error("Centrifuge client is not initialized."));
|
|
3566
|
+
const lifecycle = this.lifecycle;
|
|
3543
3567
|
const existing = this.subscriptions.get(topic);
|
|
3544
3568
|
if (existing) {
|
|
3545
3569
|
existing.subscribe();
|
|
@@ -3552,10 +3576,16 @@ var CentrifugeSession = class {
|
|
|
3552
3576
|
subscription.removeAllListeners("unsubscribed");
|
|
3553
3577
|
this.subscriptions.set(topic, subscription);
|
|
3554
3578
|
subscription.on("publication", (context) => {
|
|
3579
|
+
if (this.lifecycle !== lifecycle) return;
|
|
3555
3580
|
this.postPublication(topic, context.data);
|
|
3556
3581
|
});
|
|
3557
|
-
subscription.on("error", (context) =>
|
|
3558
|
-
|
|
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
|
+
});
|
|
3559
3589
|
subscription.subscribe();
|
|
3560
3590
|
}
|
|
3561
3591
|
/** Unsubscribe from a Centrifuge channel and clean up the local reference.
|
|
@@ -3573,13 +3603,16 @@ var CentrifugeSession = class {
|
|
|
3573
3603
|
/** Publish a message to the Centrifuge channel. */
|
|
3574
3604
|
publish(topic, data, messageId, timestamp) {
|
|
3575
3605
|
if (!this.client) return this.postError(new Error("Centrifuge client is not initialized."));
|
|
3606
|
+
const lifecycle = this.lifecycle;
|
|
3576
3607
|
const hasMetadata = messageId !== void 0 || timestamp !== void 0;
|
|
3577
3608
|
const payload = hasMetadata ? {
|
|
3578
3609
|
data,
|
|
3579
3610
|
...messageId === void 0 ? {} : { messageId },
|
|
3580
3611
|
...timestamp === void 0 ? {} : { timestamp }
|
|
3581
3612
|
} : data;
|
|
3582
|
-
void this.client.publish(topic, payload).catch((error) =>
|
|
3613
|
+
void this.client.publish(topic, payload).catch((error) => {
|
|
3614
|
+
if (this.lifecycle === lifecycle) this.postError(error);
|
|
3615
|
+
});
|
|
3583
3616
|
}
|
|
3584
3617
|
/** Forward a publication to the transport. Binary payloads take the
|
|
3585
3618
|
* zero-copy `MESSAGE_BIN` path when `transferable` is enabled; everything
|
|
@@ -3602,6 +3635,7 @@ var CentrifugeSession = class {
|
|
|
3602
3635
|
}
|
|
3603
3636
|
/** Disconnect the client and clear all subscriptions. */
|
|
3604
3637
|
stop() {
|
|
3638
|
+
this.lifecycle += 1;
|
|
3605
3639
|
for (const [, pending] of this.pendingTokenRequests) {
|
|
3606
3640
|
pending.reject(new Error("Centrifuge session stopped before the credential was resolved."));
|
|
3607
3641
|
}
|
|
@@ -3615,9 +3649,14 @@ var CentrifugeSession = class {
|
|
|
3615
3649
|
* Used as Centrifuge's `getToken` / `getChannelToken` when token bridging is
|
|
3616
3650
|
* enabled; resolved or rejected by a matching TOKEN_RESPONSE / TOKEN_ERROR. */
|
|
3617
3651
|
requestToken(kind, channel) {
|
|
3652
|
+
const lifecycle = this.lifecycle;
|
|
3618
3653
|
const requestId = this.nextRequestId;
|
|
3619
3654
|
this.nextRequestId += 1;
|
|
3620
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
|
+
}
|
|
3621
3660
|
this.pendingTokenRequests.set(requestId, { resolve, reject });
|
|
3622
3661
|
this.post({
|
|
3623
3662
|
type: CENTRIFUGE_OUTPUT_TYPE.TOKEN_REQUEST,
|
|
@@ -3838,10 +3877,24 @@ var CentrifugeWorkerTransport = class {
|
|
|
3838
3877
|
* credentialProvider, then post the fresh token (or a serialized failure)
|
|
3839
3878
|
* back to the session that issued the request. */
|
|
3840
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;
|
|
3841
3885
|
const provider = this.credentialProvider;
|
|
3842
|
-
|
|
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
|
+
}
|
|
3843
3895
|
Promise.resolve(value).then(
|
|
3844
3896
|
(token) => {
|
|
3897
|
+
if (!isCurrentBackend()) return;
|
|
3845
3898
|
if (typeof token !== "string" || token.length === 0) {
|
|
3846
3899
|
this.post({
|
|
3847
3900
|
type: CENTRIFUGE_INPUT_TYPE.TOKEN_ERROR,
|
|
@@ -3853,6 +3906,7 @@ var CentrifugeWorkerTransport = class {
|
|
|
3853
3906
|
this.post({ type: CENTRIFUGE_INPUT_TYPE.TOKEN_RESPONSE, requestId, token });
|
|
3854
3907
|
},
|
|
3855
3908
|
(error) => {
|
|
3909
|
+
if (!isCurrentBackend()) return;
|
|
3856
3910
|
this.post({ type: CENTRIFUGE_INPUT_TYPE.TOKEN_ERROR, requestId, error: serializeError(error) });
|
|
3857
3911
|
}
|
|
3858
3912
|
);
|