@peerbit/shared-log 13.2.36 → 15.0.0

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
@@ -144,6 +144,7 @@ import {
144
144
  debouncedAccumulatorMap,
145
145
  } from "./debounce.js";
146
146
  import {
147
+ CompatibilityModeRetiredError,
147
148
  NativeDurableCommitError,
148
149
  NoPeersError,
149
150
  isNotStartedError,
@@ -248,6 +249,10 @@ import {
248
249
  type ReplicationDomain,
249
250
  type ReplicationDomainConstructor,
250
251
  } from "./replication-domain.js";
252
+ import type {
253
+ AddedReplicationInfoMutation,
254
+ FullReplicationInfoMutation,
255
+ } from "./replication-info-mutation.js";
251
256
  import { ReplicationInfoV2ReceiveCoordinator } from "./replication-info-v2-receive.js";
252
257
  import { ReplicationInfoV2SendCoordinator } from "./replication-info-v2-send.js";
253
258
  import {
@@ -428,6 +433,7 @@ export {
428
433
  EntryReplicatedU32,
429
434
  EntryReplicatedU64,
430
435
  type CoverRange,
436
+ CompatibilityModeRetiredError,
431
437
  NativeDurableCommitError,
432
438
  NoPeersError,
433
439
  };
@@ -1248,7 +1254,6 @@ export type SharedLogOptions<
1248
1254
  waitForPruneDelay?: number;
1249
1255
  distributionDebounceTime?: number;
1250
1256
  strictFullReplicaFallback?: boolean;
1251
- compatibility?: number;
1252
1257
  domain?: ReplicationDomainConstructor<D>;
1253
1258
  eagerBlocks?: EagerBlocksSetting;
1254
1259
  fanout?: SharedLogFanoutOptions;
@@ -3760,7 +3765,10 @@ export class SharedLog<
3760
3765
  }
3761
3766
 
3762
3767
  get compatibility(): number | undefined {
3763
- return this._logProperties?.compatibility;
3768
+ // B12: the open option was removed and any defined value rejects at
3769
+ // open(); this is permanently undefined and dies with the residual
3770
+ // gates in a later cleanup stage.
3771
+ return (this._logProperties as any)?.compatibility;
3764
3772
  }
3765
3773
 
3766
3774
  /**
@@ -5710,7 +5718,7 @@ export class SharedLog<
5710
5718
  mergeSegments?: boolean;
5711
5719
  rebalance?: boolean;
5712
5720
  announce?: (
5713
- msg: AddedReplicationSegmentMessage | AllReplicatingSegmentsMessage,
5721
+ msg: AddedReplicationInfoMutation | FullReplicationInfoMutation,
5714
5722
  ) => void;
5715
5723
  } = {},
5716
5724
  replicationOwnershipLifecycleController = this.captureReplicationOwnershipLifecycle(),
@@ -5946,9 +5954,11 @@ export class SharedLog<
5946
5954
 
5947
5955
  if (confirmedPreliminaryRemovals.length > 0) {
5948
5956
  await this._announcements.sendReplicationAnnouncement(
5949
- new StoppedReplicating({
5950
- segmentIds: confirmedPreliminaryRemovals.map((x) => x.id),
5951
- }),
5957
+ {
5958
+ stopped: {
5959
+ segmentIds: confirmedPreliminaryRemovals.map((x) => x.id),
5960
+ },
5961
+ },
5952
5962
  replicationOwnershipLifecycleController,
5953
5963
  );
5954
5964
  this.throwIfReplicationOwnershipLifecycleInactive(
@@ -5973,7 +5983,7 @@ export class SharedLog<
5973
5983
  );
5974
5984
  this.validatePersistedReplicationRangeSnapshot(segments);
5975
5985
  await this._announcements.sendReplicationAnnouncement(
5976
- new AllReplicatingSegmentsMessage({ segments }),
5986
+ { full: { segments } },
5977
5987
  replicationOwnershipLifecycleController,
5978
5988
  );
5979
5989
  } catch (error) {
@@ -6029,7 +6039,7 @@ export class SharedLog<
6029
6039
  rebalance?: boolean;
6030
6040
  mergeSegments?: boolean;
6031
6041
  announce?: (
6032
- msg: AllReplicatingSegmentsMessage | AddedReplicationSegmentMessage,
6042
+ msg: FullReplicationInfoMutation | AddedReplicationInfoMutation,
6033
6043
  ) => void;
6034
6044
  },
6035
6045
  ) {
@@ -6270,7 +6280,7 @@ export class SharedLog<
6270
6280
  }
6271
6281
  try {
6272
6282
  await this._announcements.sendReplicationAnnouncement(
6273
- new StoppedReplicating({ segmentIds: removedSegmentIds }),
6283
+ { stopped: { segmentIds: removedSegmentIds } },
6274
6284
  replicationOwnershipLifecycleController,
6275
6285
  );
6276
6286
  this.throwIfReplicationOwnershipLifecycleInactive(
@@ -6567,7 +6577,7 @@ export class SharedLog<
6567
6577
  if (isMe && !ownerHasRanges) {
6568
6578
  try {
6569
6579
  await this._announcements.sendReplicationAnnouncement(
6570
- new AllReplicatingSegmentsMessage({ segments: [] }),
6580
+ { full: { segments: [] } },
6571
6581
  replicationOwnershipLifecycleController,
6572
6582
  );
6573
6583
  } catch (error) {
@@ -7490,7 +7500,7 @@ export class SharedLog<
7490
7500
  rebalance?: boolean;
7491
7501
  shouldApply?: () => boolean;
7492
7502
  announce?: (
7493
- msg: AllReplicatingSegmentsMessage | AddedReplicationSegmentMessage,
7503
+ msg: FullReplicationInfoMutation | AddedReplicationInfoMutation,
7494
7504
  ) => void;
7495
7505
  } = {},
7496
7506
  replicationOwnershipLifecycleController = this.captureReplicationOwnershipLifecycle(),
@@ -7537,7 +7547,7 @@ export class SharedLog<
7537
7547
  );
7538
7548
  this.validatePersistedReplicationRangeSnapshot(segments);
7539
7549
  await this._announcements.sendReplicationAnnouncement(
7540
- new AllReplicatingSegmentsMessage({ segments }),
7550
+ { full: { segments } },
7541
7551
  replicationOwnershipLifecycleController,
7542
7552
  );
7543
7553
  } catch (error) {
@@ -7588,17 +7598,17 @@ export class SharedLog<
7588
7598
  }
7589
7599
 
7590
7600
  let message:
7591
- | AllReplicatingSegmentsMessage
7592
- | AddedReplicationSegmentMessage
7601
+ | FullReplicationInfoMutation
7602
+ | AddedReplicationInfoMutation
7593
7603
  | undefined = undefined;
7594
7604
  if (options.reset) {
7595
- message = new AllReplicatingSegmentsMessage({
7596
- segments: added.map((x) => x.range.toReplicationRange()),
7597
- });
7605
+ message = {
7606
+ full: { segments: added.map((x) => x.range.toReplicationRange()) },
7607
+ };
7598
7608
  } else {
7599
- message = new AddedReplicationSegmentMessage({
7600
- segments: added.map((x) => x.range.toReplicationRange()),
7601
- });
7609
+ message = {
7610
+ added: { segments: added.map((x) => x.range.toReplicationRange()) },
7611
+ };
7602
7612
  }
7603
7613
  if (options.announce) {
7604
7614
  return options.announce(message);
@@ -14166,6 +14176,17 @@ export class SharedLog<
14166
14176
  }
14167
14177
 
14168
14178
  async open(options?: Args<T, D, R>): Promise<void> {
14179
+ // B12: replication-info network compatibility modes are retired. Read the
14180
+ // RAW argument value (the option no longer exists on the type) so untyped
14181
+ // JS callers cannot smuggle a value past the removed field, and reject
14182
+ // ANY defined value — including 10, which previously behaved like the
14183
+ // default — BEFORE any open-time side effect (rpc.open, index/native
14184
+ // setup, domain resolution, synchronizer creation, subscription setup).
14185
+ // An explicitly-present `undefined` stays accepted.
14186
+ const rawCompatibility = (options as any)?.compatibility;
14187
+ if (rawCompatibility !== undefined) {
14188
+ throw new CompatibilityModeRetiredError(rawCompatibility);
14189
+ }
14169
14190
  this.ensureNativeDurabilityRuntimeState();
14170
14191
  this._nativeStrictDurableTransactionsClosing = false;
14171
14192
  this._replicationRangeMutationsClosing = false;
@@ -14227,7 +14248,8 @@ export class SharedLog<
14227
14248
  this.domain = options?.domain
14228
14249
  ? (options.domain(this) as unknown as D)
14229
14250
  : (createReplicationDomainHash(
14230
- options?.compatibility !== undefined && options.compatibility < 10
14251
+ (options as any)?.compatibility !== undefined &&
14252
+ (options as any).compatibility < 10
14231
14253
  ? "u32"
14232
14254
  : "u64",
14233
14255
  )(this) as unknown as D);
@@ -14837,7 +14859,7 @@ export class SharedLog<
14837
14859
  sendOptions?: { priority?: number; signal?: AbortSignal },
14838
14860
  ) => this.trySendFusedRawExchangeHeads(hashes, to, sendOptions),
14839
14861
  warn,
14840
- compatibility: this._logProperties?.compatibility,
14862
+ compatibility: (this._logProperties as any)?.compatibility,
14841
14863
  resolution: this.domain.resolution,
14842
14864
  sync: options?.sync,
14843
14865
  syncronizer: options?.syncronizer,
@@ -20540,7 +20562,7 @@ export class SharedLog<
20540
20562
  throwIfInactive();
20541
20563
 
20542
20564
  if (options?.replicate) {
20543
- let messageToSend: AddedReplicationSegmentMessage | undefined = undefined;
20565
+ let messageToSend: AddedReplicationInfoMutation | undefined = undefined;
20544
20566
 
20545
20567
  if (assumeSynced) {
20546
20568
  throwIfInactive();
@@ -20568,14 +20590,14 @@ export class SharedLog<
20568
20590
  // in one large message instead
20569
20591
  announce: (msg) => {
20570
20592
  throwIfInactive();
20571
- if (msg instanceof AllReplicatingSegmentsMessage) {
20593
+ if (!("added" in msg)) {
20572
20594
  throw new Error("Unexpected");
20573
20595
  }
20574
20596
 
20575
20597
  if (messageToSend) {
20576
20598
  // merge segments to make it into one messages
20577
- for (const segment of msg.segments) {
20578
- messageToSend.segments.push(segment);
20599
+ for (const segment of msg.added.segments) {
20600
+ messageToSend.added.segments.push(segment);
20579
20601
  }
20580
20602
  } else {
20581
20603
  messageToSend = msg;
@@ -10,6 +10,7 @@ import { TimeoutError, debounceFixedInterval } from "@peerbit/time";
10
10
  import { isNotStartedError } from "./errors.js";
11
11
  import type { TransportMessage } from "./message.js";
12
12
  import type { ReplicationRangeIndexable } from "./ranges.js";
13
+ import type { ReplicationInfoMutation } from "./replication-info-mutation.js";
13
14
  import {
14
15
  AddedReplicationSegmentMessage,
15
16
  AllReplicatingSegmentsMessage,
@@ -206,14 +207,34 @@ export type ReplicationAnnouncementDeps<R extends "u32" | "u64"> = {
206
207
  queueCurrentReplicationStateAnnouncementRetry: (error: unknown) => boolean;
207
208
  // V2 is always current. Legacy publication is enabled only for an explicit
208
209
  // compatibility open and retains its exact rejection/retry semantics there.
209
- enqueueReplicationInfoV2: (message: LegacyReplicationInfoMessage) => void;
210
+ enqueueReplicationInfoV2: (mutation: ReplicationInfoMutation) => void;
210
211
  isLegacyReplicationInfoEnabled: () => boolean;
211
212
  };
212
213
 
213
- type LegacyReplicationInfoMessage =
214
+ /**
215
+ * The dormant legacy tail still broadcasts the retired wire classes when an
216
+ * explicit compatibility open enabled it (impossible since B12 stage 1; the
217
+ * tail is deleted in stage 3). Materialize the frame from the neutral
218
+ * mutation locally so the tail keeps its exact byte semantics until then.
219
+ */
220
+ const toLegacyReplicationInfoFrame = (
221
+ mutation: ReplicationInfoMutation,
222
+ ):
214
223
  | AllReplicatingSegmentsMessage
215
224
  | AddedReplicationSegmentMessage
216
- | StoppedReplicating;
225
+ | StoppedReplicating => {
226
+ if ("full" in mutation) {
227
+ return new AllReplicatingSegmentsMessage({
228
+ segments: mutation.full.segments,
229
+ });
230
+ }
231
+ if ("added" in mutation) {
232
+ return new AddedReplicationSegmentMessage({
233
+ segments: mutation.added.segments,
234
+ });
235
+ }
236
+ return new StoppedReplicating({ segmentIds: mutation.stopped.segmentIds });
237
+ };
217
238
 
218
239
  export class ReplicationAnnouncementCoordinator<R extends "u32" | "u64"> {
219
240
  replicationAnnouncementRetryDebounced:
@@ -627,7 +648,7 @@ export class ReplicationAnnouncementCoordinator<R extends "u32" | "u64"> {
627
648
  }
628
649
 
629
650
  async sendReplicationAnnouncement(
630
- message: LegacyReplicationInfoMessage,
651
+ mutation: ReplicationInfoMutation,
631
652
  ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle(),
632
653
  options?: { shouldSend?: () => boolean },
633
654
  ): Promise<void> {
@@ -652,12 +673,12 @@ export class ReplicationAnnouncementCoordinator<R extends "u32" | "u64"> {
652
673
  // after that stale send settles.
653
674
  this.rotateAnnouncementSession();
654
675
  this.advanceCurrentReplicationStateAnnouncementRepairGeneration();
655
- this.deps.enqueueReplicationInfoV2(message);
676
+ this.deps.enqueueReplicationInfoV2(mutation);
656
677
  if (!this.deps.isLegacyReplicationInfoEnabled()) {
657
678
  return;
658
679
  }
659
680
  try {
660
- await this.deps.getRpc().send(message, {
681
+ await this.deps.getRpc().send(toLegacyReplicationInfoFrame(mutation), {
661
682
  priority: CONVERGENCE_MESSAGE_PRIORITY,
662
683
  signal: ownershipLifecycleController.signal,
663
684
  });
@@ -0,0 +1,34 @@
1
+ import type { ReplicationRangeMessage } from "./ranges.js";
2
+
3
+ /**
4
+ * Neutral internal representation of a local replication-state mutation.
5
+ *
6
+ * Producers (replicate/unreplicate/join and their corrective snapshot paths)
7
+ * hand these plain tagged objects to the announcement coordinator, which maps
8
+ * them onto the directed V2 wire messages:
9
+ *
10
+ * - `full` -> FullReplicationInfoV2Message (authoritative snapshot)
11
+ * - `added` -> AddedReplicationInfoV2Message (incremental delta)
12
+ * - `stopped` -> StoppedReplicationInfoV2Message (retired segment ids)
13
+ *
14
+ * The retired legacy announcement classes (AllReplicatingSegmentsMessage,
15
+ * AddedReplicationSegmentMessage, StoppedReplicating) remain registered
16
+ * decode tombstones on the wire surface but are no longer the internal
17
+ * currency between mutation producers and the V2 sender.
18
+ */
19
+ export type FullReplicationInfoMutation = {
20
+ full: { segments: ReplicationRangeMessage<any>[] };
21
+ };
22
+
23
+ export type AddedReplicationInfoMutation = {
24
+ added: { segments: ReplicationRangeMessage<any>[] };
25
+ };
26
+
27
+ export type StoppedReplicationInfoMutation = {
28
+ stopped: { segmentIds: Uint8Array[] };
29
+ };
30
+
31
+ export type ReplicationInfoMutation =
32
+ | FullReplicationInfoMutation
33
+ | AddedReplicationInfoMutation
34
+ | StoppedReplicationInfoMutation;
@@ -8,13 +8,11 @@ import {
8
8
  import type { TransportMessage } from "./message.js";
9
9
  import type { ReplicationRangeIndexable } from "./ranges.js";
10
10
  import { deriveReplicationInfoV2ReceiverBinding } from "./replication-info-v2-binding.js";
11
+ import type { ReplicationInfoMutation } from "./replication-info-mutation.js";
11
12
  import {
12
13
  AddedReplicationInfoV2Message,
13
- AddedReplicationSegmentMessage,
14
- AllReplicatingSegmentsMessage,
15
14
  FullReplicationInfoV2Message,
16
15
  RequestReplicationInfoV2Message,
17
- StoppedReplicating,
18
16
  StoppedReplicationInfoV2Message,
19
17
  } from "./replication.js";
20
18
 
@@ -27,14 +25,9 @@ const MAX_BACKOFF_EXPONENT = 20;
27
25
 
28
26
  export { deriveReplicationInfoV2ReceiverBinding } from "./replication-info-v2-binding.js";
29
27
 
30
- export type LegacyReplicationInfoMessage =
31
- | AllReplicatingSegmentsMessage
32
- | AddedReplicationSegmentMessage
33
- | StoppedReplicating;
34
-
35
28
  type SendRequest =
36
29
  | { kind: "snapshot" }
37
- | { kind: "message"; message: LegacyReplicationInfoMessage };
30
+ | { kind: "mutation"; mutation: ReplicationInfoMutation };
38
31
 
39
32
  export type ReplicationInfoV2SendState = {
40
33
  peerHash: string;
@@ -393,9 +386,9 @@ export class ReplicationInfoV2SendCoordinator<R extends "u32" | "u64"> {
393
386
  return true;
394
387
  }
395
388
 
396
- enqueue(message: LegacyReplicationInfoMessage): void {
389
+ enqueue(mutation: ReplicationInfoMutation): void {
397
390
  for (const state of [...this._sendStates.values()]) {
398
- this.enqueueState(state, { kind: "message", message });
391
+ this.enqueueState(state, { kind: "mutation", mutation });
399
392
  }
400
393
  }
401
394
 
@@ -538,20 +531,21 @@ export class ReplicationInfoV2SendCoordinator<R extends "u32" | "u64"> {
538
531
  this.deps.validatePersistedReplicationRangeSnapshot(segments);
539
532
  return new FullReplicationInfoV2Message({ ...common, segments });
540
533
  }
541
- if (request.message instanceof AllReplicatingSegmentsMessage) {
542
- const segments = request.message.segments;
534
+ const { mutation } = request;
535
+ if ("full" in mutation) {
536
+ const segments = mutation.full.segments;
543
537
  this.deps.validatePersistedReplicationRangeSnapshot(segments);
544
538
  return new FullReplicationInfoV2Message({ ...common, segments });
545
539
  }
546
- if (request.message instanceof AddedReplicationSegmentMessage) {
540
+ if ("added" in mutation) {
547
541
  return new AddedReplicationInfoV2Message({
548
542
  ...common,
549
- segments: request.message.segments,
543
+ segments: mutation.added.segments,
550
544
  });
551
545
  }
552
546
  return new StoppedReplicationInfoV2Message({
553
547
  ...common,
554
- segmentIds: request.message.segmentIds,
548
+ segmentIds: mutation.stopped.segmentIds,
555
549
  });
556
550
  }
557
551