@optimystic/db-core 0.21.0 → 0.24.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.
Files changed (185) hide show
  1. package/README.md +336 -336
  2. package/dist/src/btree/btree.d.ts +2 -1
  3. package/dist/src/btree/btree.d.ts.map +1 -1
  4. package/dist/src/btree/btree.js +1 -1
  5. package/dist/src/btree/btree.js.map +1 -1
  6. package/dist/src/chain/chain.d.ts +1 -1
  7. package/dist/src/chain/chain.d.ts.map +1 -1
  8. package/dist/src/chain/chain.js +1 -1
  9. package/dist/src/chain/chain.js.map +1 -1
  10. package/dist/src/cluster/structs.d.ts +39 -1
  11. package/dist/src/cluster/structs.d.ts.map +1 -1
  12. package/dist/src/cluster/structs.js +24 -0
  13. package/dist/src/cluster/structs.js.map +1 -1
  14. package/dist/src/collection/collection.d.ts +20 -1
  15. package/dist/src/collection/collection.d.ts.map +1 -1
  16. package/dist/src/collection/collection.js +31 -4
  17. package/dist/src/collection/collection.js.map +1 -1
  18. package/dist/src/collections/diary/diary.d.ts.map +1 -1
  19. package/dist/src/collections/diary/diary.js +2 -1
  20. package/dist/src/collections/diary/diary.js.map +1 -1
  21. package/dist/src/collections/diary/struct.js +1 -1
  22. package/dist/src/collections/diary/struct.js.map +1 -1
  23. package/dist/src/collections/tree/collection-trunk.js +1 -1
  24. package/dist/src/collections/tree/collection-trunk.js.map +1 -1
  25. package/dist/src/collections/tree/struct.d.ts +1 -1
  26. package/dist/src/collections/tree/struct.d.ts.map +1 -1
  27. package/dist/src/collections/tree/struct.js +2 -1
  28. package/dist/src/collections/tree/struct.js.map +1 -1
  29. package/dist/src/collections/tree/tree.d.ts +5 -0
  30. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  31. package/dist/src/collections/tree/tree.js +7 -0
  32. package/dist/src/collections/tree/tree.js.map +1 -1
  33. package/dist/src/log/log.d.ts +1 -1
  34. package/dist/src/log/log.d.ts.map +1 -1
  35. package/dist/src/log/log.js +2 -2
  36. package/dist/src/log/log.js.map +1 -1
  37. package/dist/src/network/i-peer-network.d.ts +16 -0
  38. package/dist/src/network/i-peer-network.d.ts.map +1 -1
  39. package/dist/src/network/struct.d.ts +39 -2
  40. package/dist/src/network/struct.d.ts.map +1 -1
  41. package/dist/src/network/struct.js +18 -0
  42. package/dist/src/network/struct.js.map +1 -1
  43. package/dist/src/testing/test-transactor.d.ts +95 -8
  44. package/dist/src/testing/test-transactor.d.ts.map +1 -1
  45. package/dist/src/testing/test-transactor.js +133 -12
  46. package/dist/src/testing/test-transactor.js.map +1 -1
  47. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  48. package/dist/src/transaction/coordinator.js +2 -1
  49. package/dist/src/transaction/coordinator.js.map +1 -1
  50. package/dist/src/transaction/transaction.d.ts +1 -1
  51. package/dist/src/transaction/transaction.js +1 -1
  52. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  53. package/dist/src/transactor/network-transactor.js +57 -18
  54. package/dist/src/transactor/network-transactor.js.map +1 -1
  55. package/dist/src/transactor/transactor-source.d.ts.map +1 -1
  56. package/dist/src/transactor/transactor-source.js +25 -2
  57. package/dist/src/transactor/transactor-source.js.map +1 -1
  58. package/dist/src/transform/cache-source.js +1 -1
  59. package/dist/src/transform/cache-source.js.map +1 -1
  60. package/dist/src/transform/helpers.d.ts +6 -1
  61. package/dist/src/transform/helpers.d.ts.map +1 -1
  62. package/dist/src/transform/helpers.js +7 -6
  63. package/dist/src/transform/helpers.js.map +1 -1
  64. package/dist/src/transform/tracker.d.ts.map +1 -1
  65. package/dist/src/transform/tracker.js +2 -1
  66. package/dist/src/transform/tracker.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/btree/btree.ts +2 -1
  69. package/src/chain/chain.ts +2 -1
  70. package/src/cluster/membership.ts +85 -85
  71. package/src/cluster/structs.ts +43 -4
  72. package/src/cohort-topic/addressing.ts +120 -120
  73. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -253
  74. package/src/cohort-topic/antidos/bootstrap-evidence.ts +106 -106
  75. package/src/cohort-topic/antidos/index.ts +5 -5
  76. package/src/cohort-topic/antidos/rate-limiter.ts +210 -210
  77. package/src/cohort-topic/antidos/replay-guard.ts +146 -146
  78. package/src/cohort-topic/antidos/topic-budget.ts +160 -160
  79. package/src/cohort-topic/antiflood/index.ts +2 -2
  80. package/src/cohort-topic/antiflood/invariants.ts +108 -108
  81. package/src/cohort-topic/antiflood/jitter.ts +117 -117
  82. package/src/cohort-topic/coldstart.ts +237 -237
  83. package/src/cohort-topic/dmax.ts +88 -88
  84. package/src/cohort-topic/gossip/bus.ts +254 -254
  85. package/src/cohort-topic/gossip/index.ts +3 -3
  86. package/src/cohort-topic/gossip/records.ts +45 -45
  87. package/src/cohort-topic/gossip/view.ts +91 -91
  88. package/src/cohort-topic/index.ts +20 -20
  89. package/src/cohort-topic/load/barometer.ts +134 -134
  90. package/src/cohort-topic/load/index.ts +1 -1
  91. package/src/cohort-topic/member-engine.ts +430 -430
  92. package/src/cohort-topic/membership/index.ts +3 -3
  93. package/src/cohort-topic/membership/publisher.ts +163 -163
  94. package/src/cohort-topic/membership/source.ts +41 -41
  95. package/src/cohort-topic/membership/verifier.ts +461 -461
  96. package/src/cohort-topic/ports.ts +157 -157
  97. package/src/cohort-topic/promotion.ts +405 -405
  98. package/src/cohort-topic/registration/bytes.ts +37 -37
  99. package/src/cohort-topic/registration/handoff.ts +154 -154
  100. package/src/cohort-topic/registration/index.ts +6 -6
  101. package/src/cohort-topic/registration/renewal.ts +495 -495
  102. package/src/cohort-topic/registration/sharding.ts +61 -61
  103. package/src/cohort-topic/registration/store.ts +81 -81
  104. package/src/cohort-topic/registration/types.ts +91 -91
  105. package/src/cohort-topic/ring-hash.ts +50 -50
  106. package/src/cohort-topic/service.ts +416 -416
  107. package/src/cohort-topic/sig/index.ts +2 -2
  108. package/src/cohort-topic/sig/payloads.ts +59 -59
  109. package/src/cohort-topic/sig/threshold.ts +64 -64
  110. package/src/cohort-topic/tiers.ts +74 -74
  111. package/src/cohort-topic/traffic.ts +233 -233
  112. package/src/cohort-topic/walk.ts +326 -326
  113. package/src/cohort-topic/willingness.ts +237 -237
  114. package/src/cohort-topic/wire/codec.ts +216 -216
  115. package/src/cohort-topic/wire/index.ts +18 -18
  116. package/src/cohort-topic/wire/payloads.ts +126 -126
  117. package/src/cohort-topic/wire/primitives.ts +188 -188
  118. package/src/cohort-topic/wire/types.ts +475 -475
  119. package/src/cohort-topic/wire/validate.ts +512 -512
  120. package/src/collection/collection-type-registry.ts +37 -37
  121. package/src/collection/collection.ts +32 -4
  122. package/src/collections/diary/diary.ts +68 -67
  123. package/src/collections/diary/struct.ts +1 -1
  124. package/src/collections/tree/collection-trunk.ts +1 -1
  125. package/src/collections/tree/readme.md +4 -0
  126. package/src/collections/tree/struct.ts +3 -1
  127. package/src/collections/tree/tree.ts +320 -312
  128. package/src/log/log.ts +2 -2
  129. package/src/matchmaking/capability-filter.ts +45 -45
  130. package/src/matchmaking/config.ts +98 -98
  131. package/src/matchmaking/index.ts +21 -21
  132. package/src/matchmaking/multi-cohort-seeker.ts +234 -234
  133. package/src/matchmaking/provider.ts +123 -123
  134. package/src/matchmaking/query-eval.ts +105 -105
  135. package/src/matchmaking/seeker-walk.ts +127 -127
  136. package/src/matchmaking/seeker.ts +86 -86
  137. package/src/matchmaking/topic-anchor.ts +90 -90
  138. package/src/matchmaking/voting-quorum.ts +394 -394
  139. package/src/matchmaking/wire.ts +603 -603
  140. package/src/network/i-peer-network.ts +17 -0
  141. package/src/network/stale-failure.ts +43 -43
  142. package/src/network/struct.ts +41 -2
  143. package/src/network/types.ts +37 -37
  144. package/src/reactivity/backfill.ts +220 -220
  145. package/src/reactivity/backpressure.ts +191 -191
  146. package/src/reactivity/checkpoint.ts +308 -308
  147. package/src/reactivity/config.ts +172 -172
  148. package/src/reactivity/dedupe.ts +132 -132
  149. package/src/reactivity/forwarder.ts +87 -87
  150. package/src/reactivity/index.ts +34 -34
  151. package/src/reactivity/notification.ts +123 -123
  152. package/src/reactivity/policy.ts +79 -79
  153. package/src/reactivity/push-state.ts +310 -310
  154. package/src/reactivity/recover.ts +153 -153
  155. package/src/reactivity/replay-buffer.ts +141 -141
  156. package/src/reactivity/resume.ts +549 -549
  157. package/src/reactivity/rotation.ts +415 -415
  158. package/src/reactivity/subscriber.ts +132 -132
  159. package/src/reactivity/subscription.ts +66 -66
  160. package/src/reactivity/topic-anchor.ts +71 -71
  161. package/src/reactivity/verify.ts +73 -73
  162. package/src/reactivity/wire-validate.ts +13 -13
  163. package/src/reactivity/wire.ts +224 -224
  164. package/src/testing/async-wait.ts +65 -65
  165. package/src/testing/index.ts +2 -2
  166. package/src/testing/test-transactor.ts +638 -489
  167. package/src/transaction/coordinator.ts +2 -1
  168. package/src/transaction/errors.ts +91 -91
  169. package/src/transaction/operations-hash.ts +196 -196
  170. package/src/transaction/read-dependency-collector.ts +78 -78
  171. package/src/transaction/transaction.ts +1 -1
  172. package/src/transactor/change-notifier.ts +80 -80
  173. package/src/transactor/index.ts +5 -5
  174. package/src/transactor/network-transactor.ts +58 -19
  175. package/src/transactor/transactor-source.ts +25 -2
  176. package/src/transform/atomic-proxy.ts +92 -92
  177. package/src/transform/cache-source.ts +1 -1
  178. package/src/transform/helpers.ts +159 -158
  179. package/src/transform/tracker.ts +2 -1
  180. package/src/utility/backoff.ts +95 -95
  181. package/src/utility/batch-coordinator.ts +191 -191
  182. package/dist/src/transaction/context.d.ts +0 -60
  183. package/dist/src/transaction/context.d.ts.map +0 -1
  184. package/dist/src/transaction/context.js +0 -91
  185. package/dist/src/transaction/context.js.map +0 -1
