cross-tab-worker-databus 0.20.68 → 0.20.71
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 +16 -0
- package/dist/centrifuge.d.ts +2 -0
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +5 -1
- package/dist/centrifuge.js.map +2 -2
- package/dist/{chunk-X6JTPZYW.js → chunk-D2SIT473.js} +32 -5
- package/dist/chunk-D2SIT473.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +35 -4
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +73 -4
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/cluster.d.ts +2 -0
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +9 -0
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/replay-persistence.d.ts +2 -0
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +43 -1
- package/dist/index.js.map +2 -2
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.d.ts.map +1 -1
- package/docs/roadmap.md +13 -1
- package/docs/zh/roadmap.md +13 -1
- package/package.json +1 -1
- package/dist/chunk-X6JTPZYW.js.map +0 -7
package/dist/cjs/index.cjs
CHANGED
|
@@ -425,6 +425,7 @@ var WorkerClusterRuntime = class {
|
|
|
425
425
|
this.workerId = options.workerId ?? `worker-${this.tabId}-${this.environment.randomId()}`;
|
|
426
426
|
const now = this.environment.now();
|
|
427
427
|
this.currentRecord = {
|
|
428
|
+
protocolVersion: CLUSTER_PROTOCOL_VERSION,
|
|
428
429
|
workerId: this.workerId,
|
|
429
430
|
tabId: this.tabId,
|
|
430
431
|
load: 0,
|
|
@@ -746,16 +747,18 @@ var WorkerClusterRuntime = class {
|
|
|
746
747
|
}
|
|
747
748
|
/** Read-only snapshot of the cluster state (workers, routes, assignments). */
|
|
748
749
|
getSnapshot() {
|
|
750
|
+
const workers = this.storage ? this.readWorkers() : [{ ...this.currentRecord }];
|
|
749
751
|
const routes = this.storage ? readAllByPrefix(this.storage, this.routePrefix).map(({ value }) => ({
|
|
750
752
|
...value,
|
|
751
753
|
topic: this.knownTopics.get(value.topicKey) ?? null
|
|
752
754
|
})) : [];
|
|
753
755
|
return {
|
|
754
756
|
protocolVersion: CLUSTER_PROTOCOL_VERSION,
|
|
757
|
+
peerProtocolVersions: Object.fromEntries(workers.map((worker) => [worker.workerId, worker.protocolVersion ?? null])),
|
|
755
758
|
coordinated: Boolean(this.storage && this.channel),
|
|
756
759
|
suspended: this.suspended,
|
|
757
760
|
currentWorker: { ...this.currentRecord },
|
|
758
|
-
workers:
|
|
761
|
+
workers: workers.map((worker) => ({ ...worker })),
|
|
759
762
|
routes,
|
|
760
763
|
subscribedTopics: Array.from(this.subscribedTopics),
|
|
761
764
|
assignedTopics: Array.from(this.assignedTopics.values()),
|
|
@@ -1410,6 +1413,7 @@ function roundMs(value) {
|
|
|
1410
1413
|
|
|
1411
1414
|
// src/core/data-bus.ts
|
|
1412
1415
|
var PUBLICATION_EVENT = "DATABUS_PUBLICATION";
|
|
1416
|
+
var SDK_VERSION = "0.20.69";
|
|
1413
1417
|
var DEFAULT_REPLAY_MAX_PER_TOPIC = 100;
|
|
1414
1418
|
var PersistenceRetryCancelledError = class extends Error {
|
|
1415
1419
|
constructor() {
|
|
@@ -1438,6 +1442,8 @@ var CrossTabDataBus = class {
|
|
|
1438
1442
|
persistenceRetryMaxAttempts;
|
|
1439
1443
|
persistenceRetryBackoffMs;
|
|
1440
1444
|
persistenceRetryGeneration = 0;
|
|
1445
|
+
pendingReplayPersistence = [];
|
|
1446
|
+
replayPersistenceFlushScheduled = false;
|
|
1441
1447
|
replayHydration;
|
|
1442
1448
|
// Retention cleanup is coalesced so a burst of publications does not issue
|
|
1443
1449
|
// one IndexedDB read/write transaction per message. The newest cutoff wins.
|
|
@@ -1913,15 +1919,20 @@ var CrossTabDataBus = class {
|
|
|
1913
1919
|
getDiagnostics() {
|
|
1914
1920
|
let messages = 0;
|
|
1915
1921
|
if (this.replayBuffers) for (const buffer of this.replayBuffers.values()) messages += buffer.length;
|
|
1922
|
+
const cluster = this.cluster.getSnapshot();
|
|
1923
|
+
const unknownMessages = this.cluster.getUnknownMessageStats();
|
|
1924
|
+
const transport = this.transport;
|
|
1916
1925
|
return {
|
|
1917
1926
|
status: this.status,
|
|
1927
|
+
sdkVersion: SDK_VERSION,
|
|
1918
1928
|
started: this.started,
|
|
1919
1929
|
transportReady: this.transportReady,
|
|
1920
1930
|
recovery: this.getRecoveryStats(),
|
|
1921
1931
|
dedup: this.getDedupStats(),
|
|
1922
1932
|
replay: { enabled: Boolean(this.replayBuffers), topics: this.replayBuffers?.size ?? 0, messages },
|
|
1923
|
-
protocol: { version:
|
|
1924
|
-
|
|
1933
|
+
protocol: { version: cluster.protocolVersion, unknownMessages: unknownMessages.count, lastUnknownMessageType: unknownMessages.lastType, peers: cluster.peerProtocolVersions },
|
|
1934
|
+
transport: { name: transport.diagnosticsName ?? transport.constructor.name, backend: transport.diagnosticsBackend ?? null },
|
|
1935
|
+
cluster
|
|
1925
1936
|
};
|
|
1926
1937
|
}
|
|
1927
1938
|
/**
|
|
@@ -2067,12 +2078,28 @@ var CrossTabDataBus = class {
|
|
|
2067
2078
|
}
|
|
2068
2079
|
}
|
|
2069
2080
|
if (this.replayPersistence) {
|
|
2070
|
-
|
|
2081
|
+
if (this.replayPersistence.appendBatch) {
|
|
2082
|
+
this.pendingReplayPersistence.push(storedMessage);
|
|
2083
|
+
this.scheduleReplayPersistenceFlush();
|
|
2084
|
+
} else {
|
|
2085
|
+
void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
|
|
2086
|
+
}
|
|
2071
2087
|
if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
|
|
2072
2088
|
this.scheduleReplayRetentionCleanup(this.now() - this.replayRetentionMs);
|
|
2073
2089
|
}
|
|
2074
2090
|
}
|
|
2075
2091
|
}
|
|
2092
|
+
scheduleReplayPersistenceFlush() {
|
|
2093
|
+
if (this.replayPersistenceFlushScheduled) return;
|
|
2094
|
+
this.replayPersistenceFlushScheduled = true;
|
|
2095
|
+
queueMicrotask(() => {
|
|
2096
|
+
this.replayPersistenceFlushScheduled = false;
|
|
2097
|
+
const batch = this.pendingReplayPersistence.splice(0);
|
|
2098
|
+
if (batch.length === 0 || !this.replayPersistence) return;
|
|
2099
|
+
const operation = this.replayPersistence.appendBatch ? () => this.replayPersistence.appendBatch(batch) : () => Promise.all(batch.map((message) => this.replayPersistence.append(message))).then(() => void 0);
|
|
2100
|
+
void this.withPersistenceRetry("append", operation).catch((error) => this.reportPersistenceError(error));
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2076
2103
|
async hydrateReplay() {
|
|
2077
2104
|
if (!this.replayBuffers || !this.replayPersistence) {
|
|
2078
2105
|
return;
|
|
@@ -2476,6 +2503,46 @@ function createIndexedDbReplayPersistence(options) {
|
|
|
2476
2503
|
});
|
|
2477
2504
|
});
|
|
2478
2505
|
},
|
|
2506
|
+
appendBatch(messages) {
|
|
2507
|
+
if (messages.length === 0) return Promise.resolve();
|
|
2508
|
+
return serializeMutation(async () => {
|
|
2509
|
+
const db = await open();
|
|
2510
|
+
await new Promise((resolve, reject) => {
|
|
2511
|
+
let transaction;
|
|
2512
|
+
try {
|
|
2513
|
+
transaction = db.transaction(storeName, "readwrite");
|
|
2514
|
+
} catch (error) {
|
|
2515
|
+
invalidate(db);
|
|
2516
|
+
reject(error);
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
const store = transaction.objectStore(storeName);
|
|
2520
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2521
|
+
for (const message of messages) grouped.set(message.topic, [...grouped.get(message.topic) ?? [], message]);
|
|
2522
|
+
for (const [topic, topicMessages] of grouped) {
|
|
2523
|
+
const request = store.get(topic);
|
|
2524
|
+
request.onsuccess = () => {
|
|
2525
|
+
let history = (request.result?.messages ?? []).concat(topicMessages);
|
|
2526
|
+
if (pruneStrategy !== "count" && retentionMs !== void 0) {
|
|
2527
|
+
const cutoff = Date.now() - retentionMs;
|
|
2528
|
+
history = history.filter((item) => item.timestamp === void 0 || item.timestamp >= cutoff);
|
|
2529
|
+
}
|
|
2530
|
+
if (pruneStrategy !== "age") history = history.slice(-maxPerTopic);
|
|
2531
|
+
store.put({ topic, messages: history });
|
|
2532
|
+
};
|
|
2533
|
+
request.onerror = () => {
|
|
2534
|
+
invalidate(db);
|
|
2535
|
+
reject(request.error ?? new Error("Failed to read replay history."));
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
transaction.oncomplete = () => resolve();
|
|
2539
|
+
transaction.onerror = () => {
|
|
2540
|
+
invalidate(db);
|
|
2541
|
+
reject(transaction.error ?? new Error("Failed to persist replay history batch."));
|
|
2542
|
+
};
|
|
2543
|
+
});
|
|
2544
|
+
});
|
|
2545
|
+
},
|
|
2479
2546
|
clear() {
|
|
2480
2547
|
return serializeMutation(async () => {
|
|
2481
2548
|
const db = await open();
|
|
@@ -2582,6 +2649,8 @@ var WebSocketTransport = class {
|
|
|
2582
2649
|
constructor(connection) {
|
|
2583
2650
|
this.connection = connection;
|
|
2584
2651
|
}
|
|
2652
|
+
diagnosticsName = "websocket";
|
|
2653
|
+
diagnosticsBackend = "native-websocket";
|
|
2585
2654
|
socket = null;
|
|
2586
2655
|
handlers = null;
|
|
2587
2656
|
subscribedTopics = /* @__PURE__ */ new Set();
|