@optimystic/db-core 0.14.1 → 0.16.3

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 (301) hide show
  1. package/dist/src/blocks/block-store.d.ts +18 -1
  2. package/dist/src/blocks/block-store.d.ts.map +1 -1
  3. package/dist/src/blocks/helpers.d.ts +2 -2
  4. package/dist/src/blocks/helpers.d.ts.map +1 -1
  5. package/dist/src/blocks/helpers.js +2 -2
  6. package/dist/src/blocks/helpers.js.map +1 -1
  7. package/dist/src/btree/btree.d.ts +27 -6
  8. package/dist/src/btree/btree.d.ts.map +1 -1
  9. package/dist/src/btree/btree.js +104 -61
  10. package/dist/src/btree/btree.js.map +1 -1
  11. package/dist/src/chain/chain.d.ts.map +1 -1
  12. package/dist/src/chain/chain.js +11 -1
  13. package/dist/src/chain/chain.js.map +1 -1
  14. package/dist/src/cluster/index.d.ts +1 -0
  15. package/dist/src/cluster/index.d.ts.map +1 -1
  16. package/dist/src/cluster/index.js +1 -0
  17. package/dist/src/cluster/index.js.map +1 -1
  18. package/dist/src/cluster/membership.d.ts +49 -0
  19. package/dist/src/cluster/membership.d.ts.map +1 -0
  20. package/dist/src/cluster/membership.js +75 -0
  21. package/dist/src/cluster/membership.js.map +1 -0
  22. package/dist/src/cluster/structs.d.ts +50 -1
  23. package/dist/src/cluster/structs.d.ts.map +1 -1
  24. package/dist/src/cluster/structs.js +9 -1
  25. package/dist/src/cluster/structs.js.map +1 -1
  26. package/dist/src/cohort-topic/addressing.d.ts +8 -2
  27. package/dist/src/cohort-topic/addressing.d.ts.map +1 -1
  28. package/dist/src/cohort-topic/addressing.js +11 -3
  29. package/dist/src/cohort-topic/addressing.js.map +1 -1
  30. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +13 -7
  31. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -1
  32. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +17 -6
  33. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -1
  34. package/dist/src/cohort-topic/antidos/replay-guard.d.ts +17 -0
  35. package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -1
  36. package/dist/src/cohort-topic/antidos/replay-guard.js +33 -0
  37. package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -1
  38. package/dist/src/cohort-topic/antidos/topic-budget.d.ts +7 -0
  39. package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -1
  40. package/dist/src/cohort-topic/antidos/topic-budget.js +8 -2
  41. package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -1
  42. package/dist/src/cohort-topic/coldstart.d.ts +34 -14
  43. package/dist/src/cohort-topic/coldstart.d.ts.map +1 -1
  44. package/dist/src/cohort-topic/coldstart.js +25 -11
  45. package/dist/src/cohort-topic/coldstart.js.map +1 -1
  46. package/dist/src/cohort-topic/gossip/bus.d.ts +12 -1
  47. package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -1
  48. package/dist/src/cohort-topic/gossip/bus.js +27 -1
  49. package/dist/src/cohort-topic/gossip/bus.js.map +1 -1
  50. package/dist/src/cohort-topic/gossip/view.d.ts +8 -0
  51. package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -1
  52. package/dist/src/cohort-topic/gossip/view.js +17 -1
  53. package/dist/src/cohort-topic/gossip/view.js.map +1 -1
  54. package/dist/src/cohort-topic/member-engine.d.ts +10 -3
  55. package/dist/src/cohort-topic/member-engine.d.ts.map +1 -1
  56. package/dist/src/cohort-topic/member-engine.js +29 -14
  57. package/dist/src/cohort-topic/member-engine.js.map +1 -1
  58. package/dist/src/cohort-topic/membership/publisher.d.ts +14 -7
  59. package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -1
  60. package/dist/src/cohort-topic/membership/publisher.js +15 -27
  61. package/dist/src/cohort-topic/membership/publisher.js.map +1 -1
  62. package/dist/src/cohort-topic/membership/verifier.d.ts +35 -0
  63. package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -1
  64. package/dist/src/cohort-topic/membership/verifier.js +88 -3
  65. package/dist/src/cohort-topic/membership/verifier.js.map +1 -1
  66. package/dist/src/cohort-topic/promotion.d.ts +7 -0
  67. package/dist/src/cohort-topic/promotion.d.ts.map +1 -1
  68. package/dist/src/cohort-topic/promotion.js +16 -2
  69. package/dist/src/cohort-topic/promotion.js.map +1 -1
  70. package/dist/src/cohort-topic/registration/renewal.d.ts +18 -0
  71. package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -1
  72. package/dist/src/cohort-topic/registration/renewal.js +54 -0
  73. package/dist/src/cohort-topic/registration/renewal.js.map +1 -1
  74. package/dist/src/cohort-topic/registration/types.d.ts +12 -0
  75. package/dist/src/cohort-topic/registration/types.d.ts.map +1 -1
  76. package/dist/src/cohort-topic/registration/types.js +14 -0
  77. package/dist/src/cohort-topic/registration/types.js.map +1 -1
  78. package/dist/src/cohort-topic/service.d.ts +4 -3
  79. package/dist/src/cohort-topic/service.d.ts.map +1 -1
  80. package/dist/src/cohort-topic/service.js +38 -12
  81. package/dist/src/cohort-topic/service.js.map +1 -1
  82. package/dist/src/cohort-topic/sig/payloads.d.ts +23 -7
  83. package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -1
  84. package/dist/src/cohort-topic/sig/payloads.js +22 -6
  85. package/dist/src/cohort-topic/sig/payloads.js.map +1 -1
  86. package/dist/src/cohort-topic/traffic.d.ts +6 -0
  87. package/dist/src/cohort-topic/traffic.d.ts.map +1 -1
  88. package/dist/src/cohort-topic/traffic.js +16 -1
  89. package/dist/src/cohort-topic/traffic.js.map +1 -1
  90. package/dist/src/cohort-topic/walk.d.ts +22 -7
  91. package/dist/src/cohort-topic/walk.d.ts.map +1 -1
  92. package/dist/src/cohort-topic/walk.js +78 -18
  93. package/dist/src/cohort-topic/walk.js.map +1 -1
  94. package/dist/src/cohort-topic/wire/codec.d.ts +3 -1
  95. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -1
  96. package/dist/src/cohort-topic/wire/codec.js +7 -1
  97. package/dist/src/cohort-topic/wire/codec.js.map +1 -1
  98. package/dist/src/cohort-topic/wire/index.d.ts +1 -1
  99. package/dist/src/cohort-topic/wire/index.d.ts.map +1 -1
  100. package/dist/src/cohort-topic/wire/index.js +1 -1
  101. package/dist/src/cohort-topic/wire/index.js.map +1 -1
  102. package/dist/src/cohort-topic/wire/payloads.d.ts +5 -4
  103. package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -1
  104. package/dist/src/cohort-topic/wire/payloads.js +14 -5
  105. package/dist/src/cohort-topic/wire/payloads.js.map +1 -1
  106. package/dist/src/cohort-topic/wire/primitives.d.ts +65 -0
  107. package/dist/src/cohort-topic/wire/primitives.d.ts.map +1 -0
  108. package/dist/src/cohort-topic/wire/primitives.js +173 -0
  109. package/dist/src/cohort-topic/wire/primitives.js.map +1 -0
  110. package/dist/src/cohort-topic/wire/types.d.ts +132 -5
  111. package/dist/src/cohort-topic/wire/types.d.ts.map +1 -1
  112. package/dist/src/cohort-topic/wire/validate.d.ts +15 -5
  113. package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -1
  114. package/dist/src/cohort-topic/wire/validate.js +146 -123
  115. package/dist/src/cohort-topic/wire/validate.js.map +1 -1
  116. package/dist/src/collection/collection.d.ts +23 -12
  117. package/dist/src/collection/collection.d.ts.map +1 -1
  118. package/dist/src/collection/collection.js +111 -35
  119. package/dist/src/collection/collection.js.map +1 -1
  120. package/dist/src/collection/struct.d.ts +37 -2
  121. package/dist/src/collection/struct.d.ts.map +1 -1
  122. package/dist/src/collection/struct.js +16 -1
  123. package/dist/src/collection/struct.js.map +1 -1
  124. package/dist/src/collections/tree/tree.d.ts +18 -1
  125. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  126. package/dist/src/collections/tree/tree.js +36 -6
  127. package/dist/src/collections/tree/tree.js.map +1 -1
  128. package/dist/src/index.d.ts +1 -0
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +1 -0
  131. package/dist/src/index.js.map +1 -1
  132. package/dist/src/log/log.d.ts +26 -3
  133. package/dist/src/log/log.d.ts.map +1 -1
  134. package/dist/src/log/log.js +34 -7
  135. package/dist/src/log/log.js.map +1 -1
  136. package/dist/src/log/struct.d.ts +5 -4
  137. package/dist/src/log/struct.d.ts.map +1 -1
  138. package/dist/src/log/struct.js.map +1 -1
  139. package/dist/src/matchmaking/wire.d.ts +2 -1
  140. package/dist/src/matchmaking/wire.d.ts.map +1 -1
  141. package/dist/src/matchmaking/wire.js +13 -92
  142. package/dist/src/matchmaking/wire.js.map +1 -1
  143. package/dist/src/network/i-key-network.d.ts +9 -0
  144. package/dist/src/network/i-key-network.d.ts.map +1 -1
  145. package/dist/src/network/i-repo.d.ts +8 -0
  146. package/dist/src/network/i-repo.d.ts.map +1 -1
  147. package/dist/src/network/struct.d.ts +10 -0
  148. package/dist/src/network/struct.d.ts.map +1 -1
  149. package/dist/src/reactivity/push-state.d.ts.map +1 -1
  150. package/dist/src/reactivity/push-state.js +4 -33
  151. package/dist/src/reactivity/push-state.js.map +1 -1
  152. package/dist/src/reactivity/wire-validate.d.ts +7 -22
  153. package/dist/src/reactivity/wire-validate.d.ts.map +1 -1
  154. package/dist/src/reactivity/wire-validate.js +7 -73
  155. package/dist/src/reactivity/wire-validate.js.map +1 -1
  156. package/dist/src/reactivity/wire.js +3 -3
  157. package/dist/src/reactivity/wire.js.map +1 -1
  158. package/dist/src/testing/async-wait.d.ts +23 -0
  159. package/dist/src/testing/async-wait.d.ts.map +1 -0
  160. package/dist/src/testing/async-wait.js +42 -0
  161. package/dist/src/testing/async-wait.js.map +1 -0
  162. package/dist/src/testing/index.d.ts +3 -0
  163. package/dist/src/testing/index.d.ts.map +1 -0
  164. package/dist/src/testing/index.js +3 -0
  165. package/dist/src/testing/index.js.map +1 -0
  166. package/dist/src/testing/test-transactor.d.ts +44 -0
  167. package/dist/src/testing/test-transactor.d.ts.map +1 -0
  168. package/dist/src/testing/test-transactor.js +423 -0
  169. package/dist/src/testing/test-transactor.js.map +1 -0
  170. package/dist/src/transaction/actions-engine.d.ts +6 -3
  171. package/dist/src/transaction/actions-engine.d.ts.map +1 -1
  172. package/dist/src/transaction/actions-engine.js +8 -7
  173. package/dist/src/transaction/actions-engine.js.map +1 -1
  174. package/dist/src/transaction/coordinator.d.ts +43 -22
  175. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  176. package/dist/src/transaction/coordinator.js +443 -178
  177. package/dist/src/transaction/coordinator.js.map +1 -1
  178. package/dist/src/transaction/errors.d.ts +82 -0
  179. package/dist/src/transaction/errors.d.ts.map +1 -0
  180. package/dist/src/transaction/errors.js +93 -0
  181. package/dist/src/transaction/errors.js.map +1 -0
  182. package/dist/src/transaction/index.d.ts +6 -3
  183. package/dist/src/transaction/index.d.ts.map +1 -1
  184. package/dist/src/transaction/index.js +4 -2
  185. package/dist/src/transaction/index.js.map +1 -1
  186. package/dist/src/transaction/operations-hash.d.ts +119 -0
  187. package/dist/src/transaction/operations-hash.d.ts.map +1 -0
  188. package/dist/src/transaction/operations-hash.js +180 -0
  189. package/dist/src/transaction/operations-hash.js.map +1 -0
  190. package/dist/src/transaction/read-dependency-collector.d.ts +43 -0
  191. package/dist/src/transaction/read-dependency-collector.d.ts.map +1 -0
  192. package/dist/src/transaction/read-dependency-collector.js +69 -0
  193. package/dist/src/transaction/read-dependency-collector.js.map +1 -0
  194. package/dist/src/transaction/session.d.ts +12 -3
  195. package/dist/src/transaction/session.d.ts.map +1 -1
  196. package/dist/src/transaction/session.js +35 -9
  197. package/dist/src/transaction/session.js.map +1 -1
  198. package/dist/src/transaction/transaction.d.ts +162 -1
  199. package/dist/src/transaction/transaction.d.ts.map +1 -1
  200. package/dist/src/transaction/transaction.js +85 -3
  201. package/dist/src/transaction/transaction.js.map +1 -1
  202. package/dist/src/transaction/validator.d.ts +3 -11
  203. package/dist/src/transaction/validator.d.ts.map +1 -1
  204. package/dist/src/transaction/validator.js +69 -28
  205. package/dist/src/transaction/validator.js.map +1 -1
  206. package/dist/src/transactor/network-transactor.d.ts +35 -0
  207. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  208. package/dist/src/transactor/network-transactor.js +146 -40
  209. package/dist/src/transactor/network-transactor.js.map +1 -1
  210. package/dist/src/transactor/transactor-source.d.ts +19 -5
  211. package/dist/src/transactor/transactor-source.d.ts.map +1 -1
  212. package/dist/src/transactor/transactor-source.js +43 -11
  213. package/dist/src/transactor/transactor-source.js.map +1 -1
  214. package/dist/src/transform/atomic-proxy.d.ts +35 -5
  215. package/dist/src/transform/atomic-proxy.d.ts.map +1 -1
  216. package/dist/src/transform/atomic-proxy.js +39 -7
  217. package/dist/src/transform/atomic-proxy.js.map +1 -1
  218. package/dist/src/transform/cache-source.d.ts +36 -5
  219. package/dist/src/transform/cache-source.d.ts.map +1 -1
  220. package/dist/src/transform/cache-source.js +84 -8
  221. package/dist/src/transform/cache-source.js.map +1 -1
  222. package/dist/src/transform/helpers.d.ts.map +1 -1
  223. package/dist/src/transform/helpers.js +8 -2
  224. package/dist/src/transform/helpers.js.map +1 -1
  225. package/dist/src/transform/tracker.d.ts +17 -2
  226. package/dist/src/transform/tracker.d.ts.map +1 -1
  227. package/dist/src/transform/tracker.js +66 -11
  228. package/dist/src/transform/tracker.js.map +1 -1
  229. package/dist/src/utility/backoff.d.ts +48 -0
  230. package/dist/src/utility/backoff.d.ts.map +1 -0
  231. package/dist/src/utility/backoff.js +60 -0
  232. package/dist/src/utility/backoff.js.map +1 -0
  233. package/dist/src/utility/pending.d.ts +1 -0
  234. package/dist/src/utility/pending.d.ts.map +1 -1
  235. package/dist/src/utility/pending.js +3 -1
  236. package/dist/src/utility/pending.js.map +1 -1
  237. package/package.json +4 -4
  238. package/src/blocks/block-store.ts +19 -1
  239. package/src/blocks/helpers.ts +3 -3
  240. package/src/btree/btree.ts +108 -63
  241. package/src/chain/chain.ts +11 -1
  242. package/src/cluster/index.ts +1 -0
  243. package/src/cluster/membership.ts +85 -0
  244. package/src/cluster/structs.ts +51 -1
  245. package/src/cohort-topic/addressing.ts +12 -4
  246. package/src/cohort-topic/antidos/bootstrap-evidence.ts +21 -9
  247. package/src/cohort-topic/antidos/replay-guard.ts +37 -0
  248. package/src/cohort-topic/antidos/topic-budget.ts +17 -2
  249. package/src/cohort-topic/coldstart.ts +48 -15
  250. package/src/cohort-topic/gossip/bus.ts +39 -2
  251. package/src/cohort-topic/gossip/view.ts +26 -1
  252. package/src/cohort-topic/member-engine.ts +36 -15
  253. package/src/cohort-topic/membership/publisher.ts +25 -33
  254. package/src/cohort-topic/membership/verifier.ts +119 -3
  255. package/src/cohort-topic/promotion.ts +23 -2
  256. package/src/cohort-topic/registration/renewal.ts +73 -1
  257. package/src/cohort-topic/registration/types.ts +15 -0
  258. package/src/cohort-topic/service.ts +41 -15
  259. package/src/cohort-topic/sig/payloads.ts +29 -9
  260. package/src/cohort-topic/traffic.ts +23 -1
  261. package/src/cohort-topic/walk.ts +95 -24
  262. package/src/cohort-topic/wire/codec.ts +12 -0
  263. package/src/cohort-topic/wire/index.ts +2 -0
  264. package/src/cohort-topic/wire/payloads.ts +14 -5
  265. package/src/cohort-topic/wire/primitives.ts +188 -0
  266. package/src/cohort-topic/wire/types.ts +136 -4
  267. package/src/cohort-topic/wire/validate.ts +168 -134
  268. package/src/collection/collection.ts +123 -40
  269. package/src/collection/struct.ts +44 -2
  270. package/src/collections/tree/tree.ts +42 -5
  271. package/src/index.ts +1 -0
  272. package/src/log/log.ts +37 -9
  273. package/src/log/struct.ts +5 -4
  274. package/src/matchmaking/wire.ts +25 -102
  275. package/src/network/i-key-network.ts +10 -0
  276. package/src/network/i-repo.ts +8 -0
  277. package/src/network/struct.ts +10 -0
  278. package/src/reactivity/push-state.ts +4 -37
  279. package/src/reactivity/wire-validate.ts +7 -81
  280. package/src/reactivity/wire.ts +3 -3
  281. package/src/testing/async-wait.ts +65 -0
  282. package/src/testing/index.ts +2 -0
  283. package/src/testing/test-transactor.ts +468 -0
  284. package/src/transaction/actions-engine.ts +8 -6
  285. package/src/transaction/coordinator.ts +498 -229
  286. package/src/transaction/errors.ts +91 -0
  287. package/src/transaction/index.ts +23 -3
  288. package/src/transaction/operations-hash.ts +196 -0
  289. package/src/transaction/read-dependency-collector.ts +78 -0
  290. package/src/transaction/session.ts +37 -9
  291. package/src/transaction/transaction.ts +211 -4
  292. package/src/transaction/validator.ts +73 -48
  293. package/src/transactor/network-transactor.ts +164 -49
  294. package/src/transactor/transactor-source.ts +47 -12
  295. package/src/transform/atomic-proxy.ts +92 -49
  296. package/src/transform/cache-source.ts +87 -9
  297. package/src/transform/helpers.ts +8 -2
  298. package/src/transform/tracker.ts +69 -11
  299. package/src/utility/backoff.ts +95 -0
  300. package/src/utility/pending.ts +3 -1
  301. package/src/transaction/context.ts +0 -103