@@ -5,4 +5,21 @@ export type IPeerNetwork = {
5
5
  * Dial a peer and establish a protocol stream
6
6
  */
7
7
  connect(peerId: PeerId, protocol: string, options?: AbortOptions): Promise<Stream>;
8
+
9
+ /**
10
+ * Optionally teach the dialer how to reach `peerId`, from addresses carried by an
11
+ * application-level message (a cluster record's peer map, a redirect payload).
12
+ *
13
+ * This exists because libp2p only propagates a peer's addresses to peers it is
14
+ * DIRECTLY connected to. A cohort here is chosen by key position, so a member
15
+ * routinely shares a cohort with a relay-only peer it has never met and holds an
16
+ * empty address list for it — a dial by peer id alone then fails immediately. Our
17
+ * own protocol messages already carry the addresses; this is the seam that lets a
18
+ * recipient keep them.
19
+ *
20
+ * Addresses are multiaddr strings (matching `ClusterPeers` / `RedirectPayload`), so
21
+ * db-core needs no multiaddr dependency. Implementations without an address book
22
+ * omit this method.
23
+ */
24
+ recordPeerAddresses?(peerId: PeerId, multiaddrs: string[]): void;
8
25
  }
@@ -1,43 +1,43 @@
1
- import type { StaleFailure } from "./struct.js";
2
-
3
- /**
4
- * The single rule for "is this non-success retryable after a re-read?" Both write paths and the
5
- * transactor's aggregation call this — no consumer re-derives it.
6
- *
7
- * {@link StaleFailure.conflict} is authoritative when present. The `missing`/`pending` fallback
8
- * covers producers that have not been taught the field, including a remote peer on an older build
9
- * (the repo protocol is plain JSON, so an unset field simply arrives absent).
10
- */
11
- export function isConflictFailure(failure: StaleFailure): boolean {
12
- return failure.conflict ?? Boolean(failure.missing?.length || failure.pending?.length);
13
- }
14
-
15
- /**
16
- * The single rule for picking one {@link StaleFailure.staleAt} out of several candidates.
17
- *
18
- * Highest `rev` wins: the losing writer's next request has to clear EVERY holder, so the largest
19
- * confirmed revision is the binding constraint and any smaller one understates it. Ties keep the
20
- * earlier candidate. Undefined entries (a block or batch with no confirmed number, or a peer that
21
- * predates the field) contribute nothing, and an all-undefined input yields undefined so callers
22
- * can omit the key rather than emit `staleAt: undefined`.
23
- *
24
- * Every site that has more than one candidate calls this — the producers scanning several blocks
25
- * (`StorageRepo.pend`/`.commit`, `CoordinatorRepo.classifyStaleRejection`) as well as
26
- * `NetworkTransactor` rebuilding one response from many per-batch ones. Uniformity is what makes
27
- * the transactor's aggregate meaningful: if a producer reported an arbitrary block instead of its
28
- * highest, taking the max across producers would still understate the constraint.
29
- *
30
- * NOTE: comparing revisions across blocks is only meaningful because one pend covers one
31
- * collection, so every candidate comes from the same revision counter. If a pend is ever allowed
32
- * to span collections, these numbers come from unrelated counters and selection must become
33
- * per-collection.
34
- */
35
- export function highestStaleAt(candidates: readonly StaleFailure['staleAt'][]): StaleFailure['staleAt'] {
36
- let best: StaleFailure['staleAt'];
37
- for (const candidate of candidates) {
38
- if (candidate !== undefined && (best === undefined || candidate.rev > best.rev)) {
39
- best = candidate;
40
- }
41
- }
42
- return best;
43
- }
1
+ import type { StaleFailure } from "./struct.js";
2
+
3
+ /**
4
+ * The single rule for "is this non-success retryable after a re-read?" Both write paths and the
5
+ * transactor's aggregation call this — no consumer re-derives it.
6
+ *
7
+ * {@link StaleFailure.conflict} is authoritative when present. The `missing`/`pending` fallback
8
+ * covers producers that have not been taught the field, including a remote peer on an older build
9
+ * (the repo protocol is plain JSON, so an unset field simply arrives absent).
10
+ */
11
+ export function isConflictFailure(failure: StaleFailure): boolean {
12
+ return failure.conflict ?? Boolean(failure.missing?.length || failure.pending?.length);
13
+ }
14
+
15
+ /**
16
+ * The single rule for picking one {@link StaleFailure.staleAt} out of several candidates.
17
+ *
18
+ * Highest `rev` wins: the losing writer's next request has to clear EVERY holder, so the largest
19
+ * confirmed revision is the binding constraint and any smaller one understates it. Ties keep the
20
+ * earlier candidate. Undefined entries (a block or batch with no confirmed number, or a peer that
21
+ * predates the field) contribute nothing, and an all-undefined input yields undefined so callers
22
+ * can omit the key rather than emit `staleAt: undefined`.
23
+ *
24
+ * Every site that has more than one candidate calls this — the producers scanning several blocks
25
+ * (`StorageRepo.pend`/`.commit`, `CoordinatorRepo.classifyStaleRejection`) as well as
26
+ * `NetworkTransactor` rebuilding one response from many per-batch ones. Uniformity is what makes
27
+ * the transactor's aggregate meaningful: if a producer reported an arbitrary block instead of its
28
+ * highest, taking the max across producers would still understate the constraint.
29
+ *
30
+ * NOTE: comparing revisions across blocks is only meaningful because one pend covers one
31
+ * collection, so every candidate comes from the same revision counter. If a pend is ever allowed
32
+ * to span collections, these numbers come from unrelated counters and selection must become
33
+ * per-collection.
34
+ */
35
+ export function highestStaleAt(candidates: readonly StaleFailure['staleAt'][]): StaleFailure['staleAt'] {
36
+ let best: StaleFailure['staleAt'];
37
+ for (const candidate of candidates) {
38
+ if (candidate !== undefined && (best === undefined || candidate.rev > best.rev)) {
39
+ best = candidate;
40
+ }
41
+ }
42
+ return best;
43
+ }
@@ -154,8 +154,22 @@ export type BlockUnavailableReason =
154
154
  /** Records for this block exist here but it cannot be reconstructed locally — a
155
155
  * revision was received with no base to apply it to, or its history is truncated. */
