@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
@@ -1,4 +1,9 @@
1
1
  import type { BlockOperation, IBlock, BlockId, BlockStore, ReadPurpose } from "../index.js";
2
2
  export declare function get<T extends IBlock>(store: BlockStore<T>, id: BlockId, purpose?: ReadPurpose): Promise<T>;
3
3
  export declare function apply<T extends IBlock>(store: BlockStore<T>, block: IBlock, op: BlockOperation): void;
4
+ /** Canonical sha256 of a block's content, base64url: `base64url(SHA256(canonicalJson(block)))`.
5
+ * The single hash used wherever two nodes must agree on what a block contains — quorum restoration
6
+ * content-agreement (db-p2p `selectQuorumBlock` candidates) and commit content digests. Keep ONE
7
+ * implementation: a drifted copy reports honest content as forged. */
8
+ export declare function canonicalBlockHash(block: IBlock): Promise<string>;
4
9
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/blocks/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG5F,wBAAsB,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAIhH;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,QAG9F"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/blocks/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI5F,wBAAsB,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAIhH;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,QAG9F;AAED;;;sEAGsE;AACtE,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE"}
@@ -1,4 +1,7 @@
1
+ import { sha256 } from "multiformats/hashes/sha2";
2
+ import { toString as uint8ArrayToString } from "uint8arrays";
1
3
  import { applyOperation } from "../transform/helpers.js";
