@peerbit/shared-log 16.0.25 → 16.0.27

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/src/index.ts CHANGED
@@ -260,10 +260,14 @@ import type {
260
260
  AddedReplicationInfoMutation,
261
261
  FullReplicationInfoMutation,
262
262
  } from "./replication-info-mutation.js";
263
- import { ReplicationInfoV2ReceiveCoordinator } from "./replication-info-v2-receive.js";
263
+ import {
264
+ ReplicationInfoV2ReceiveCoordinator,
265
+ type ReplicationInfoV2ReceiveDiagnostic,
266
+ } from "./replication-info-v2-receive.js";
264
267
  import {
265
268
  type ReplicationInfoV2ConfirmationOptions,
266
269
  ReplicationInfoV2SendCoordinator,
270
+ type ReplicationInfoV2SendDiagnostic,
267
271
  } from "./replication-info-v2-send.js";
268
272
  import {
269
273
  type ReplicationStatus,
@@ -1860,6 +1864,39 @@ export type PersistedReceiptPeerReadinessUnsupportedReason =
1860
1864
  | "persisted-receipts-unsupported"
1861
1865
  | "replication-confirmation-unsupported";
1862
1866
 
1867
+ /**
1868
+ * Bounded, payload-free diagnostics for one persisted-receipt readiness
1869
+ * inspection. Session and transport identities remain opaque; revision and
1870
+ * sequence values are decimal strings so the snapshot is JSON-safe. `reason`
1871
+ * fields classify the current state; rejected-packet and disconnected-peer
1872
+ * history is deliberately not retained.
1873
+ */
1874
+ export type PersistedReceiptPeerReadinessDiagnostic = Readonly<{
1875
+ version: 1;
1876
+ log: "open" | "closed";
1877
+ session: Readonly<{
1878
+ state: "absent" | "current";
1879
+ phase?: "opening" | "open" | "departing" | "superseded";
1880
+ established: boolean;
1881
+ suspended: boolean;
1882
+ openingBarrierActive: boolean;
1883
+ replicationInfoBlocked: boolean;
1884
+ receiveCleanupGateOpen: boolean;
1885
+ }>;
1886
+ capability: Readonly<{
1887
+ state: "absent" | "observed";
1888
+ persistedReceipts: boolean;
1889
+ replicationConfirmation: boolean;
1890
+ }>;
1891
+ sender: ReplicationInfoV2SendDiagnostic;
1892
+ receiver: ReplicationInfoV2ReceiveDiagnostic;
1893
+ }>;
1894
+
1895
+ type PersistedReceiptPeerReadinessDiagnosticAttachment = Readonly<{
1896
+ /** Present only when `diagnostics: true` was requested. */
1897
+ diagnostic?: PersistedReceiptPeerReadinessDiagnostic;
1898
+ }>;
1899
+
1863
1900
  /**
1864
1901
  * Detached view of one public key's current persisted-receipt generation.
1865
1902
  * `generation` is opaque: callers may compare it for equality, but must not
@@ -1868,20 +1905,23 @@ export type PersistedReceiptPeerReadinessUnsupportedReason =
1868
1905
  * leadership and outbound confirmation can still change within a generation.
1869
1906
  */
1870
1907
  export type PersistedReceiptPeerReadiness =
1871
- | Readonly<{
1908
+ | (Readonly<{
1872
1909
  status: "ready";
1873
1910
  generation: string;
1874
- }>
1875
- | Readonly<{
1911
+ }> &
1912
+ PersistedReceiptPeerReadinessDiagnosticAttachment)
1913
+ | (Readonly<{
1876
1914
  status: "pending";
1877
1915
  reason: PersistedReceiptPeerReadinessPendingReason;
1878
1916
  generation?: string;
1879
- }>
1880
- | Readonly<{
1917
+ }> &
1918
+ PersistedReceiptPeerReadinessDiagnosticAttachment)
1919
+ | (Readonly<{
1881
1920
  status: "unsupported";
1882
1921
  reason: PersistedReceiptPeerReadinessUnsupportedReason;
1883
1922
  generation: string;
1884
- }>;
1923
+ }> &
1924
+ PersistedReceiptPeerReadinessDiagnosticAttachment);
1885
1925
 
1886
1926
  export type PersistedReceiptPeerReady = Extract<
1887
1927
  PersistedReceiptPeerReadiness,
@@ -1892,6 +1932,8 @@ export type PersistedReceiptPeerReadinessOptions<
1892
1932
  T,
1893
1933
  R extends "u32" | "u64",
1894
1934
  > = Readonly<{
1935
+ /** Include a bounded, JSON-safe diagnostic snapshot in the result. */
1936
+ diagnostics?: boolean;
1895
1937
  /** Require this peer to be a freshly planned leader for every entry. */
1896
1938
  entries?: readonly (ShallowOrFullEntry<T> | EntryReplicated<R>)[];
1897
1939
  /**
@@ -1952,6 +1994,7 @@ const MAX_PERSISTED_RECEIPT_ATTEMPT_MS = 2_000;
1952
1994
  const MAX_PERSISTED_RECEIPT_REQUESTS_GLOBAL = 8;
1953
1995
  const MAX_PERSISTED_RECEIPT_REQUESTS_PER_PEER = 2;
1954
1996
  const MAX_PERSISTED_RECEIPT_READINESS_WAITERS = 1_024;
1997
+ const MAX_PERSISTED_RECEIPT_READINESS_DIAGNOSTIC_SUMMARY_CHARS = 768;
1955
1998
  const PERSISTED_RECEIPT_INGRESS_PEER_REQUEST_CAPACITY = 16;
1956
1999
  const PERSISTED_RECEIPT_INGRESS_PEER_HASH_CAPACITY = 8_192;
1957
2000
  const PERSISTED_RECEIPT_INGRESS_PEER_REQUESTS_PER_SECOND = 8;
@@ -4087,10 +4130,15 @@ export class SharedLog<
4087
4130
  : 0),
4088
4131
  }),
4089
4132
  {
4090
- mode: new AcknowledgeDelivery({
4091
- redundancy: 1,
4092
- to: [properties.target],
4093
- }),
4133
+ // The transient rearm is only a bounded recovery hint. The ensuing
4134
+ // authenticated Full/Applied exchange is the readiness proof, so do not
4135
+ // let an ACK-delivery promise park the recovery scheduler itself.
4136
+ mode: properties.requestRemoteFullRearm
4137
+ ? new SilentDelivery({ redundancy: 1, to: [properties.target] })
4138
+ : new AcknowledgeDelivery({
4139
+ redundancy: 1,
4140
+ to: [properties.target],
4141
+ }),
4094
4142
  priority: CONVERGENCE_MESSAGE_PRIORITY,
4095
4143
  signal: properties.signal,
4096
4144
  },
@@ -4442,8 +4490,7 @@ export class SharedLog<
4442
4490
 
4443
4491
  const profile = this._logProperties?.sync?.profile;
4444
4492
  const startedAt = syncProfileStart(profile);
4445
- const mode =
4446
- resolvedRoot === fanoutService.publicKeyHash ? "root" : "node";
4493
+ const mode = resolvedRoot === fanoutService.publicKeyHash ? "root" : "node";
4447
4494
  const before = profile
4448
4495
  ? snapshotFanoutOpenMetrics(fanoutService, this.topic, resolvedRoot)
4449
4496
  : undefined;
@@ -5765,6 +5812,117 @@ export class SharedLog<
5765
5812
  });
5766
5813
  }
5767
5814
 
5815
+ private persistedReceiptReadinessDiagnostic(
5816
+ peerHash: string,
5817
+ ): PersistedReceiptPeerReadinessDiagnostic {
5818
+ const peerSession = this._peerSessions.current(peerHash);
5819
+ const receiveEpoch = this._peerSessions.receiveEpoch(peerHash);
5820
+ const capabilitySession = this._peerSyncCapabilitySessions.get(peerHash);
5821
+ const capabilities = this._peerSyncCapabilities.get(peerHash) ?? 0;
5822
+ const replicationInfoBlocked =
5823
+ this._peerSessions.isReplicationInfoBlocked(peerHash);
5824
+ const receiveCleanupGateOpen =
5825
+ this._peerSessions.isReceiveCleanupGateOpen(peerHash);
5826
+ const active = peerSession?.isActive() ?? false;
5827
+ const established =
5828
+ !this.closed &&
5829
+ peerSession?.phase === "open" &&
5830
+ active &&
5831
+ !replicationInfoBlocked &&
5832
+ receiveCleanupGateOpen;
5833
+ const session = Object.freeze({
5834
+ state: peerSession ? ("current" as const) : ("absent" as const),
5835
+ ...(peerSession ? { phase: peerSession.phase } : {}),
5836
+ established,
5837
+ suspended: !established,
5838
+ openingBarrierActive: peerSession?.openingBarrierActive ?? false,
5839
+ replicationInfoBlocked,
5840
+ receiveCleanupGateOpen,
5841
+ });
5842
+ const capabilityObserved =
5843
+ capabilitySession !== undefined &&
5844
+ this._peerSyncCapabilityTimestamps.has(peerHash);
5845
+ const capability = Object.freeze({
5846
+ state: capabilityObserved ? ("observed" as const) : ("absent" as const),
5847
+ persistedReceipts:
5848
+ (capabilities & SYNC_CAPABILITY_PERSISTED_ENTRY_RECEIPTS) !== 0,
5849
+ replicationConfirmation:
5850
+ (capabilities & SYNC_CAPABILITY_REPLICATION_INFO_V2_CONFIRM) !== 0,
5851
+ });
5852
+ return Object.freeze({
5853
+ version: 1 as const,
5854
+ log: this.closed ? ("closed" as const) : ("open" as const),
5855
+ session,
5856
+ capability,
5857
+ sender: this._v2Send.diagnosePeer({
5858
+ peerHash,
5859
+ ...(peerSession ? { peerSession } : {}),
5860
+ ...(capabilitySession === undefined
5861
+ ? {}
5862
+ : { receiverTransportSession: capabilitySession }),
5863
+ }),
5864
+ receiver: this._v2Receive.diagnosePeer({
5865
+ peerHash,
5866
+ ...(peerSession ? { peerSession } : {}),
5867
+ receiveEpoch,
5868
+ ...(capabilitySession === undefined
5869
+ ? {}
5870
+ : { senderTransportSession: capabilitySession }),
5871
+ }),
5872
+ });
5873
+ }
5874
+
5875
+ private attachPersistedReceiptReadinessDiagnostic<
5876
+ Snapshot extends PersistedReceiptPeerReadiness,
5877
+ >(
5878
+ peerHash: string,
5879
+ snapshot: Snapshot,
5880
+ enabled: boolean | undefined,
5881
+ ): Snapshot {
5882
+ if (enabled !== true) return snapshot;
5883
+ return Object.freeze({
5884
+ ...snapshot,
5885
+ diagnostic: this.persistedReceiptReadinessDiagnostic(peerHash),
5886
+ }) as Snapshot;
5887
+ }
5888
+
5889
+ private formatPersistedReceiptReadinessDiagnostic(peerHash: string): string {
5890
+ const diagnostic = this.persistedReceiptReadinessDiagnostic(peerHash);
5891
+ const { session, capability, sender, receiver } = diagnostic;
5892
+ const summary = [
5893
+ `session=${session.state}/${session.phase ?? "none"}/${
5894
+ session.established ? "established" : "suspended"
5895
+ }`,
5896
+ `capability=${capability.state}/persisted:${
5897
+ capability.persistedReceipts ? 1 : 0
5898
+ }/confirm:${capability.replicationConfirmation ? 1 : 0}`,
5899
+ `sender=${sender.state}/${sender.reason}/revision:${
5900
+ sender.currentRevision
5901
+ }/pending:${sender.pendingRevision ?? "none"}/inflight:${
5902
+ sender.inFlightSequence ?? "none"
5903
+ }@${sender.inFlightRevision ?? "none"}/applied:${
5904
+ sender.appliedRevision ?? "none"
5905
+ }/confirmation:${sender.confirmationSequence ?? "none"}@${
5906
+ sender.confirmationRevision ?? "none"
5907
+ }/waiters:${sender.confirmationWaiters}/oldest-ms:${
5908
+ sender.oldestConfirmationAgeMs ?? "none"
5909
+ }/retries:${sender.retryAttempts ?? 0}`,
5910
+ `receiver=${receiver.state}/${receiver.reason}/phase:${
5911
+ receiver.phase ?? "none"
5912
+ }/request:${receiver.requestState ?? "none"}/applied-sequence:${
5913
+ receiver.lastAppliedSequence ?? "none"
5914
+ }/retries:${receiver.requestAttempts ?? 0}/rearm:${
5915
+ receiver.remoteFullRearm
5916
+ }/rearm-attempts:${receiver.remoteFullRearmAttempts}/rearm-outstanding:${
5917
+ receiver.remoteFullRearmOutstanding
5918
+ }/rearm-global:${receiver.remoteFullRearmGlobalOutstanding}`,
5919
+ ].join(" ");
5920
+ return summary.slice(
5921
+ 0,
5922
+ MAX_PERSISTED_RECEIPT_READINESS_DIAGNOSTIC_SUMMARY_CHARS,
5923
+ );
5924
+ }
5925
+
5768
5926
  private dispatchPersistedReceiptReadinessChange(peerHash: string): void {
5769
5927
  this.events.dispatchEvent(
5770
5928
  new CustomEvent<PersistedReceiptPeerReadinessEvent>(
@@ -5856,9 +6014,7 @@ export class SharedLog<
5856
6014
  };
5857
6015
  }
5858
6016
 
5859
- private persistedReceiptPeerSession(
5860
- peerHash: string,
5861
- ):
6017
+ private persistedReceiptPeerSession(peerHash: string):
5862
6018
  | {
5863
6019
  capabilitySession: bigint;
5864
6020
  peerSession: PeerSession;
@@ -24381,6 +24537,11 @@ export class SharedLog<
24381
24537
  private nudgePersistedReceiptPeerReadiness(
24382
24538
  publicKey: PublicSignKey,
24383
24539
  signal: AbortSignal,
24540
+ rearmCurrentUnconfirmed?: {
24541
+ peerSession: PeerSession;
24542
+ receiveEpoch: object | null;
24543
+ capabilitySession: bigint;
24544
+ },
24384
24545
  ): void {
24385
24546
  if (this.closed || signal.aborted) return;
24386
24547
  const peerHash = publicKey.hashcode();
@@ -24388,8 +24549,7 @@ export class SharedLog<
24388
24549
  if (
24389
24550
  !peerSession ||
24390
24551
  peerSession.phase === "departing" ||
24391
- (peerSession.phase === "opening" &&
24392
- !peerSession.openingBarrierActive)
24552
+ (peerSession.phase === "opening" && !peerSession.openingBarrierActive)
24393
24553
  ) {
24394
24554
  // A barrier rejection deliberately leaves the current session in its
24395
24555
  // fail-closed opening phase after the barrier window has settled. Ask the
@@ -24416,11 +24576,20 @@ export class SharedLog<
24416
24576
  const receiptTarget = this.persistedReceiptPeerSession(peerHash);
24417
24577
  if (
24418
24578
  receiptTarget &&
24419
- !this._v2Send.hasCurrentStateForPeer({
24579
+ (!this._v2Send.hasCurrentStateForPeer({
24420
24580
  peerHash,
24421
24581
  peerSession: receiptTarget.peerSession,
24422
24582
  receiverTransportSession: receiptTarget.capabilitySession,
24423
- })
24583
+ }) ||
24584
+ (rearmCurrentUnconfirmed?.peerSession === receiptTarget.peerSession &&
24585
+ rearmCurrentUnconfirmed.receiveEpoch === receiptTarget.receiveEpoch &&
24586
+ rearmCurrentUnconfirmed.capabilitySession ===
24587
+ receiptTarget.capabilitySession &&
24588
+ !this._v2Send.isLatestConfirmedForPeer({
24589
+ peerHash,
24590
+ peerSession: receiptTarget.peerSession,
24591
+ receiverTransportSession: receiptTarget.capabilitySession,
24592
+ })))
24424
24593
  ) {
24425
24594
  this._v2Receive.reAdvertiseLocalCapabilityForRemoteFull({
24426
24595
  peerHash,
@@ -24446,7 +24615,18 @@ export class SharedLog<
24446
24615
  key: PublicSignKey,
24447
24616
  options: PersistedReceiptPeerReadinessOptions<T, R> = {},
24448
24617
  ): Promise<PersistedReceiptPeerReadiness> {
24449
- return this.inspectPersistedReceiptPeerReadiness(key, options);
24618
+ if (options.diagnostics !== true) {
24619
+ return this.inspectPersistedReceiptPeerReadiness(key, options);
24620
+ }
24621
+ const snapshot = await this.inspectPersistedReceiptPeerReadiness(
24622
+ key,
24623
+ options,
24624
+ );
24625
+ return this.attachPersistedReceiptReadinessDiagnostic(
24626
+ key.hashcode(),
24627
+ snapshot,
24628
+ options.diagnostics,
24629
+ );
24450
24630
  }
24451
24631
 
24452
24632
  private async inspectPersistedReceiptPeerReadiness(
@@ -24563,7 +24743,9 @@ export class SharedLog<
24563
24743
  * Wait for a public key's current (or replacement) connection generation to
24564
24744
  * become persisted-receipt ready. Transition listeners are installed before
24565
24745
  * the first asynchronous inspection, and a bounded recovery tick repairs
24566
- * missed subscriber/capability wakes without retaining stale PeerSessions.
24746
+ * missed subscriber/capability wakes without retaining stale PeerSessions. A
24747
+ * current sender gets one normal confirmation interval before an exact-
24748
+ * generation watchdog requests a bounded Full rearm.
24567
24749
  * This waiter is advisory only; the following persisted delivery remains the
24568
24750
  * operation that proves the requested remote durability quorum.
24569
24751
  */
@@ -24621,6 +24803,14 @@ export class SharedLog<
24621
24803
  let rerun = false;
24622
24804
  let recoveryTimer: ReturnType<typeof setTimeout> | undefined;
24623
24805
  let confirmationController: AbortController | undefined;
24806
+ let confirmationRecoveryTarget:
24807
+ | {
24808
+ peerSession: PeerSession;
24809
+ receiveEpoch: object | null;
24810
+ capabilitySession: bigint;
24811
+ notBefore: number;
24812
+ }
24813
+ | undefined;
24624
24814
  let lastSnapshot: PersistedReceiptPeerReadiness | undefined;
24625
24815
  const createTimeoutError = () => {
24626
24816
  const suffix = lastSnapshot
@@ -24628,8 +24818,10 @@ export class SharedLog<
24628
24818
  "reason" in lastSnapshot ? `/${lastSnapshot.reason}` : ""
24629
24819
  })`
24630
24820
  : "";
24821
+ const diagnostic =
24822
+ this.formatPersistedReceiptReadinessDiagnostic(peerHash);
24631
24823
  return new TimeoutError(
24632
- `Timeout waiting for persisted-receipt readiness from ${peerHash}${suffix}`,
24824
+ `Timeout waiting for persisted-receipt readiness from ${peerHash}${suffix}; diagnostic: ${diagnostic}`,
24633
24825
  );
24634
24826
  };
24635
24827
 
@@ -24651,6 +24843,7 @@ export class SharedLog<
24651
24843
  new AbortError("Persisted-receipt readiness generation changed"),
24652
24844
  );
24653
24845
  confirmationController = undefined;
24846
+ confirmationRecoveryTarget = undefined;
24654
24847
  operationController.abort(
24655
24848
  new AbortError("Persisted-receipt readiness wait settled"),
24656
24849
  );
@@ -24706,7 +24899,14 @@ export class SharedLog<
24706
24899
  recoveryTimer = setTimeout(() => {
24707
24900
  recoveryTimer = undefined;
24708
24901
  if (!continueWait()) return;
24709
- this.nudgePersistedReceiptPeerReadiness(key, operationSignal);
24902
+ const recoveryTarget = confirmationRecoveryTarget;
24903
+ this.nudgePersistedReceiptPeerReadiness(
24904
+ key,
24905
+ operationSignal,
24906
+ recoveryTarget && Date.now() >= recoveryTarget.notBefore
24907
+ ? recoveryTarget
24908
+ : undefined,
24909
+ );
24710
24910
  if (checkInFlight) {
24711
24911
  // Confirmation can legitimately span several recovery intervals.
24712
24912
  // Keep repairing the exact current generation without aborting its
@@ -24719,6 +24919,34 @@ export class SharedLog<
24719
24919
  }, recoveryDelayMs);
24720
24920
  recoveryTimer.unref?.();
24721
24921
  };
24922
+ const prepareConfirmationRecovery = () => {
24923
+ const target = this.persistedReceiptPeerSession(peerHash);
24924
+ if (!target) {
24925
+ confirmationRecoveryTarget = undefined;
24926
+ return undefined;
24927
+ }
24928
+ const retained = confirmationRecoveryTarget;
24929
+ if (
24930
+ retained?.peerSession !== target.peerSession ||
24931
+ retained.receiveEpoch !== target.receiveEpoch ||
24932
+ retained.capabilitySession !== target.capabilitySession
24933
+ ) {
24934
+ confirmationRecoveryTarget = {
24935
+ peerSession: target.peerSession,
24936
+ receiveEpoch: target.receiveEpoch,
24937
+ capabilitySession: target.capabilitySession,
24938
+ notBefore: Date.now() + recoveryDelayMs,
24939
+ };
24940
+ }
24941
+ const recoveryTarget = confirmationRecoveryTarget!;
24942
+ this.nudgePersistedReceiptPeerReadiness(
24943
+ key,
24944
+ operationSignal,
24945
+ Date.now() >= recoveryTarget.notBefore ? recoveryTarget : undefined,
24946
+ );
24947
+ armRecoveryTick();
24948
+ return target;
24949
+ };
24722
24950
  const runCheck = async () => {
24723
24951
  checkScheduled = false;
24724
24952
  if (!continueWait()) return;
@@ -24735,27 +24963,36 @@ export class SharedLog<
24735
24963
  );
24736
24964
  lastSnapshot = snapshot;
24737
24965
  if (!continueWait()) return;
24738
- if (rerun) return;
24739
24966
  if (snapshot.status === "ready") {
24967
+ confirmationRecoveryTarget = undefined;
24968
+ if (rerun) return;
24740
24969
  // A wake observed while the asynchronous inspection was running may
24741
24970
  // already have invalidated this snapshot. Drain that coalesced wake
24742
24971
  // before publishing readiness.
24743
- resolve(snapshot);
24972
+ resolve(
24973
+ this.attachPersistedReceiptReadinessDiagnostic(
24974
+ peerHash,
24975
+ snapshot,
24976
+ options.diagnostics,
24977
+ ),
24978
+ );
24744
24979
  return;
24745
24980
  }
24746
24981
  if (
24747
24982
  snapshot.status === "pending" &&
24748
24983
  snapshot.reason === "replication-confirmation-pending"
24749
24984
  ) {
24750
- const target = this.persistedReceiptPeerSession(peerHash);
24985
+ // Retain one watchdog deadline for the exact generation across
24986
+ // coalesced inspection wakes. A sub-interval event stream must drive,
24987
+ // rather than indefinitely postpone, the mature recovery nudge.
24988
+ const target = prepareConfirmationRecovery();
24989
+ if (rerun) return;
24751
24990
  if (target) {
24752
- // A confirmation waiter cannot create an outbound V2 stream when
24753
- // that exact session state is missing. Nudge first, then keep the
24754
- // recovery tick alive while the single confirmation wait remains.
24755
- this.nudgePersistedReceiptPeerReadiness(key, operationSignal);
24756
- armRecoveryTick();
24757
24991
  const currentConfirmationController = new AbortController();
24758
24992
  confirmationController = currentConfirmationController;
24993
+ const currentConfirmationRecoveryTarget =
24994
+ confirmationRecoveryTarget;
24995
+ let confirmationSucceeded = false;
24759
24996
  try {
24760
24997
  await this._v2Send.confirmLatestForPeer(
24761
24998
  {
@@ -24771,6 +25008,7 @@ export class SharedLog<
24771
25008
  ]),
24772
25009
  },
24773
25010
  );
25011
+ confirmationSucceeded = true;
24774
25012
  } catch (error) {
24775
25013
  if (!continueWait()) return;
24776
25014
  if (!(error instanceof AbortError)) {
@@ -24781,6 +25019,12 @@ export class SharedLog<
24781
25019
  if (confirmationController === currentConfirmationController) {
24782
25020
  confirmationController = undefined;
24783
25021
  }
25022
+ if (
25023
+ confirmationSucceeded &&
25024
+ confirmationRecoveryTarget === currentConfirmationRecoveryTarget
25025
+ ) {
25026
+ confirmationRecoveryTarget = undefined;
25027
+ }
24784
25028
  }
24785
25029
  if (!continueWait()) return;
24786
25030
  snapshot = await this.inspectPersistedReceiptPeerReadiness(
@@ -24790,13 +25034,32 @@ export class SharedLog<
24790
25034
  );
24791
25035
  lastSnapshot = snapshot;
24792
25036
  if (!continueWait()) return;
24793
- if (rerun) return;
24794
25037
  if (snapshot.status === "ready") {
24795
- resolve(snapshot);
25038
+ confirmationRecoveryTarget = undefined;
25039
+ if (rerun) return;
25040
+ resolve(
25041
+ this.attachPersistedReceiptReadinessDiagnostic(
25042
+ peerHash,
25043
+ snapshot,
25044
+ options.diagnostics,
25045
+ ),
25046
+ );
24796
25047
  return;
24797
25048
  }
25049
+ if (
25050
+ snapshot.status === "pending" &&
25051
+ snapshot.reason === "replication-confirmation-pending"
25052
+ ) {
25053
+ prepareConfirmationRecovery();
25054
+ } else {
25055
+ confirmationRecoveryTarget = undefined;
25056
+ }
25057
+ if (rerun) return;
24798
25058
  }
25059
+ } else {
25060
+ confirmationRecoveryTarget = undefined;
24799
25061
  }
25062
+ if (rerun) return;
24800
25063
  if (!continueWait()) return;
24801
25064
  this.nudgePersistedReceiptPeerReadiness(key, operationSignal);
24802
25065
  } catch (error) {