156
156
  | 'unmaterializable'
157
- /** Nothing is held locally and the cohort could not be consulted to confirm it. */
158
- | 'peers-unreachable';
157
+ /** Nothing is held locally; PART of the cohort answered and part could not be asked.
158
+ * A silent peer could be the sole holder, so the absence is a guess — but other
159
+ * coordinators are reachable, so asking one of them can still settle it. Also the
160
+ * fallback when the consult could not run at all (the cohort lookup itself failed):
161
+ * a routing failure says nothing about how many cohort members were reachable. */
162
+ | 'peers-unreachable'
163
+ /** Nothing is held locally and NO cohort member outside the answering node could be
164
+ * asked at all. Distinct from `peers-unreachable` in exactly the way that matters to
165
+ * a caller: there is no better-connected coordinator to re-ask, so the answer will
166
+ * not improve until that node's connectivity does. Its local view is all there is. */
167
+ | 'cohort-unreachable'
168
+ /** Nothing is held locally, but a cohort peer positively CLAIMED a revision of this
169
+ * block, and the answering node could neither corroborate that claim to a quorum nor
170
+ * acquire the content. The block is known to exist somewhere; reporting it absent
171
+ * would be a lie regardless of whether anyone was silent. */
172
+ | 'claimed-elsewhere';
159
173
 
