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.
@@ -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: this.readWorkers().map((worker) => ({ ...worker })),
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: this.cluster.getSnapshot().protocolVersion, unknownMessages: this.cluster.getUnknownMessageStats().count, lastUnknownMessageType: this.cluster.getUnknownMessageStats().lastType },
1924
- cluster: this.cluster.getSnapshot()
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
- void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
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();