4
+ import { canonicalJson } from "../utility/canonical-json.js";
2
5
  export async function get(store, id, purpose) {
3
6
  const block = await store.tryGet(id, purpose);
4
7
  if (!block)
@@ -9,4 +12,13 @@ export function apply(store, block, op) {
9
12
  applyOperation(block, op);
10
13
  store.update(block.header.id, op);
11
14
  }
15
+ /** Canonical sha256 of a block's content, base64url: `base64url(SHA256(canonicalJson(block)))`.
16
+ * The single hash used wherever two nodes must agree on what a block contains — quorum restoration
17
+ * content-agreement (db-p2p `selectQuorumBlock` candidates) and commit content digests. Keep ONE
18
+ * implementation: a drifted copy reports honest content as forged. */
19
+ export async function canonicalBlockHash(block) {
20
+ const bytes = new TextEncoder().encode(canonicalJson(block));
21
+ const digest = await sha256.digest(bytes);
22
+ return uint8ArrayToString(digest.digest, 'base64url');
23
+ }
12
24
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/blocks/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,GAAG,CAAmB,KAAoB,EAAE,EAAW,EAAE,OAAqB;IACnG,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK;QAAE,MAAM,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,UAAU,KAAK,CAAmB,KAAoB,EAAE,KAAa,EAAE,EAAkB;IAC9F,cAAc,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/blocks/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,GAAG,CAAmB,KAAoB,EAAE,EAAW,EAAE,OAAqB;IACnG,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK;QAAE,MAAM,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,UAAU,KAAK,CAAmB,KAAoB,EAAE,KAAa,EAAE,EAAkB;IAC9F,cAAc,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;sEAGsE;AACtE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAa;IACrD,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,kBAAkB,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACvD,CAAC"}
@@ -18,6 +18,13 @@ export declare const CURRENT_MEMBERSHIP_VERSION: 2;
18
18
  * it. `membershipDigest({})` (the empty set) is a fixed constant.
19
19
  */
20
20
  export declare function membershipDigest(peers: ClusterPeers): Promise<string>;
21
+ /**
22
+ * Canonical membership digest for an explicit peer-id list — the same digest {@link membershipDigest}
23
+ * derives from a peer map's keys. ONE implementation, so a verifier reading a stored `peerIds` list
24
+ * (e.g. a persisted block commit proof) and a coordinator reading live `ClusterPeers` can never
25
+ * disagree. Sorts a copy; the caller's array is not mutated.
26
+ */
27
+ export declare function membershipDigestFromIds(ids: readonly string[]): Promise<string>;
21
28
  /**
22
29
  * The membership digest that participates in a record's hashes, or `undefined` for a legacy (v1 /
23
30
  * unversioned) record. `undefined` selects the pre-binding hashing (empty-string concat is a no-op), so
@@ -1 +1 @@
1
- {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/cluster/membership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAG,CAAU,CAAC;AAWrD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAK3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,kBAAkB,CAAC,GAAG,MAAM,GAAG,SAAS,CAEhI;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItG;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI3H;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI/J"}
1
+ {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/cluster/membership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAM3E;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAG,CAAU,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,kBAAkB,CAAC,GAAG,MAAM,GAAG,SAAS,CAEhI;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItG;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI3H;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI/J"}
@@ -1,6 +1,7 @@
1
1
  import { sha256 } from "multiformats/hashes/sha2";
2
2
  import { base58btc } from "multiformats/bases/base58";
3
3
  import { toString as uint8ArrayToString } from "uint8arrays/to-string";
4
+ import { canonicalJson } from "../utility/canonical-json.js";
4
5
  /**
5
6
  * Membership-binding version a new coordinator emits. A record at this version folds a
6
7
  * {@link membershipDigest} of its peer set into `messageHash` / `promiseHash` / `commitHash`, so the
@@ -9,12 +10,6 @@ import { toString as uint8ArrayToString } from "uint8arrays/to-string";
9
10
  * this change.
10
11
  */
11
12
  export const CURRENT_MEMBERSHIP_VERSION = 2;
12
- /** Deterministic JSON: sorts object keys so the hash is independent of key insertion order. */
13
- function canonicalJson(value) {
14
- return JSON.stringify(value, (_, v) => v && typeof v === 'object' && !Array.isArray(v)
15
- ? Object.keys(v).sort().reduce((o, k) => { o[k] = v[k]; return o; }, {})
16
- : v);
17
- }
18
13
  /**
19
14
  * Canonical membership digest for a peer set: `base64url(SHA256(canonicalJson(sorted peer-id list)))`.
20
15
  *
@@ -25,8 +20,17 @@ function canonicalJson(value) {
25
20
  * it. `membershipDigest({})` (the empty set) is a fixed constant.
26
21
  */
27
22
  export async function membershipDigest(peers) {
28
- const ids = Object.keys(peers ?? {}).sort();
29
- const bytes = new TextEncoder().encode(canonicalJson(ids));
23
+ return membershipDigestFromIds(Object.keys(peers ?? {}));
24
+ }
25
+ /**
26
+ * Canonical membership digest for an explicit peer-id list — the same digest {@link membershipDigest}
27
+ * derives from a peer map's keys. ONE implementation, so a verifier reading a stored `peerIds` list
28
+ * (e.g. a persisted block commit proof) and a coordinator reading live `ClusterPeers` can never
29
+ * disagree. Sorts a copy; the caller's array is not mutated.
30
+ */
31
+ export async function membershipDigestFromIds(ids) {
32
+ const sorted = [...ids].sort();
33
+ const bytes = new TextEncoder().encode(canonicalJson(sorted));
30
34
  const hash = await sha256.digest(bytes);
31
35
  return uint8ArrayToString(hash.digest, 'base64url');
32
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"membership.js","sourceRoot":"","sources":["../../../src/cluster/membership.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"membership.js","sourceRoot":"","sources":["../../../src/cluster/membership.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAU,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAmB;IACzD,OAAO,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,GAAsB;IACnE,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAqE;IAC3G,OAAO,MAAM,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAAoB,EAAE,MAAe;IACpF,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,WAAmB,EAAE,OAAoB,EAAE,MAAe;IACzG,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9F,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,WAAmB,EAAE,OAAoB,EAAE,QAAmC,EAAE,MAAe;IAC7I,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACrD,CAAC"}
@@ -62,8 +62,15 @@ export type ClusterRecord = {
62
62
  membershipVersion?: 1 | 2;
63
63
  /** Membership digest of {@link ClusterRecord.peers}; present iff `membershipVersion === 2`. base64url. */
64
64
  membershipDigest?: string;
65
+ /**
66
+ * The transaction's operations and the block the coordinator selected the cohort by
67
+ * ({@link RepoMessage.coordinatingBlockIds}). There is deliberately NO top-level copy of the
68
+ * coordinating block ids on the record: `messageHash` covers `message` only, so a duplicate at
69
+ * this level would be outside every hash and any relaying peer could rewrite it — and the
70
+ * membership admission gate derives its own cohort view from exactly that id
71
+ * (`ClusterMember.deriveExpectedClusterView`). One source of truth, inside the hash.
72
+ */
65
73
  message: RepoMessage;
66
- coordinatingBlockIds?: string[];
67
74
  promises: {
68
75
  [peerId: string]: Signature;
69
76
  };
@@ -183,5 +190,24 @@ export interface ClusterConsensusConfig {
183
190
  * single-node / local dev where you knowingly run below the safe floor.
184
191
  */
185
192
  allowUnvalidatedSmallCluster?: boolean;
193
+ /**
194
+ * What a member WITH a transaction validator does with a pend that carries no `validation`
195
+ * payload. See {@link UnvalidatablePendPolicy}; default 'accept'.
196
+ */
197
+ unvalidatablePendPolicy?: UnvalidatablePendPolicy;
186
198
  }
199
+ /**
200
+ * What a receiver WITH a transaction checker does with a pend that carries no
201
+ * {@link PendRequest.validation} payload — the single-collection (`Collection.sync`) shape, which
202
+ * has no transaction to re-execute.
203
+ *
204
+ * - `'accept'` (default) preserves the historical behaviour: the pend is approved unchecked.
205
+ * - `'reject'` is the fail-closed posture for a deployment that has decided every write must be
206
+ * re-checkable; it REFUSES `Collection.sync` writes, which is the point, not a bug.
207
+ *
208
+ * Irrelevant on a receiver with no checker, which never re-checks anything. Named once here and
209
+ * referenced by every tier that carries the knob (`ClusterConsensusConfig`, db-p2p's
210
+ * `ClusterPolicyOptions` and `StorageRepoOptions`) so the three cannot drift apart.
211
+ */
212
+ export type UnvalidatablePendPolicy = 'accept' | 'reject';
187
213
  //# sourceMappingURL=structs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../../src/cluster/structs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../../src/cluster/structs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE;AAC9D;;;;;GAKG;GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,CAE3G;AAED,8GAA8G;AAC9G,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,UAAU,CAM7F;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,CAAC,EAAE,EAAE,MAAM,GAAG;QACb,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,wDAAwD;QACxD,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;CACF,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAC1B,0GAA0G;IAC1G,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE;QAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC1C,OAAO,EAAE;QAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACzC,8FAA8F;IAC9F,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,eAAe,CAAC,EAAE;QACjB,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,aAAa,EAAE;YAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;KAC5C,CAAC;CACF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,MAAM,WAAW,sBAAsB;IACtC,4GAA4G;IAC5G,sBAAsB,EAAE,MAAM,CAAC;IAC/B,kEAAkE;IAClE,uBAAuB,EAAE,MAAM,CAAC;IAChC,gDAAgD;IAChD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,0EAA0E;IAC1E,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,6EAA6E;IAC7E,wBAAwB,EAAE,MAAM,CAAC;IACjC,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sFAAsF;IACtF,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,wEAAwE;IACxE,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,sDAAsD;IACtD,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,gEAAgE;IAChE,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,iFAAiF;IACjF,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,gLAAgL;IAChL,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;IAC7C,4GAA4G;IAC5G,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gIAAgI;IAChI,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC;;;OAGG;IACH,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"structs.js","sourceRoot":"","sources":["../../../src/cluster/structs.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,IAAuB,EAAE,KAAc;IAC9F,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,8GAA8G;AAC9G,MAAM,UAAU,8BAA8B,CAAC,IAAY,EAAE,SAAoB;IAChF,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QACxF,KAAK,UAAU,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAC5F,OAAO,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC;AACF,CAAC;AA2CD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"structs.js","sourceRoot":"","sources":["../../../src/cluster/structs.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,IAAuB,EAAE,KAAc;IAC9F,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,8GAA8G;AAC9G,MAAM,UAAU,8BAA8B,CAAC,IAAY,EAAE,SAAoB;IAChF,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QACxF,KAAK,UAAU,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAC5F,OAAO,CAAC,CAAC,OAAO,yBAAyB,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC;AACF,CAAC;AAkDD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC"}
@@ -23,4 +23,18 @@ export type ActionContext = {
23
23
  /** Optional uncommitted pending action ID */
24
24
  actionId?: ActionId;
25
25
  };
26
+ /** The id of the action that produced `rev` within `context`'s uncheckpointed committed list,
27
+ * or `undefined` when the list names no action at that revision.
28
+ *
29
+ * `undefined` is legitimate, not an error, and has three causes: the revision's log slot belongs
30
+ * to an entry that carries no action (a CHECKPOINT or an INVALIDATION entry takes a revision of
31
+ * its own); `rev` predates the most recent checkpoint, which is as far back as a context read off
32
+ * a log reaches (`Log.getActionContext`, `Log.getFrom`); or the context was never built from a
33
+ * log at all. A caller printing this must carry a placeholder rather than invent an id.
34
+ *
35
+ * NOTE: linear in `committed`, which grows one entry per commit between context reads; fine now —
36
+ * every caller is a `debug`-gated diagnostic, so this does not run on a normal path at all. If a
37
+ * non-diagnostic caller ever appears, index the lookup or search from the end (the entry at the
38
+ * context's own `rev` is normally the last one). */
39
+ export declare function actionIdAt(context: ActionContext, rev: number): ActionId | undefined;
26
40
  //# sourceMappingURL=action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/collection/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;IACR,kEAAkE;IAClE,WAAW,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAElH,MAAM,MAAM,SAAS,GAAG;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG;IAC3B,kDAAkD;IAClD,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC"}
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/collection/action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;IACR,kEAAkE;IAClE,WAAW,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAElH,MAAM,MAAM,SAAS,GAAG;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG;IAC3B,kDAAkD;IAClD,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;oDAYoD;AACpD,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAEpF"}
@@ -1,2 +1,17 @@
1
- export {};
1
+ /** The id of the action that produced `rev` within `context`'s uncheckpointed committed list,
2
+ * or `undefined` when the list names no action at that revision.
3
+ *
4
+ * `undefined` is legitimate, not an error, and has three causes: the revision's log slot belongs
5
+ * to an entry that carries no action (a CHECKPOINT or an INVALIDATION entry takes a revision of
6
+ * its own); `rev` predates the most recent checkpoint, which is as far back as a context read off
7
+ * a log reaches (`Log.getActionContext`, `Log.getFrom`); or the context was never built from a
8
+ * log at all. A caller printing this must carry a placeholder rather than invent an id.
9
+ *
10
+ * NOTE: linear in `committed`, which grows one entry per commit between context reads; fine now —
11
+ * every caller is a `debug`-gated diagnostic, so this does not run on a normal path at all. If a
12
+ * non-diagnostic caller ever appears, index the lookup or search from the end (the entry at the
13
+ * context's own `rev` is normally the last one). */
14
+ export function actionIdAt(context, rev) {
15
+ return context.committed.find(entry => entry.rev === rev)?.actionId;
16
+ }
2
17
  //# sourceMappingURL=action.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","sourceRoot":"","sources":["../../../src/collection/action.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../../src/collection/action.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;oDAYoD;AACpD,MAAM,UAAU,UAAU,CAAC,OAAsB,EAAE,GAAW;IAC7D,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC;AACrE,CAAC"}
@@ -62,13 +62,53 @@ export declare class Collection<TAction> implements ICollection<TAction> {
62
62
  /** Tracked Changes */
63
63
  readonly tracker: Tracker<IBlock>;
64
64
  private readonly filterConflict?;
65
+ /** Short random tag naming THIS instance (see {@link newInstanceTag}). Open paths generate
66
+ * it BEFORE construction (so pre-construction diagnostics such as attachToLog can carry it);
67
+ * the default covers direct construction in tests. */
68
+ readonly instanceTag: string;
65
69
  private pending;
66
70
  private readonly latchId;
71
+ /** The action id of a write currently in flight ON THIS INSTANCE'S BEHALF, or `undefined`
72
+ * outside a write. Read by {@link updateInternal}: if the committed log now carries an entry
73
+ * under this id, that action's work is already durable despite the failure answer that sent us
74
+ * back here — `NetworkTransactor.commit` commits the collection header and log tail BEFORE
75
+ * sweeping the remaining blocks, so a later sweep block confirming a conflict reports failure
76
+ * over an action whose log entry already landed. Such an entry is CONSUMED
77
+ * ({@link consumeOwnEntry}) rather than replayed, because replaying re-appends content the
78
+ * committed tail already carries, producing a duplicate entry under one action id at two
79
+ * revisions.
80
+ *
81
+ * The collection owns this fact rather than taking it as a `updateInternal` argument so that no
82
+ * refresh path can forget to supply it — {@link update} and {@link updateAndSync} are refreshes
83
+ * on behalf of a READER, the field is unset for them, and the consume branch cannot fire. Before
84
+ * this was a field, `TransactionCoordinator.commit`'s inter-attempt refresh went through
85
+ * `update()` and was therefore indistinguishable from a reader refresh even though the
86
+ * coordinator held the very id it was retrying.
87
+ *
88
+ * LIFETIME is the whole attempt CYCLE, not the latched span: it must survive the refresh
89
+ * BETWEEN a failed attempt and its retry, which is the only moment it is ever read. In
90
+ * {@link syncInternal} that cycle is contained inside the collection latch `sync()` holds; in
91
+ * `TransactionCoordinator.commit` the inter-attempt `update()` runs OUTSIDE the commit latch
92
+ * span by design (`Latches` is non-reentrant), so the coordinator's clear necessarily runs
93
+ * latch-free. That is safe: this is a single field write, {@link beginInFlightAction}'s
94
+ * disposer only clears an id it still owns, and the only reader runs under the latch — so the
95
+ * worst a foreign concurrent refresh can see is a cleared field (it stops consuming), never a
96
+ * field it should not have consumed. */
97
+ private inFlightActionId?;
67
98
  protected constructor(id: CollectionId, transactor: ITransactor, handlers: Record<ActionType, ActionHandler<TAction>>, source: TransactorSource<IBlock>,
68
99
  /** Cache of unmodified blocks from the source */
69
100
  sourceCache: CacheSource<IBlock>,
70
101
  /** Tracked Changes */
71
- tracker: Tracker<IBlock>, filterConflict?: ((action: Action<TAction>, potential: Action<TAction>[]) => Action<TAction> | undefined) | undefined);
102
+ tracker: Tracker<IBlock>, filterConflict?: ((action: Action<TAction>, potential: Action<TAction>[]) => Action<TAction> | undefined) | undefined,
103
+ /** Short random tag naming THIS instance (see {@link newInstanceTag}). Open paths generate
104
+ * it BEFORE construction (so pre-construction diagnostics such as attachToLog can carry it);
105
+ * the default covers direct construction in tests. */
106
+ instanceTag?: string);
107
+ /** A fresh instance tag: four random bytes rendered base64url — six characters, enough that
108
+ * two instances over one collection id do not collide by accident, short enough to ride on
109
+ * every trace line (same shape as the node tag in quereus-plugin-optimystic's
110
+ * collection-factory). Scopes {@link latchId} per instance and labels diagnostics. */
111
+ private static newInstanceTag;
72
112
  /** Open an EXISTING collection.
73
113
  *
74
114
  * Resolves to `undefined` when the header block probe comes back empty — an
@@ -105,13 +145,145 @@ export declare class Collection<TAction> implements ICollection<TAction> {
105
145
  * repeats the same doomed request because each retry re-runs the same losing read.
106
146
  *
107
147
  * Equal revisions still adopt `next`: the rev is unchanged but its `committed` list may be
108
- * more complete than what we hold. */
148
+ * more complete than what we hold.
149
+ *
150
+ * This is also the one seam where lineage divergence is observable: at every revision BOTH
151
+ * sides name an action for, the two ids must agree. Revision
152
+ * numbers are per-collection counters, so two separately-built copies under one id can each
153
+ * occupy the same revision with DIFFERENT actions while each stays internally self-consistent
154
+ * — {@link reportShortfall} structurally cannot see that (its two numbers come from one
155
+ * chain), and this is the only place two `committed` lists meet. Naming different actions at
156
+ * one revision proves the two sides are different lineages (`collection:lineage-divergence`;
157
+ * see docs/debugging.md § "Did the refresh itself fail to close the gap?"). WHICH two sides
158
+ * depends on the caller, and the line says so in `site=` — see {@link DivergenceSite}, which
159
+ * defines the two values and what each one indicts.
160
+ *
161
+ * Every line from here also carries `tag=`, the {@link Collection.instanceTag} of the handle
162
+ * reporting. One process routinely holds several handles on one collection id; without the
163
+ * tag, two handles' lines interleave into what reads like one handle contradicting itself.
164
+ *
165
+ * Logs, does not throw — same reasoning as {@link reportShortfall}: `update()` runs
166
+ * blanket-style over every registered collection between commit retries, and aborting here
167
+ * would promote a diagnosis to production behaviour before the line has ever been seen to
168
+ * fire in the wild. Adoption then proceeds unchanged, which means the line is a PER-DISCOVERY
169
+ * report, not a per-refresh one: adopting `next` overwrites the held lineage marker with the
170
+ * log's, so the next refresh of this instance compares log-to-log and stays silent — even
171
+ * though block content materialized under the old lineage may still be in caches. The line
172
+ * marks the refresh that first observed the disagreement.
173
+ *
174
+ * NOTE: adoption resolves the CONTEXT disagreement, not the content one — the read caches on
175
+ * this instance still hold blocks materialized under the old lineage, and since the revision
176
+ * did not change nothing re-reads them. Conditional today: no fork has been reproduced (see
177
+ * the still-open upstream reproducer), so this instrument exists to find out whether one
178
+ * happens at all. If the line is ever seen firing in the field, decide then whether a
179
+ * divergence should also drop the read cache (and whether to keep re-reporting per refresh)
180
+ * — that is a behaviour change, and this seam deliberately makes none.
181
+ *
182
+ * The comparison is {@link earliestFork}, not a single lookup at the current revision: the
183
+ * two `committed` lists overlap across several revisions, and the LOWEST one they disagree at
184
+ * is where the lineages actually parted — a fork below the current revision was previously
185
+ * silent. `forkRev=` names it, `heldAction=`/`readAction=` are the two ids AT it, and
186
+ * `heldRev=`/`readRev=` are the two contexts' own revisions, so the line says both where the
187
+ * split began and how far each side has since travelled.
188
+ *
189
+ * The refusal line reports its two action ids at `readRev=` — the read's revision — on BOTH
190
+ * sides, because that is the only revision the two can be compared at: `next` never names an
191
+ * action above its own revision, so looking each side up at its own revision would compare
192
+ * different revisions and print two different ids for one honest lineage. Equal ids there mean
193
+ * the read is an older view of THIS lineage (ordinary lag, correctly refused); different ids
194
+ * mean a fork; `none` on the held side means this handle's own list does not reach back to the
195
+ * read's revision — the signature of a context bootstrapped from an over-claiming tail (see
196
+ * the NOTE in {@link bootstrapContext}), which is exactly the case {@link earliestFork} has no
197
+ * shared revision to report on.
198
+ *
199
+ * Gated on `log.enabled`, like every {@link actionIdAt} caller: the comparison buys nothing
200
+ * when the line has no sink, and the lists — one entry per commit between context reads,
201
+ * truncated at each checkpoint — are only walked on a run that has the namespace turned on.
202
+ * Silence proves nothing either way, because a revision is only comparable when BOTH sides
203
+ * name an action for it: an invented collection has no context at all, a revision slot the log
204
+ * gave to a checkpoint or invalidation entry names none, and a revision older than the read
205
+ * log's most recent checkpoint has already fallen off the read side's list. */
109
206
  private static advanceContext;
207
+ /** The EARLIEST revision the two contexts provably disagree about: the lowest revision both
208
+ * `committed` lists name an action for, where the two ids differ.
209
+ *
210
+ * Comparing only at the holder's current revision — what this used to do — misses a fork that
211
+ * began earlier and has since been overtaken by same-numbered commits on both sides, which is
212
+ * the shape a replica that forked and kept writing actually has. Taking the lowest disagreeing
213
+ * revision instead names the split point rather than an arbitrary later symptom of it.
214
+ *
215
+ * Revisions only one side names are skipped, not treated as disagreement: {@link actionIdAt}'s
216
+ * `undefined` is legitimate (checkpoint/invalidation slots, and revisions that predate the
217
+ * other side's most recent checkpoint), so a one-sided entry is missing evidence, not evidence
218
+ * of a fork.
219
+ *
220
+ * NOTE: linear in the two lists, which hold one entry per commit between context reads and
221
+ * truncate at each checkpoint. Every caller is `log.enabled`-gated, so this does not run at
222
+ * all on a normal run; if a non-diagnostic caller ever appears, index by revision instead. */
223
+ private static earliestFork;
224
+ /** Report a refresh that failed to move FORWARDS past a revision it had already read for
225
+ * itself — the sibling of {@link advanceContext}'s `collection:context-not-lowered`, which
226
+ * reports a collection declining to move BACKWARDS.
227
+ *
228
+ * `tailRev` is what the committed log tail claimed is committed under this id; `after` is
229
+ * where a SEPARATE read path (the chain walk) actually landed. Landing below the claim means
230
+ * this refresh closed nothing, which from outside the class is otherwise indistinguishable
231
+ * from "there was nothing newer to adopt".
232
+ *
233
+ * This detects LAG, and only lag. It CANNOT see lineage divergence: both of its numbers
234
+ * come from the same chain — `tailRev` off the tail block this collection's own header
235
+ * names, `after` from a walk of that same chain — and a forked replica is internally
236
+ * self-consistent, its tail claiming exactly what its own walk reaches. Two copies of one
237
+ * collection id holding the same revision under different actions therefore keep this line
238
+ * silent forever. That case is `collection:lineage-divergence`, reported from
239
+ * {@link advanceContext}, which compares action ids — the one value comparable across
240
+ * copies — rather than revision counters.
241
+ *
242
+ * Carries the same `tag=` as {@link advanceContext}'s lines, and for the same reason: several
243
+ * handles on one collection id otherwise read as one self-contradicting handle.
244
+ *
245
+ * Logs, does not throw: `update()` is called blanket-style over every registered collection
246
+ * between commit retries, and a shortfall is not yet known to be illegitimate — an abort here
247
+ * would promote an unproven diagnosis to production behaviour. Deliberately does NOT adopt
248
+ * `tailRev` either: the two numbers come from different read paths, and papering over the
249
+ * disagreement destroys the evidence this line exists to produce. */
250
+ private static reportShortfall;
110
251
  act(...actions: Action<TAction>[]): Promise<void>;
111
252
  private actInternal;
112
253
  private internalTransact;
113
254
  /** Load external changes and update our context to the latest log revision - resolve any conflicts with our pending actions. */
114
255
  update(): Promise<void>;
256
+ /** Drops the pending actions this sync's OWN committed entry already made durable, instead of
257
+ * replaying them into a duplicate entry (see {@link inFlightActionId}).
258
+ *
259
+ * `addActions` wrote exactly the snapshot pending list under this action id, and the entry's
260
+ * actions are therefore the LEADING `entry.actions.length` items of `this.pending` — anything
261
+ * staged since is behind them, because {@link actInternal} appends. Under
262
+ * {@link syncInternal} nothing can even be staged mid-cycle (`act()` shares the collection
263
+ * latch); under `TransactionCoordinator.commit` the mark spans a latch-free inter-attempt
264
+ * window, so an `act()` there CAN grow `pending` — still only at the tail, so the slice stays
265
+ * right.
266
+ *
267
+ * The guard below is the load-bearing part: `slice` fails SILENTLY if that correspondence ever
268
+ * breaks, dropping actions that were never committed, so an entry longer than `pending` throws
269
+ * instead of losing work. (See the sibling note on `syncInternal`'s post-commit replay, which
270
+ * rests on the same invariant.) */
271
+ private consumeOwnEntry;
272
+ /** Maps each pending action to its effective form against a remote entry: the original, a
273
+ * replacement, or dropped. A replacement or a discard changes the pending set; the tracker still
274
+ * holds the pre-filter transforms, so report it as mutated to force a replay that re-stages
275
+ * against the effective actions. Identity comparison per the contract: keep => same instance,
276
+ * replace => new instance.
277
+ * NOTE: a filterConflict hook that always allocates a fresh (but equal) instance instead of
278
+ * returning the same one forces a replay on every update — if that ever shows up as a hot path,
279
+ * compare by value/id here instead of by reference. */
280
+ private filterAgainstEntry;
281
+ /** Refresh this instance against the stored log: adopt the latest committed revision, resolve
282
+ * pending actions against everything that landed since, and replay them if anything conflicts.
283
+ *
284
+ * Takes no in-flight action id — it reads {@link inFlightActionId} off `this`, which is set for
285
+ * exactly the write attempt cycles that own one (see that field). Callers cannot get this wrong
286
+ * by omission. */
115
287
  private updateInternal;
116
288
  /** Capture the current staged state — tracker transforms plus the pending
117
289
  * action queue — so it can be restored later via {@link restorePending}.
@@ -195,6 +367,48 @@ export declare class Collection<TAction> implements ICollection<TAction> {
195
367
  * through {@link updateAndSync}, so an unnecessary flush still pays a full,
196
368
  * cache-bypassing {@link update} before discovering it has nothing to do. */
197
369
  hasUnsyncedChanges(): boolean;
370
+ /** The committed revision this collection currently READS at, or `undefined` for an
371
+ * INVENTED collection that has never adopted a committed revision
372
+ * ({@link createOrOpen} found no header and staged a fresh empty one).
373
+ *
374
+ * Not the revision a pending write will land at: {@link getNextRev} is this plus one
375
+ * (`undefined` counting as 0), so a diagnostic that prints this value BEFORE a commit
376
+ * is naming the revision the commit will supersede, not the one it produces.
377
+ *
378
+ * DIAGNOSTIC ONLY — do not branch on this. Every block this collection reads is
379
+ * materialized at this revision ({@link TransactorSource.tryGet} passes it as the
380
+ * read context), and the revision advances ONLY through an explicit call on THIS
381
+ * instance — {@link update} or {@link sync} on the single-node path, or
382
+ * {@link recordCommitted} when a {@link TransactionCoordinator} commits this
383
+ * collection (the session/consensus path, where no `update()` is involved at all).
384
+ * Nothing moves it passively — not time, not another collection's
385
+ * commit, not a peer's notification. So a collection sitting here at a lagging
386
+ * revision silently serves an old root with no error, and two collections in one
387
+ * process can be at different revisions at the same instant. That gap is invisible
388
+ * from outside the class without this accessor, which is the whole reason it
389
+ * exists: `docs/debugging.md` (§ "Which revision did a read descend?") explains
390
+ * how an operator reads the difference. */
391
+ committedRevision(): number | undefined;
392
+ /** The id of the action that PRODUCED the revision {@link committedRevision} reports —
393
+ * this collection's lineage marker at that revision — or `undefined` when the action
394
+ * context holds no entry at the current revision.
395
+ *
396
+ * `undefined` is legitimate, not an error: an INVENTED collection has no context at
397
+ * all, and otherwise {@link actionIdAt} resolves nothing at the current revision for
398
+ * the reasons listed there. A caller printing this must therefore carry a placeholder
399
+ * rather than invent an id. The contexts this class writes itself
400
+ * ({@link recordCommitted}, the inline bump in `syncInternal`,
401
+ * {@link bootstrapContext}) always do hold one.
402
+ *
403
+ * DIAGNOSTIC ONLY — do not branch on this. Its value is the one thing about a revision
404
+ * that IS comparable across collections and across nodes: a revision number is
405
+ * per-collection and says nothing on its own, so two nodes reporting the same
406
+ * collection id at the same revision are indistinguishable between "one collection,
407
+ * one node lagging" and "two separately-built collections each counting from 1". Equal
408
+ * action ids mean one lineage; different action ids at the same revision mean two.
409
+ * `docs/debugging.md` (§ "Which revision did a read descend?") spells out how an
410
+ * operator reads the pair. */
411
+ committedActionId(): ActionId | undefined;
198
412
  /** Fold a just-committed set of transforms into this collection's read cache
199
413
  * so subsequent reads (and stages) through THIS instance observe the committed
200
414
  * state, mirroring what {@link sync} does inline after a successful transact.
@@ -213,11 +427,66 @@ export declare class Collection<TAction> implements ICollection<TAction> {
213
427
  getNextRev(): number;
214
428
  /** Record a just-committed action: append its ActionRev to the committed list
215
429
  * and advance the revision. Returns the new revision. Mirrors the inline bump
216
- * in {@link syncInternal}. */
217
- recordCommitted(actionId: ActionId): number;
430
+ * in {@link syncInternal} — which needs no such rev check because it computes and
431
+ * uses its `newRev` inside one latched span.
432
+ *
433
+ * @param rev - the revision this action was PENDED at, captured once (at the log
434
+ * append in `TransactionCoordinator.applyActionsToCollection`) and threaded through
435
+ * the pend/commit round trips. Storage assigned the action THAT number; recording it
436
+ * at any other would fork this instance's revision counter from storage permanently
437
+ * (context adoption is one-way — see {@link advanceContext}). With the coordinator
438
+ * holding this instance's latch across the whole commit span the mismatch cannot
439
+ * happen; the throw is the tripwire for any path that still bypasses the latch. */
440
+ recordCommitted(actionId: ActionId, rev: number): number;
441
+ /** Acquire this instance's latch — the same mutex {@link act}, {@link update},
442
+ * {@link sync}, and {@link updateAndSync} serialize behind — returning its release.
443
+ * Exists so a TransactionCoordinator can hold the latch across its WHOLE commit span
444
+ * (log append → pend → commit → local fold), keeping any refresh of this instance from
445
+ * interleaving with a mid-flight commit. `Latches` is non-reentrant: while holding this,
446
+ * the holder must not call any of those latched methods on this instance. The caller
447
+ * MUST call the release exactly once, in a `finally`. */
448
+ acquireLatch(): Promise<() => void>;
449
+ /** Bracket a write attempt cycle on this instance under `actionId`, so a refresh taken between
450
+ * a failed attempt and its retry recognises that action's own already-durable log entry (see
451
+ * {@link inFlightActionId} for why, and for the lifetime this must span).
452
+ *
453
+ * The returned disposer clears the mark and MUST be called in a `finally` covering every exit
454
+ * from the retry cycle — return, retry exhaustion, partial commit, hard error, abort. A mark
455
+ * left behind would let a LATER, unrelated refresh consume a foreign entry that happens to
456
+ * carry the same id. The clear is id-guarded, so a disposer whose mark has since been replaced
457
+ * by another attempt is a no-op rather than wiping the newer one; disposers may therefore be
458
+ * called out of order and more than once. Shaped like {@link acquireLatch} deliberately: a
459
+ * disposer is harder to forget than a paired `end…` call.
460
+ *
461
+ * A concurrent READER's refresh landing in that latch-free window is fine, and is reachable
462
+ * today (`OptimysticModule` declares `concurrencyMode = 'reentrant-reads'`, so scans inside a
463
+ * transaction share one instance and only serialize on the latch). Consuming is a property of
464
+ * the (instance, action id) pair, not of who calls: whoever refreshes first drops exactly the
465
+ * durable entry's actions and the write's own later refresh then finds nothing new. The leading
466
+ * slice stays right because {@link actInternal} APPENDS, so anything staged after the entry was
467
+ * written survives it.
468
+ *
469
+ * NOTE: a concurrent WRITER is the hazard. The mark deliberately outlives the latch (see
470
+ * {@link inFlightActionId}), so two writes overlapping on ONE instance can trample each other's:
471
+ * a second write that acquires the latch between the first's failed attempt and its
472
+ * retry-refresh replaces the id, and the first's refresh then reads the SECOND write's id —
473
+ * consuming that write's durable entry and dropping pending actions of its own that never
474
+ * landed. Not reachable today: a coordinator commit and a `sync()` both run on one session call
475
+ * path, which is the same assumption the participant selection in
476
+ * `TransactionCoordinator.commitOnce` and its `rollback` already rest on. If a second writer is
477
+ * ever allowed to drive the SAME instance concurrently, this must become a per-attempt token (a
478
+ * mark object compared by identity, refusing to replace a live one) rather than a bare id. */
479
+ beginInFlightAction(actionId: ActionId): () => void;
218
480
  /** Push our pending actions to the transactor */
219
481
  sync(options?: SyncOptions): Promise<void>;
482
+ /** Mints the one action id this sync reuses across all of its retry attempts, and owns it for
483
+ * the WHOLE cycle — including the inter-attempt refresh, which is the only thing that reads it
484
+ * (see {@link inFlightActionId}). `sync()`/`updateAndSync()` hold the collection latch across
485
+ * all of this, so the mark's lifetime is contained inside the latched span here; the disposer
486
+ * runs on every exit, including a throw out of retry exhaustion or an abort. */
220
487
  private syncInternal;
488
+ /** The retry loop behind {@link syncInternal}, run with `actionId` already marked in flight. */
489
+ private syncAttempts;
221
490
  updateAndSync(options?: SyncOptions): Promise<void>;
222
491
  selectLog(forward?: boolean): AsyncIterableIterator<Action<TAction>>;
223
492
  private replayActions;
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../../src/collection/collection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../../src/collection/collection.ts"],"names":[],"mappings":"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"}