160
174
  export type GetBlockResult = {
161
175
  /** The retrieved block - undefined if the block was deleted */
@@ -179,6 +193,16 @@ export type GetBlockResult = {
179
193
  * guess, not an authoritative absent. Every producer that omits it (including
180
194
  * TestTransactor) keeps meaning "authoritative". */
181
195
  unavailable?: BlockUnavailableReason;
196
+ /** Set when this repo served committed content it could NOT confirm is current: its
197
+ * freshness consult did not converge AND a cohort peer claimed a strictly higher
198
+ * revision than the one served, within the view the caller asked for (unpinned, or
199
+ * pinned at or above the claim). Carries that claimed revision. The claim did not
200
+ * drive a successful repair — it failed the read-repair corroboration quorum, or was
201
+ * corroborated but the content could not be acquired — so it is evidence of DOUBT,
202
+ * never a revision to adopt. Distinct from `unavailable`, which is about EXISTENCE:
203
+ * the content here is real, it may just be behind. Absent = confirmed, so every
204
+ * producer that omits it keeps its meaning. */
205
+ unconfirmedAheadRev?: number;
182
206
  };
183
207
 
184
208
  /**
@@ -194,6 +218,21 @@ export class BlockUnavailableError extends Error {
194
218
  }
195
219
  }
196
220
 
221
+ /**
222
+ * Thrown by an unpinned ("give me latest") block read whose surviving answer carries
223
+ * {@link GetBlockResult.unconfirmedAheadRev}: every reachable coordinator served content
224
+ * it could not confirm is current, while a cohort peer claimed a strictly higher revision
225
+ * nothing could corroborate or refute. Sibling of {@link BlockUnavailableError} — that one
226
+ * is about EXISTENCE (blockless answer, could not find out), this one about CURRENCY (real
227
+ * content, possibly behind). Not a StaleFailure: `Collection.sync` does not retry it.
228
+ */
229
+ export class BlockPossiblyStaleError extends Error {
230
+ constructor(readonly blockId: BlockId, readonly claimedRev: number) {
231
+ super(`Block ${blockId} may be stale: a cohort peer claimed rev ${claimedRev} that no reachable coordinator could confirm or refute`);
232
+ this.name = 'BlockPossiblyStaleError';
233
+ }
234
+ }
235
+
197
236
  export type GetBlockResults = Record<BlockId, GetBlockResult>;
198
237
 
199
238
  /**
@@ -1,37 +1,37 @@
1
- /**
2
- * Portable type aliases for peer networking.
3
- *
4
- * These minimal structural types decouple db-core from any concrete
5
- * networking library (e.g. libp2p). Concrete implementations in
6
- * transport packages (db-p2p) satisfy these structurally.
7
- */
8
-
9
- /** Minimal peer identifier — structurally compatible with libp2p's PeerId. */
10
- export type PeerId = {
11
- toString(): string;
12
- equals(other: unknown): boolean;
13
- };
14
-
15
- /** Opaque network stream — db-core never accesses stream internals. */
16
- export type Stream = {
17
- close(): Promise<void>;
18
- };
19
-
20
- /** Options for abortable operations. */
21
- export type AbortOptions = {
22
- signal?: AbortSignal;
23
- };
24
-
25
- /** Create a lightweight PeerId from its string representation. */
26
- export function peerIdFromString(id: string): PeerId {
27
- return {
28
- toString: () => id,
29
- equals: (other: unknown) =>
30
- other != null
31
- && typeof other === 'object'
32
- && 'toString' in other
33
- && typeof (other as PeerId).toString === 'function'
34
- && (other as PeerId).toString() === id,
35
- };
36
- }
37
-
1
+ /**
2
+ * Portable type aliases for peer networking.
3
+ *
4
+ * These minimal structural types decouple db-core from any concrete
5
+ * networking library (e.g. libp2p). Concrete implementations in
6
+ * transport packages (db-p2p) satisfy these structurally.
7
+ */
8
+
9
+ /** Minimal peer identifier — structurally compatible with libp2p's PeerId. */
10
+ export type PeerId = {
11
+ toString(): string;
12
+ equals(other: unknown): boolean;
13
+ };
14
+
15
+ /** Opaque network stream — db-core never accesses stream internals. */
16
+ export type Stream = {
17
+ close(): Promise<void>;
18
+ };
19
+
20
+ /** Options for abortable operations. */
21
+ export type AbortOptions = {
22
+ signal?: AbortSignal;
23
+ };
24
+
25
+ /** Create a lightweight PeerId from its string representation. */
26
+ export function peerIdFromString(id: string): PeerId {
27
+ return {
28
+ toString: () => id,
29
+ equals: (other: unknown) =>
30
+ other != null
31
+ && typeof other === 'object'
32
+ && 'toString' in other
33
+ && typeof (other as PeerId).toString === 'function'
34
+ && (other as PeerId).toString() === id,
35
+ };
36
+ }
37
+