@optimystic/db-core 0.24.2 → 0.25.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 (103) hide show
  1. package/dist/src/blocks/helpers.d.ts +5 -0
  2. package/dist/src/blocks/helpers.d.ts.map +1 -1
  3. package/dist/src/blocks/helpers.js +12 -0
  4. package/dist/src/blocks/helpers.js.map +1 -1
  5. package/dist/src/cluster/membership.d.ts +7 -0
  6. package/dist/src/cluster/membership.d.ts.map +1 -1
  7. package/dist/src/cluster/membership.js +12 -8
  8. package/dist/src/cluster/membership.js.map +1 -1
  9. package/dist/src/cluster/structs.d.ts +27 -1
  10. package/dist/src/cluster/structs.d.ts.map +1 -1
  11. package/dist/src/cluster/structs.js.map +1 -1
  12. package/dist/src/collection/action.d.ts +14 -0
  13. package/dist/src/collection/action.d.ts.map +1 -1
  14. package/dist/src/collection/action.js +16 -1
  15. package/dist/src/collection/action.js.map +1 -1
  16. package/dist/src/collection/collection.d.ts +273 -4
  17. package/dist/src/collection/collection.d.ts.map +1 -1
  18. package/dist/src/collection/collection.js +427 -30
  19. package/dist/src/collection/collection.js.map +1 -1
  20. package/dist/src/collections/tree/tree.d.ts +16 -1
  21. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  22. package/dist/src/collections/tree/tree.js +19 -0
  23. package/dist/src/collections/tree/tree.js.map +1 -1
  24. package/dist/src/index.d.ts +1 -0
  25. package/dist/src/index.d.ts.map +1 -1
  26. package/dist/src/index.js +1 -0
  27. package/dist/src/index.js.map +1 -1
  28. package/dist/src/network/i-repo.d.ts +11 -1
  29. package/dist/src/network/i-repo.d.ts.map +1 -1
  30. package/dist/src/network/stale-failure.d.ts +21 -0
  31. package/dist/src/network/stale-failure.d.ts.map +1 -1
  32. package/dist/src/network/stale-failure.js +22 -0
  33. package/dist/src/network/stale-failure.js.map +1 -1
  34. package/dist/src/network/struct.d.ts +66 -21
  35. package/dist/src/network/struct.d.ts.map +1 -1
  36. package/dist/src/network/struct.js.map +1 -1
  37. package/dist/src/testing/test-transactor.d.ts +22 -0
  38. package/dist/src/testing/test-transactor.d.ts.map +1 -1
  39. package/dist/src/testing/test-transactor.js +44 -5
  40. package/dist/src/testing/test-transactor.js.map +1 -1
  41. package/dist/src/transaction/coordinator.d.ts +20 -0
  42. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  43. package/dist/src/transaction/coordinator.js +256 -113
  44. package/dist/src/transaction/coordinator.js.map +1 -1
  45. package/dist/src/transaction/operations-hash.d.ts +1 -1
  46. package/dist/src/transaction/operations-hash.js +1 -1
  47. package/dist/src/transaction/transaction.d.ts +4 -2
  48. package/dist/src/transaction/transaction.d.ts.map +1 -1
  49. package/dist/src/transactor/network-transactor.d.ts +21 -1
  50. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  51. package/dist/src/transactor/network-transactor.js +122 -29
  52. package/dist/src/transactor/network-transactor.js.map +1 -1
  53. package/dist/src/transactor/transactor-source.d.ts +11 -5
  54. package/dist/src/transactor/transactor-source.d.ts.map +1 -1
  55. package/dist/src/transactor/transactor-source.js +16 -8
  56. package/dist/src/transactor/transactor-source.js.map +1 -1
  57. package/dist/src/transform/cache-source.d.ts +13 -0
  58. package/dist/src/transform/cache-source.d.ts.map +1 -1
  59. package/dist/src/transform/cache-source.js +18 -0
  60. package/dist/src/transform/cache-source.js.map +1 -1
  61. package/dist/src/transform/digest.d.ts +18 -0
  62. package/dist/src/transform/digest.d.ts.map +1 -0
  63. package/dist/src/transform/digest.js +65 -0
  64. package/dist/src/transform/digest.js.map +1 -0
  65. package/dist/src/transform/index.d.ts +1 -0
  66. package/dist/src/transform/index.d.ts.map +1 -1
  67. package/dist/src/transform/index.js +1 -0
  68. package/dist/src/transform/index.js.map +1 -1
  69. package/dist/src/transform/tracker.d.ts +16 -0
  70. package/dist/src/transform/tracker.d.ts.map +1 -1
  71. package/dist/src/transform/tracker.js +40 -1
  72. package/dist/src/transform/tracker.js.map +1 -1
  73. package/dist/src/utility/canonical-json.d.ts +11 -0
  74. package/dist/src/utility/canonical-json.d.ts.map +1 -0
  75. package/dist/src/utility/canonical-json.js +15 -0
  76. package/dist/src/utility/canonical-json.js.map +1 -0
  77. package/dist/src/utility/lru-map.d.ts +2 -0
  78. package/dist/src/utility/lru-map.d.ts.map +1 -1
  79. package/dist/src/utility/lru-map.js +4 -0
  80. package/dist/src/utility/lru-map.js.map +1 -1
  81. package/package.json +2 -1
  82. package/src/blocks/helpers.ts +26 -13
  83. package/src/cluster/membership.ts +87 -85
  84. package/src/cluster/structs.ts +28 -1
  85. package/src/collection/action.ts +17 -0
  86. package/src/collection/collection.ts +1129 -688
  87. package/src/collections/tree/tree.ts +341 -320
  88. package/src/index.ts +24 -23
  89. package/src/network/i-repo.ts +59 -46
  90. package/src/network/stale-failure.ts +67 -43
  91. package/src/network/struct.ts +332 -270
  92. package/src/testing/test-transactor.ts +680 -638
  93. package/src/transaction/coordinator.ts +1266 -1110
  94. package/src/transaction/operations-hash.ts +1 -1
  95. package/src/transaction/transaction.ts +4 -2
  96. package/src/transactor/network-transactor.ts +123 -32
  97. package/src/transactor/transactor-source.ts +17 -9
  98. package/src/transform/cache-source.ts +20 -0
  99. package/src/transform/digest.ts +72 -0
  100. package/src/transform/index.ts +1 -0
  101. package/src/transform/tracker.ts +42 -1
  102. package/src/utility/canonical-json.ts +16 -0
  103. package/src/utility/lru-map.ts +5 -0