@@ -1,7 +1,24 @@
1
1
  import type { BlockType, IBlock, BlockId, BlockHeader, BlockOperation } from "./index.js";
2
+ /** Why a block was read, used to filter the optimistic-concurrency conflict (read) set.
3
+ *
4
+ * - `value` — the transaction's result depends on this block's CONTENT (a leaf/entry, a log
5
+ * entry, or an interior node whose keys/counts were used as data). Always retained.
6
+ * - `navigation` — an interior structural block (a B-tree branch) walked through ONLY to reach
7
+ * a separately-captured `value` read further down the same descent. May be dropped from the
8
+ * conflict set, because any concurrent restructuring that would change the queried result also
9
+ * bumps the revision of a `value` block that stays in the set (the target leaf). See
10
+ * docs/correctness.md Theorem 5.
11
+ *
12
+ * Defaults to `value` EVERYWHERE a read is captured, so an unclassified read is always retained
13
+ * (fail-safe): a forgotten tag can only cause an extra false-positive rejection, never a missed
14
+ * conflict. Only the B-tree point-lookup descent opts specific interior reads into `navigation`. */
15
+ export type ReadPurpose = 'value' | 'navigation';
2
16
  export type BlockSource<T extends IBlock> = {
3
17
  createBlockHeader(type: BlockType, newId?: BlockId): BlockHeader;
4
- tryGet(id: BlockId): Promise<T | undefined>;
18
+ /** Fetch a block by id. `purpose` classifies WHY the block is being read for OCC read-set
19
+ * filtering (see {@link ReadPurpose}); it defaults to `value`, so existing callers that omit
20
+ * it capture a retained read exactly as before. */
21
+ tryGet(id: BlockId, purpose?: ReadPurpose): Promise<T | undefined>;
5
22
  generateId(): BlockId;
6
23
  };