package/src/index.ts CHANGED
@@ -1,23 +1,24 @@
1
- export * from "./blocks/index.js";
2
- export * from "./btree/index.js";
3
- export * from "./chain/index.js";
4
- export * from "./cluster/index.js";
5
- export * from "./cohort-topic/index.js";
6
- export * from "./collection/index.js";
7
- export * from "./collections/index.js";
8
- export * from "./log/index.js";
9
- export * from "./matchmaking/index.js";
10
- export * from "./network/index.js";
11
- export * from "./reactivity/index.js";
12
- export * from "./transaction/index.js";
13
- export * from "./transactor/index.js";
14
- export * from "./transform/index.js";
15
- export * from "./utility/groupby.js";
16
- export * from "./utility/hash-string.js";
17
- export * from "./utility/latches.js";
18
- export * from "./utility/nameof.js";
19
- export * from "./utility/ensured.js";
20
- export * from "./utility/pending.js";
21
- export * from "./utility/block-id-to-bytes.js";
22
- export * from "./utility/lru-map.js";
23
- export * from "./utility/backoff.js";
1
+ export * from "./blocks/index.js";
2
+ export * from "./btree/index.js";
3
+ export * from "./chain/index.js";
4
+ export * from "./cluster/index.js";
5
+ export * from "./cohort-topic/index.js";
6
+ export * from "./collection/index.js";
7
+ export * from "./collections/index.js";
8
+ export * from "./log/index.js";
9
+ export * from "./matchmaking/index.js";
10
+ export * from "./network/index.js";
11
+ export * from "./reactivity/index.js";
12
+ export * from "./transaction/index.js";
13
+ export * from "./transactor/index.js";
14
+ export * from "./transform/index.js";
15
+ export * from "./utility/canonical-json.js";
16
+ export * from "./utility/groupby.js";
17
+ export * from "./utility/hash-string.js";
18
+ export * from "./utility/latches.js";
19
+ export * from "./utility/nameof.js";
20
+ export * from "./utility/ensured.js";
21
+ export * from "./utility/pending.js";
22
+ export * from "./utility/block-id-to-bytes.js";
23
+ export * from "./utility/lru-map.js";
24
+ export * from "./utility/backoff.js";
@@ -1,46 +1,59 @@
1
- import type { ActionBlocks, CommitResult, GetBlockResults, PendResult, PendRequest, BlockGets, BlockId, ActionId } from "../index.js";
2
-
3
- export type MessageOptions = {
4
- expiration?: number;
5
- signal?: AbortSignal;
6
- /**
7
- * Per-peer dial deadline in ms. Bounds only the dial portion of a call, so
8
- * an unreachable peer fails fast and the caller's retry loop can re-pick
9
- * a different coordinator. Independent of `expiration` (the overall budget):
10
- * a 30s transaction with `dialTimeoutMs: 3000` can afford ten 3s dial
11
- * attempts against different peers. Once a dial succeeds, the response wait
12
- * is bound by the remaining `expiration` budget. Undefined means "do not
13
- * impose a separate dial cap; the overall budget is the cap".
14
- */
15
- dialTimeoutMs?: number;
16
- /**
17
- * Blocks this coordinator is responsible for driving through consensus. Threaded
18
- * from {@link NetworkTransactor.pend}'s batch (which consolidates several blocks
19
- * onto one coordinating peer) into {@link CoordinatorRepo.pend}, which uses it to
20
- * pick the cluster anchor. Absent on bare per-block paths, which fall back to the
21
- * transforms' own block ids.
22
- */
23
- coordinatingBlockIds?: BlockId[];
24
- }
25
-
26
- export type RepoCommitRequest = {
27
- blockIds: BlockId[];
28
- actionId: ActionId;
29
- rev: number;
30
- /**
31
- * The collection's chain tail block id (the {@link CommitRequest.tailId}). Optional on the per-block
32
- * repo operation: the transactor threads the committing collection's tail through every per-block
33
- * commit so the coordinator carries it into the consensus commit op, letting the committing node's
34
- * `StorageRepo.commit` stamp it onto the emitted `CollectionChangeEvent` (the reactivity topic anchor
35
- * `H(tailId ‖ "reactivity")`). Absent on bare per-block paths (e.g. churn replication), which never
36
- * originate reactivity.
37
- */
38
- tailId?: BlockId;
39
- };
40
-
41
- export type IRepo = {
42
- get(blockGets: BlockGets, options?: MessageOptions): Promise<GetBlockResults>;
43
- pend(request: PendRequest, options?: MessageOptions): Promise<PendResult>;
44
- cancel(actionRef: ActionBlocks, options?: MessageOptions): Promise<void>;
45
- commit(request: RepoCommitRequest, options?: MessageOptions): Promise<CommitResult>;
46
- }
1
+ import type { ActionBlocks, BlockContentDigests, CommitResult, GetBlockResults, PendResult, PendRequest, BlockGets, BlockId, ActionId } from "../index.js";
2
+
3
+ export type MessageOptions = {
4
+ expiration?: number;
5
+ signal?: AbortSignal;
6
+ /**
7
+ * Per-peer dial deadline in ms. Bounds only the dial portion of a call, so
8
+ * an unreachable peer fails fast and the caller's retry loop can re-pick
9
+ * a different coordinator. Independent of `expiration` (the overall budget):
10
+ * a 30s transaction with `dialTimeoutMs: 3000` can afford ten 3s dial
11
+ * attempts against different peers. Once a dial succeeds, the response wait
12
+ * is bound by the remaining `expiration` budget. Undefined means "do not
13
+ * impose a separate dial cap; the overall budget is the cap".
14
+ */
15
+ dialTimeoutMs?: number;
16
+ /**
17
+ * Blocks this coordinator is responsible for driving through consensus. Threaded
18
+ * from {@link NetworkTransactor.pend}'s batch (which consolidates several blocks
19
+ * onto one coordinating peer) into {@link CoordinatorRepo.pend}, which uses it to
20
+ * pick the cluster anchor. Absent on bare per-block paths, which fall back to the
21
+ * transforms' own block ids.
22
+ */
23
+ coordinatingBlockIds?: BlockId[];
24
+ }
25
+
26
+ export type RepoCommitRequest = {
27
+ blockIds: BlockId[];
28
+ actionId: ActionId;
29
+ rev: number;
30
+ /**
31
+ * The collection's chain tail block id (the {@link CommitRequest.tailId}). Optional on the per-block
32
+ * repo operation: the transactor threads the committing collection's tail through every per-block
33
+ * commit so the coordinator carries it into the consensus commit op, letting the committing node's
34
+ * `StorageRepo.commit` stamp it onto the emitted `CollectionChangeEvent` (the reactivity topic anchor
35
+ * `H(tailId ‖ "reactivity")`). Absent on bare per-block paths (e.g. churn replication), which never
36
+ * originate reactivity.
37
+ */
38
+ tailId?: BlockId;
39
+ /**
40
+ * Per-block content declarations for this batch (see {@link BlockContentDigests}).
41
+ * The transactor narrows its action-wide {@link CommitRequest.blockDigests} to this request's own
42
+ * `blockIds` before sending, so a cohort only ever declares for the blocks it is driving —
43
+ * `RepoClient.commit` wraps this whole request into the `RepoMessage`, and the coordinator places
44
+ * the received request into the consensus message, so the digests land inside every cohort
45
+ * signature's preimage. Absent when no block in this batch could be digested (each undeclared
46
+ * block falls back to corroboration).
47
+ */
48
+ // NOTE: accepted tradeoff — optional by necessity, and NOT free: an undeclared block retains no
49
+ // durable commit proof and so cannot gain holders by push. Full rationale and the measured cost of
50
+ // making it required live at {@link CommitRequest.blockDigests} in `struct.ts`; keep the two in sync.
51
+ blockDigests?: BlockContentDigests;
52
+ };
53
+
54
+ export type IRepo = {
55
+ get(blockGets: BlockGets, options?: MessageOptions): Promise<GetBlockResults>;
56
+ pend(request: PendRequest, options?: MessageOptions): Promise<PendResult>;
57
+ cancel(actionRef: ActionBlocks, options?: MessageOptions): Promise<void>;
58
+ commit(request: RepoCommitRequest, options?: MessageOptions): Promise<CommitResult>;
59
+ }
@@ -1,43 +1,67 @@
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 { ActionId, ActionRev } from "../collection/action.js";
2
+ import type { StaleFailure } from "./struct.js";
3
+
4
+ /**
5
+ * The single rule for "is this non-success retryable after a re-read?" Both write paths and the
6
+ * transactor's aggregation call this — no consumer re-derives it.
7
+ *
8
+ * {@link StaleFailure.conflict} is authoritative when present. The `missing`/`pending` fallback
9
+ * covers producers that have not been taught the field, including a remote peer on an older build
10
+ * (the repo protocol is plain JSON, so an unset field simply arrives absent).
11
+ */
12
+ export function isConflictFailure(failure: StaleFailure): boolean {
13
+ return failure.conflict ?? Boolean(failure.missing?.length || failure.pending?.length);
14
+ }
15
+
16
+ /**
17
+ * The single rule for picking one {@link StaleFailure.staleAt} out of several candidates.
18
+ *
19
+ * Highest `rev` wins: the losing writer's next request has to clear EVERY holder, so the largest
20
+ * confirmed revision is the binding constraint and any smaller one understates it. Ties keep the
21
+ * earlier candidate. Undefined entries (a block or batch with no confirmed number, or a peer that
22
+ * predates the field) contribute nothing, and an all-undefined input yields undefined so callers
23
+ * can omit the key rather than emit `staleAt: undefined`.
24
+ *
25
+ * Every site that has more than one candidate calls this — the producers scanning several blocks
26
+ * (`StorageRepo.pend`/`.commit`, `CoordinatorRepo.classifyStaleRejection`) as well as
27
+ * `NetworkTransactor` rebuilding one response from many per-batch ones. Uniformity is what makes
28
+ * the transactor's aggregate meaningful: if a producer reported an arbitrary block instead of its
29
+ * highest, taking the max across producers would still understate the constraint.
30
+ *
31
+ * NOTE: comparing revisions across blocks is only meaningful because one pend covers one
32
+ * collection, so every candidate comes from the same revision counter. If a pend is ever allowed
33
+ * to span collections, these numbers come from unrelated counters and selection must become
34
+ * per-collection.
35
+ */
36
+ export function highestStaleAt(candidates: readonly StaleFailure['staleAt'][]): StaleFailure['staleAt'] {
37
+ let best: StaleFailure['staleAt'];
38
+ for (const candidate of candidates) {
39
+ if (candidate !== undefined && (best === undefined || candidate.rev > best.rev)) {
40
+ best = candidate;
41
+ }
42
+ }
43
+ return best;
44
+ }
45
+
46
+ /**
47
+ * The single rule for "is the action holding this revision OUR OWN?" — true only when `latest`
48
+ * names exactly the revision being requested AND the same action requesting it.
49
+ *
50
+ * A write touching several blocks is committed one group at a time, so it can end up with some
51
+ * blocks durable and the rest refused (a *torn action*). The retry reuses the same `actionId`, so
52
+ * every revision check meets the writer's own durable half and must treat it as a no-op rather
53
+ * than as a rival's win — otherwise the writer is refused by its own committed work, forever.
54
+ *
55
+ * Deliberately `===` only. At `latest.rev > rev` this returns false even for our own action: the
56
+ * follow-on commit is refused as stale anyway (`StorageRepo.commit`'s `missedCommits` branch), so
57
+ * approving would only defer the refusal by a round trip, and `latest` alone can no longer name
58
+ * who holds `rev` — that needs the revision index (see `IRevisionActionReader`).
59
+ *
60
+ * Every revision-vs-action check calls this: the pend tier (`StorageRepo.pend`,
61
+ * `ClusterMember.validatePendOperations`, `CoordinatorRepo.classifyStaleRejection`) and the commit
62
+ * tier (`StorageRepo.commit`'s `alreadyDone` partition, `ClusterMember.validateCommitRevisions`).
63
+ * Single-sourcing it is what makes "all the tiers agree" a fact rather than a comment.
64
+ */
65
+ export function isOwnRevision(latest: ActionRev | undefined, rev: number | undefined, actionId: ActionId): boolean {
66
+ return latest !== undefined && rev !== undefined && latest.rev === rev && latest.actionId === actionId;
67
+ }