7
24
  export type BlockStore<T extends IBlock> = BlockSource<T> & {
@@ -1 +1 @@
1
- {"version":3,"file":"block-store.d.ts","sourceRoot":"","sources":["../../../src/blocks/block-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1F,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI;IAC3C,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IACjE,MAAM,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,UAAU,IAAI,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC3D,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;IACnD,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC"}
1
+ {"version":3,"file":"block-store.d.ts","sourceRoot":"","sources":["../../../src/blocks/block-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1F;;;;;;;;;;;;oGAYoG;AACpG,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;AAEjD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI;IAC3C,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IACjE;;wDAEoD;IACpD,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACnE,UAAU,IAAI,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC3D,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;IACnD,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { BlockOperation, IBlock, BlockId, BlockStore } from "../index.js";
2
- export declare function get<T extends IBlock>(store: BlockStore<T>, id: BlockId): Promise<T>;
1
+ import type { BlockOperation, IBlock, BlockId, BlockStore, ReadPurpose } from "../index.js";
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
4
  //# 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,MAAM,aAAa,CAAC;AAG/E,wBAAsB,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAIzF;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":"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,6 +1,6 @@
1
1
  import { applyOperation } from "../transform/helpers.js";
2
- export async function get(store, id) {
3
- const block = await store.tryGet(id);
2
+ export async function get(store, id, purpose) {
3
+ const block = await store.tryGet(id, purpose);
4
4
  if (!block)
5
5
  throw Error(`Missing block (${id})`);
6
6
  return block;
@@ -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;IAC5E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrC,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":"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"}
@@ -2,6 +2,7 @@ import { Path, type ITreeTrunk, type KeyRange, type getTrunkFunc } from "./index
2
2
  import type { BlockId, BlockStore } from "../blocks/index.js";
3
3
  import type { BranchNode, ITreeNode, LeafNode } from "./nodes.js";
4
4
  import type { TreeBlock } from "./tree-block.js";
5
+ import { type AtomicScope } from "../transform/atomic-proxy.js";
5
6
  export declare const NodeCapacity = 64;
6
7
  /**
7
8
  * Represents a lightweight B+(ish)Tree (data at leaves, but no linked list of leaves).
@@ -14,24 +15,29 @@ export declare class BTree<TKey, TEntry> {
14
15
  readonly trunk: ITreeTrunk;
15
16
  readonly keyFromEntry: (entry: TEntry) => TKey;
16
17
  readonly compare: (a: TKey, b: TKey) => number;
18
+ readonly nodeCapacity: number;
17
19
  protected _version: number;
18
20
  private _proxy?;
19
21
  /**
20
22
  * @param [compare=(a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0] a comparison function for keys. The default uses < and > operators.
21
23
  * @param [keyFromEntry=(entry: TEntry) => entry as unknown as TKey] a function to extract the key from an entry. The default assumes the key is the entry itself.
22
24
  */
23
- constructor(store: BlockStore<ITreeNode>, trunk: ITreeTrunk, keyFromEntry?: (entry: TEntry) => TKey, compare?: (a: TKey, b: TKey) => number);
25
+ constructor(store: BlockStore<ITreeNode>, trunk: ITreeTrunk, keyFromEntry?: (entry: TEntry) => TKey, compare?: (a: TKey, b: TKey) => number, nodeCapacity?: number);
24
26
  private atomic;
25
27
  static createRoot(store: BlockStore<ITreeNode>): LeafNode<never>;
26
- static create<TKey, TEntry>(store: BlockStore<ITreeNode | TreeBlock>, createTrunk: getTrunkFunc, keyFromEntry?: (entry: TEntry) => TKey, compare?: (a: TKey, b: TKey) => 0 | 1 | -1, newId?: BlockId): BTree<TKey, TEntry>;
28
+ static create<TKey, TEntry>(store: BlockStore<ITreeNode | TreeBlock>, createTrunk: getTrunkFunc, keyFromEntry?: (entry: TEntry) => TKey, compare?: (a: TKey, b: TKey) => 0 | 1 | -1, nodeCapacity?: number, newId?: BlockId): BTree<TKey, TEntry>;
27
29
  /** @returns a path to the first entry (on = false if no entries) */
28
30
  first(): Promise<Path<TKey, TEntry>>;
29
31
  /** @returns a path to the last entry (on = false if no entries) */
30
32
  last(): Promise<Path<TKey, TEntry>>;
31
33
  /** Attempts to find the given key
32
34
  * @returns Path to the key or the "crack" before it. If `on` is true on the resulting path, the key was found.
33
- * If `on` is false, next() and prior() can attempt to move to the nearest match. */
34
- find(key: TKey): Promise<Path<TKey, TEntry>>;
35
+ * If `on` is false, next() and prior() can attempt to move to the nearest match.
36
+ * @param navigate when true, the interior descent nodes are captured as droppable `navigation`
37
+ * reads (see {@link getPath}). DEFAULT false: `find` returns a Path a caller may use to seed a
38
+ * range scan (`ascending`/`next`), whose result depends on tree shape, so its structural reads
39
+ * must stay retained. Only {@link get} — a point lookup with no such escape — opts in. */
40
+ find(key: TKey, navigate?: boolean): Promise<Path<TKey, TEntry>>;
35
41
  /** Retrieves the entry for the given key.
36
42
  * Use find instead for a path to the key, the nearest match, or as a basis for navigation.
37
43
  * @returns the entry for the given key if found; undefined otherwise. */
@@ -44,6 +50,11 @@ export declare class BTree<TKey, TEntry> {
44
50
  range(range: KeyRange<TKey>): AsyncIterableIterator<Path<TKey, TEntry>>;
45
51
  /** @returns true if the given path remains valid; false if the tree has been mutated, invalidating the path. */
46
52
  isValid(path: Path<TKey, TEntry>): boolean;
53
+ /** Bump the version so every Path handed out before now reports invalid (see {@link isValid}).
54
+ * Needed when the tree's data was mutated through a DIFFERENT store binding than this instance —
55
+ * e.g. the tree collection's action handler writes through a per-action Atomic store, so this
56
+ * read-side btree never sees the mutation and must be told to invalidate outstanding paths. */
57
+ invalidatePaths(): void;
47
58
  /**
48
59
  * Adds a value to the tree. Be sure to check the result, as the tree does not allow duplicate keys.
49
60
  * Added entries are frozen to ensure immutability
@@ -57,7 +68,7 @@ export declare class BTree<TKey, TEntry> {
57
68
  * * on = false if update/insert failed.
58
69
  * * wasUpdate = true, given path is not on an entry
59
70
  * * else newEntry's new key already present; returned path is "near" existing entry */
60
- updateAt(path: Path<TKey, TEntry>, newEntry: TEntry): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]>;
71
+ updateAt(path: Path<TKey, TEntry>, newEntry: TEntry, parent?: AtomicScope): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]>;
61
72
  /** Inserts the entry if it doesn't exist, or updates it if it does.
62
73
  * The entry is frozen to ensure immutability.
63
74
  * @returns path to the new entry. on = true if existing; on = false if new. */
@@ -104,7 +115,17 @@ export declare class BTree<TKey, TEntry> {
104
115
  private internalDescending;
105
116
  private findFirst;
106
117
  private findLast;
107
- protected getPath(node: ITreeNode, key: TKey): Promise<Path<TKey, TEntry>>;
118
+ /** Descend from `node` toward `key`, building the path. When `navigate` is true this is a point
119
+ * lookup (from {@link get}): each interior branch read below the root is captured as a droppable
120
+ * `navigation` read, and the terminal leaf is upgraded back to a retained `value` read — the
121
+ * result depends only on the leaf, and any concurrent restructuring that would change the result
122
+ * also bumps the leaf's revision (Theorem 5), so the interior reads are redundant. When false
123
+ * (find/range/scan/write descents) every read stays `value` (retained). */
124
+ protected getPath(node: ITreeNode, key: TKey, navigate?: boolean): Promise<Path<TKey, TEntry>>;
125
+ /** Upgrade a captured read of `id` to a retained `value` read via the store chain's read
126
+ * collector (duck-typed — a no-op for stores without one, e.g. test doubles or log-walk
127
+ * caches). Used by a point lookup to pin its terminal leaf. */
128
+ private markReadValue;
108
129
  private indexOfEntry;
109
130
  protected indexOfKey(keys: TKey[], key: TKey): number;
110
131
  private internalNext;
@@ -1 +1 @@
1
- {"version":3,"file":"btree.d.ts","sourceRoot":"","sources":["../../../src/btree/btree.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"btree.d.ts","sourceRoot":"","sources":["../../../src/btree/btree.ts"],"names":[],"mappings":"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"}
@@ -14,30 +14,32 @@ export class BTree {
14
14
  trunk;
15
15
  keyFromEntry;
16
16
  compare;
17
+ nodeCapacity;
17
18
  _version = 0; // only for path invalidation
18
19
  _proxy;
19
20
  /**
20
21
  * @param [compare=(a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0] a comparison function for keys. The default uses < and > operators.
21
22
  * @param [keyFromEntry=(entry: TEntry) => entry as unknown as TKey] a function to extract the key from an entry. The default assumes the key is the entry itself.
22
23
  */
23
- constructor(store, trunk, keyFromEntry = (entry) => entry, compare = (a, b) => a < b ? -1 : a > b ? 1 : 0) {
24
+ constructor(store, trunk, keyFromEntry = (entry) => entry, compare = (a, b) => a < b ? -1 : a > b ? 1 : 0, nodeCapacity = NodeCapacity) {
24
25
  this.store = store;
25
26
  this.trunk = trunk;
26
27
  this.keyFromEntry = keyFromEntry;
27
28
  this.compare = compare;
29
+ this.nodeCapacity = nodeCapacity;
28
30
  }
29
- atomic(fn) {
30
- return this._proxy ? this._proxy.atomic(fn) : fn();
31
+ atomic(fn, parent) {
32
+ return this._proxy ? this._proxy.atomic(fn, parent) : fn(parent);
31
33
  }
32
34
  static createRoot(store) {
33
35
  return newLeafNode(store, []);
34
36
  }
35
- static create(store, createTrunk, keyFromEntry = (entry) => entry, compare = (a, b) => a < b ? -1 : a > b ? 1 : 0, newId) {
37
+ static create(store, createTrunk, keyFromEntry = (entry) => entry, compare = (a, b) => a < b ? -1 : a > b ? 1 : 0, nodeCapacity = NodeCapacity, newId) {
36
38
  const proxy = new AtomicProxy(store);
37
39
  const root = BTree.createRoot(proxy);
38
40
  proxy.insert(root);
39
41
  const trunk = createTrunk(proxy, root.header.id, newId);
40
- const tree = new BTree(proxy, trunk, keyFromEntry, compare);
42
+ const tree = new BTree(proxy, trunk, keyFromEntry, compare, nodeCapacity);
41
43
  tree._proxy = proxy;
42
44
  return tree;
43
45
  }
@@ -51,15 +53,23 @@ export class BTree {
51
53
  }
52
54
  /** Attempts to find the given key
53
55
  * @returns Path to the key or the "crack" before it. If `on` is true on the resulting path, the key was found.
54
- * If `on` is false, next() and prior() can attempt to move to the nearest match. */
55
- async find(key) {
56
- return await this.getPath(await this.trunk.get(), key);
56
+ * If `on` is false, next() and prior() can attempt to move to the nearest match.
57
+ * @param navigate when true, the interior descent nodes are captured as droppable `navigation`
58
+ * reads (see {@link getPath}). DEFAULT false: `find` returns a Path a caller may use to seed a
59
+ * range scan (`ascending`/`next`), whose result depends on tree shape, so its structural reads
60
+ * must stay retained. Only {@link get} — a point lookup with no such escape — opts in. */
61
+ async find(key, navigate = false) {
62
+ return await this.getPath(await this.trunk.get(), key, navigate);
57
63
  }
58
64
  /** Retrieves the entry for the given key.
59
65
  * Use find instead for a path to the key, the nearest match, or as a basis for navigation.
60
66
  * @returns the entry for the given key if found; undefined otherwise. */
61
67
  async get(key) {
62
- return this.at(await this.find(key));
68
+ // Value-of-key point lookup: the result depends ONLY on the terminal leaf's content, so the
69
+ // interior descent nodes are pure navigation and are dropped from the OCC conflict set. The
70
+ // returned Path is consumed by `at` and discarded — it never seeds a scan — so navigate=true
71
+ // is safe here (unlike the public `find`).
72
+ return this.at(await this.find(key, /* navigate */ true));
63
73
  }
64
74
  /** @returns the entry for the given path if on an entry; undefined otherwise. */
65
75
  at(path) {
@@ -97,6 +107,13 @@ export class BTree {
97
107
  isValid(path) {
98
108
  return path.version === this._version;
99
109
  }
110
+ /** Bump the version so every Path handed out before now reports invalid (see {@link isValid}).
111
+ * Needed when the tree's data was mutated through a DIFFERENT store binding than this instance —
112
+ * e.g. the tree collection's action handler writes through a per-action Atomic store, so this
113
+ * read-side btree never sees the mutation and must be told to invalidate outstanding paths. */
114
+ invalidatePaths() {
115
+ ++this._version;
116
+ }
100
117
  /**
101
118
  * Adds a value to the tree. Be sure to check the result, as the tree does not allow duplicate keys.
102
119
  * Added entries are frozen to ensure immutability
@@ -119,7 +136,7 @@ export class BTree {
119
136
  * * on = false if update/insert failed.
120
137
  * * wasUpdate = true, given path is not on an entry
121
138
  * * else newEntry's new key already present; returned path is "near" existing entry */
122
- async updateAt(path, newEntry) {
139
+ async updateAt(path, newEntry, parent) {
123
140
  return this.atomic(async () => {
124
141
  this.validatePath(path);
125
142
  if (path.on) {
@@ -130,7 +147,7 @@ export class BTree {
130
147
  result[0].version = ++this._version;
131
148
  }
132
149
  return result;
133
- });
150
+ }, parent);
134
151
  }
135
152
  /** Inserts the entry if it doesn't exist, or updates it if it does.
136
153
  * The entry is frozen to ensure immutability.
@@ -155,11 +172,13 @@ export class BTree {
155
172
  * @returns path to new entry and whether an update or insert attempted.
156
173
  * If getUpdated callback returns a row that is already present, the resulting path will not be on. */
157
174
  async merge(newEntry, getUpdated) {
158
- return this.atomic(async () => {
159
- const newKey = await this.keyFromEntry(newEntry);
175
+ return this.atomic(async (scope) => {
176
+ const newKey = this.keyFromEntry(newEntry);
160
177
  const path = await this.find(newKey);
161
178
  if (path.on) {
162
- const result = await this.updateAt(path, getUpdated(this.getEntry(path))); // Don't use internalUpdate - need to freeze and check for mutation
179
+ // Thread this scope into updateAt so the nested call joins this atomic batch
180
+ // (reuses the tracker) rather than opening/committing a second scope.
181
+ const result = await this.updateAt(path, getUpdated(this.getEntry(path)), scope); // Don't use internalUpdate - need to freeze and check for mutation
163
182
  // Note: updateAt already increments version, so don't double-increment here
164
183
  return result;
165
184
  }
@@ -244,7 +263,7 @@ export class BTree {
244
263
  /** @returns a path one step backward. on will be true if the path hasn't hit the end. */
245
264
  async prior(path) {
246
265
  const newPath = path.clone();
247
- this.movePrior(newPath);
266
+ await this.movePrior(newPath);
248
267
  return newPath;
249
268
  }
250
269
  /** Attempts to advance the given path one step backwards. (mutates the path) */
@@ -294,20 +313,39 @@ export class BTree {
294
313
  }
295
314
  return endPath;
296
315
  }
297
- async getPath(node, key) {
316
+ /** Descend from `node` toward `key`, building the path. When `navigate` is true this is a point
317
+ * lookup (from {@link get}): each interior branch read below the root is captured as a droppable
318
+ * `navigation` read, and the terminal leaf is upgraded back to a retained `value` read — the
319
+ * result depends only on the leaf, and any concurrent restructuring that would change the result
320
+ * also bumps the leaf's revision (Theorem 5), so the interior reads are redundant. When false
321
+ * (find/range/scan/write descents) every read stays `value` (retained). */
322
+ async getPath(node, key, navigate = false) {
298
323
  if (node.header.type === TreeLeafBlockType) {
299
324
  const leaf = node;
325
+ if (navigate) {
326
+ // The leaf ends a point-lookup descent: its content is the load-bearing VALUE read.
327
+ // Its parent fetched it as `navigation` (a child's type is unknown before reading);
328
+ // upgrade it (value-wins) so it is retained while the interior nodes above are dropped.
329
+ this.markReadValue(leaf.header.id);
330
+ }
300
331
  const [on, index] = this.indexOfEntry(leaf.entries, key);
301
332
  return new Path([], leaf, index, on, this._version);
302
333
  }
303
334
  else {
304
335
  const branch = node;
305
336
  const index = this.indexOfKey(branch.partitions, key);
306
- const path = await this.getPath(await get(this.store, branch.nodes[index]), key);
337
+ const child = await get(this.store, branch.nodes[index], navigate ? 'navigation' : 'value');
338
+ const path = await this.getPath(child, key, navigate);
307
339
  path.branches.unshift(new PathBranch(branch, index));
308
340
  return path;
309
341
  }
310
342
  }
343
+ /** Upgrade a captured read of `id` to a retained `value` read via the store chain's read
344
+ * collector (duck-typed — a no-op for stores without one, e.g. test doubles or log-walk
345
+ * caches). Used by a point lookup to pin its terminal leaf. */
346
+ markReadValue(id) {
347
+ this.store.markReadValue?.(id);
348
+ }
311
349
  indexOfEntry(entries, key) {
312
350
  let lo = 0;
313
351
  let hi = entries.length - 1;
@@ -346,35 +384,36 @@ export class BTree {
346
384
  if (!path.on) { // Attempt to move off of crack
347
385
  path.on = path.branches.every(branch => branch.index >= 0 && branch.index < branch.node.nodes.length)
348
386
  && path.leafIndex >= 0 && path.leafIndex < path.leafNode.entries.length;
349
- if (path.on) {
350
- return;
387
+ if (path.on || path.leafIndex < path.leafNode.entries.length) {
388
+ return; // got on an entry, or an interior crack — nothing to advance into
351
389
  }
390
+ // end-of-leaf crack (leafIndex === entries.length): fall through to branch-popping leaf-advance
352
391
  }
353
- else if (path.leafIndex >= path.leafNode.entries.length - 1) {
354
- let popCount = 0;
355
- let found = false;
356
- const last = path.branches.length - 1;
357
- while (popCount <= last && !found) {
358
- const branch = path.branches[last - popCount];
359
- if (branch.index === branch.node.partitions.length) // last node in branch
360
- ++popCount;
361
- else
362
- found = true;
363
- }
364
- if (!found) {
365
- path.leafIndex = path.leafNode.entries.length; // after last row = end crack
366
- path.on = false;
367
- }
368
- else {
369
- path.branches.splice(-popCount, popCount);
370
- const branch = path.branches.at(-1);
371
- ++branch.index;
372
- this.moveToFirst(await get(this.store, branch.node.nodes[branch.index]), path);
373
- }
374
- }
375
- else {
392
+ else if (path.leafIndex < path.leafNode.entries.length - 1) {
376
393
  ++path.leafIndex;
377
394
  path.on = true;
395
+ return;
396
+ }
397
+ // on the last entry of the leaf, or off an end-of-leaf crack: advance into the next leaf
398
+ let popCount = 0;
399
+ let found = false;
400
+ const last = path.branches.length - 1;
401
+ while (popCount <= last && !found) {
402
+ const branch = path.branches[last - popCount];
403
+ if (branch.index === branch.node.partitions.length) // last node in branch
404
+ ++popCount;
405
+ else
406
+ found = true;
407
+ }
408
+ if (!found) {
409
+ path.leafIndex = path.leafNode.entries.length; // after last row = end crack
410
+ path.on = false;
411
+ }
412
+ else {
413
+ path.branches.splice(-popCount, popCount);
414
+ const branch = path.branches.at(-1);
415
+ ++branch.index;
416
+ await this.moveToFirst(await get(this.store, branch.node.nodes[branch.index]), path);
378
417
  }
379
418
  }
380
419
  async internalPrior(path) {
@@ -413,7 +452,7 @@ export class BTree {
413
452
  if (this.compare(oldKey, newKey) !== 0) { // if key changed, delete and re-insert
414
453
  let newPath = await this.internalInsert(newEntry);
415
454
  if (newPath.on) { // insert succeeded
416
- this.internalDelete(await this.find(oldKey)); // Re-find - the prior insert invalidated the path
455
+ await this.internalDelete(await this.find(oldKey)); // Re-find - the prior insert invalidated the path
417
456
  newPath = await this.find(newKey); // Re-find- delete invalidated path
418
457
  }
419
458
  return [newPath, false];
@@ -463,7 +502,7 @@ export class BTree {
463
502
  }
464
503
  if (split) {
465
504
  const newBranch = newBranchNode(this.store, [split.key], [await this.trunk.getId(), split.right.header.id]);
466
- await this.store.insert(newBranch);
505
+ this.store.insert(newBranch);
467
506
  await this.trunk.set(newBranch);
468
507
  path.branches.unshift(new PathBranch(newBranch, split.indexDelta));
469
508
  }
@@ -527,7 +566,7 @@ export class BTree {
527
566
  }
528
567
  leafInsert(path, entry) {
529
568
  const { leafNode: leaf, leafIndex: index } = path;
530
- if (leaf.entries.length < NodeCapacity) { // No split needed
569
+ if (leaf.entries.length < this.nodeCapacity) { // No split needed
531
570
  apply(this.store, leaf, [entries$, index, 0, [entry]]);
532
571
  return undefined;
533
572
  }
@@ -555,7 +594,7 @@ export class BTree {
555
594
  const pathBranch = path.branches[branchIndex];
556
595
  const { index: splitIndex, node } = pathBranch;
557
596
  pathBranch.index += split.indexDelta;
558
- if (node.nodes.length < NodeCapacity) { // no split needed
597
+ if (node.nodes.length < this.nodeCapacity) { // no split needed
559
598
  apply(this.store, node, [partitions$, splitIndex, 0, [split.key]]);
560
599
  apply(this.store, node, [nodes$, splitIndex + 1, 0, [split.right.header.id]]);
561
600
  return undefined;
@@ -572,6 +611,7 @@ export class BTree {
572
611
  newNodes.splice(pathBranch.index + 1, 0, split.right.header.id);
573
612
  }
574
613
  const newBranch = newBranchNode(this.store, newPartitions, newNodes);
614
+ this.store.insert(newBranch);
575
615
  // Delete partitions and nodes
576
616
  const newPartition = node.partitions[midIndex - 1];
577
617
  apply(this.store, node, [partitions$, midIndex - 1, newPartitions.length + 1, []]);
@@ -583,7 +623,7 @@ export class BTree {
583
623
  return new Split(newPartition, newBranch, delta);
584
624
  }
585
625
  async rebalanceLeaf(path, depth) {
586
- if (depth === 0 || path.leafNode.entries.length >= (NodeCapacity >>> 1)) {
626
+ if (depth === 0 || path.leafNode.entries.length >= (this.nodeCapacity >>> 1)) {
587
627
  return undefined;
588
628
  }
589
629
  const leaf = path.leafNode;
@@ -592,16 +632,16 @@ export class BTree {
592
632
  const pNode = parent.node;
593
633
  const rightSibId = pNode.nodes[pIndex + 1];
594
634
  const rightSib = rightSibId ? (await get(this.store, rightSibId)) : undefined;
595
- if (rightSib && rightSib.entries.length > (NodeCapacity >>> 1)) { // Attempt to borrow from right sibling
635
+ if (rightSib && rightSib.entries.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from right sibling
596
636
  const entry = rightSib.entries[0];
597
637
  apply(this.store, rightSib, [entries$, 0, 1, []]);
598
638
  apply(this.store, leaf, [entries$, leaf.entries.length, 0, [entry]]);
599
- this.updatePartition(pIndex + 1, path, depth - 1, this.keyFromEntry(entry));
639
+ this.updatePartition(pIndex + 1, path, depth - 1, this.keyFromEntry(rightSib.entries[0]));
600
640
  return undefined;
601
641
  }
602
642
  const leftSibId = pNode.nodes[pIndex - 1];
603
643
  const leftSib = leftSibId ? (await get(this.store, leftSibId)) : undefined;
604
- if (leftSib && leftSib.entries.length > (NodeCapacity >>> 1)) { // Attempt to borrow from left sibling
644
+ if (leftSib && leftSib.entries.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from left sibling
605
645
  const entry = leftSib.entries[leftSib.entries.length - 1];
606
646
  apply(this.store, leftSib, [entries$, leftSib.entries.length - 1, 1, []]);
607
647
  apply(this.store, leaf, [entries$, 0, 0, [entry]]);
@@ -609,7 +649,7 @@ export class BTree {
609
649
  path.leafIndex += 1;
610
650
  return undefined;
611
651
  }
612
- if (rightSib && rightSib.entries.length + leaf.entries.length <= NodeCapacity) { // Attempt to merge right sibling into leaf (right sib deleted)
652
+ if (rightSib && rightSib.entries.length + leaf.entries.length <= this.nodeCapacity) { // Attempt to merge right sibling into leaf (right sib deleted)
613
653
  apply(this.store, leaf, [entries$, leaf.entries.length, 0, rightSib.entries]);
614
654
  this.deletePartition(pNode, pIndex);
615
655
  if (pIndex === 0) { // 0th node of parent, update parent key
@@ -618,7 +658,12 @@ export class BTree {
618
658
  this.store.delete(rightSib.header.id);
619
659
  return await this.rebalanceBranch(path, depth - 1);
620
660
  }
621
- if (leftSib && leftSib.entries.length + leaf.entries.length <= NodeCapacity) { // Attempt to merge into left sibling (leaf deleted)
661
+ if (leftSib && leftSib.entries.length + leaf.entries.length <= this.nodeCapacity) { // Attempt to merge into left sibling (leaf deleted)
662
+ // NOTE: this fixes leafIndex, but the parent-branch index up the path is left pointing at
663
+ // the now-deleted leaf's old slot (same tolerated-staleness class as rebalanceBranch's
664
+ // left-merge, part c). Harmless today because deleteAt version-bumps and abandons the
665
+ // returned path. A future path-reuse consumer must audit BOTH left-merge sites, not just
666
+ // rebalanceBranch.
622
667
  path.leafNode = leftSib;
623
668
  path.leafIndex += leftSib.entries.length;
624
669
  apply(this.store, leftSib, [entries$, leftSib.entries.length, 0, leaf.entries]);
@@ -633,7 +678,7 @@ export class BTree {
633
678
  if (depth === 0 && branch.partitions.length === 0) { // last node... collapse child into root
634
679
  return path.branches[depth + 1]?.node ?? path.leafNode;
635
680
  }
636
- if (depth === 0 || (branch.nodes.length >= NodeCapacity >>> 1)) {
681
+ if (depth === 0 || (branch.nodes.length >= this.nodeCapacity >>> 1)) {
637
682
  return undefined;
638
683
  }
639
684
  const parent = path.branches.at(depth - 1);
@@ -641,7 +686,7 @@ export class BTree {
641
686
  const pNode = parent.node;
642
687
  const rightSibId = pNode.nodes[pIndex + 1];
643
688
  const rightSib = rightSibId ? (await get(this.store, rightSibId)) : undefined;
644
- if (rightSib && rightSib.nodes.length > (NodeCapacity >>> 1)) { // Attempt to borrow from right sibling
689
+ if (rightSib && rightSib.nodes.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from right sibling
645
690
  const node = rightSib.nodes[0];
646
691
  const rightKey = rightSib.partitions[0];
647
692
  this.insertPartition(branch, branch.partitions.length, pNode.partitions[pIndex], node);
@@ -651,7 +696,7 @@ export class BTree {
651
696
  }
652
697
  const leftSibId = pNode.nodes[pIndex - 1];
653
698
  const leftSib = leftSibId ? (await get(this.store, leftSibId)) : undefined;
654
- if (leftSib && leftSib.nodes.length > (NodeCapacity >>> 1)) { // Attempt to borrow from left sibling
699
+ if (leftSib && leftSib.nodes.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from left sibling
655
700
  const node = leftSib.nodes[leftSib.nodes.length - 1];
656
701
  const pKey = leftSib.partitions[leftSib.partitions.length - 1];
657
702
  this.insertPartition(branch, 0, pNode.partitions[pIndex - 1], node, 0);
@@ -660,26 +705,24 @@ export class BTree {
660
705
  this.updatePartition(pIndex, path, depth - 1, pKey);
661
706
  return undefined;
662
707
  }
663
- if (rightSib && rightSib.nodes.length + branch.nodes.length <= NodeCapacity) { // Attempt to merge right sibling into self
708
+ if (rightSib && rightSib.nodes.length + branch.nodes.length <= this.nodeCapacity) { // Attempt to merge right sibling into self
664
709
  const pKey = pNode.partitions[pIndex];
665
710
  this.deletePartition(pNode, pIndex);
666
711
  apply(this.store, branch, [partitions$, branch.partitions.length, 0, [pKey]]);
667
712
  apply(this.store, branch, [partitions$, branch.partitions.length, 0, rightSib.partitions]);
668
713
  apply(this.store, branch, [nodes$, branch.nodes.length, 0, rightSib.nodes]);
669
- if (pIndex === 0 && pNode.partitions.length > 0) { // if parent is left edge, new right sibling is now the first partition
670
- this.updatePartition(pIndex, path, depth - 1, pNode.partitions[0]);
671
- }
672
714
  this.store.delete(rightSib.header.id);
673
715
  return this.rebalanceBranch(path, depth - 1);
674
716
  }
675
- if (leftSib && leftSib.nodes.length + branch.nodes.length <= NodeCapacity) { // Attempt to merge self into left sibling
717
+ if (leftSib && leftSib.nodes.length + branch.nodes.length <= this.nodeCapacity) { // Attempt to merge self into left sibling
718
+ const leftLen = leftSib.nodes.length; // capture BEFORE the appends below shift it (see leaf path at rebalanceLeaf)
676
719
  const pKey = pNode.partitions[pIndex - 1];
677
720
  this.deletePartition(pNode, pIndex - 1);
678
721
  apply(this.store, leftSib, [partitions$, leftSib.partitions.length, 0, [pKey]]);
679
722
  apply(this.store, leftSib, [partitions$, leftSib.partitions.length, 0, branch.partitions]);
680
723
  apply(this.store, leftSib, [nodes$, leftSib.nodes.length, 0, branch.nodes]);
681
724
  pathBranch.node = leftSib;
682
- pathBranch.index += leftSib.nodes.length;
725
+ pathBranch.index += leftLen; // branch's children now sit after leftSib's original children
683
726
  this.store.delete(branch.header.id);
684
727
  return this.rebalanceBranch(path, depth - 1);
685
728
  }