@optimystic/db-core 0.0.1

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 (345) hide show
  1. package/README.md +328 -0
  2. package/dist/index.min.js +18 -0
  3. package/dist/index.min.js.map +7 -0
  4. package/dist/src/blocks/block-store.d.ts +12 -0
  5. package/dist/src/blocks/block-store.d.ts.map +1 -0
  6. package/dist/src/blocks/block-store.js +2 -0
  7. package/dist/src/blocks/block-store.js.map +1 -0
  8. package/dist/src/blocks/block-types.d.ts +3 -0
  9. package/dist/src/blocks/block-types.d.ts.map +1 -0
  10. package/dist/src/blocks/block-types.js +9 -0
  11. package/dist/src/blocks/block-types.js.map +1 -0
  12. package/dist/src/blocks/helpers.d.ts +4 -0
  13. package/dist/src/blocks/helpers.d.ts.map +1 -0
  14. package/dist/src/blocks/helpers.js +12 -0
  15. package/dist/src/blocks/helpers.js.map +1 -0
  16. package/dist/src/blocks/index.d.ts +5 -0
  17. package/dist/src/blocks/index.d.ts.map +1 -0
  18. package/dist/src/blocks/index.js +5 -0
  19. package/dist/src/blocks/index.js.map +1 -0
  20. package/dist/src/blocks/structs.d.ts +14 -0
  21. package/dist/src/blocks/structs.d.ts.map +1 -0
  22. package/dist/src/blocks/structs.js +2 -0
  23. package/dist/src/blocks/structs.js.map +1 -0
  24. package/dist/src/btree/btree.d.ts +135 -0
  25. package/dist/src/btree/btree.d.ts.map +1 -0
  26. package/dist/src/btree/btree.js +727 -0
  27. package/dist/src/btree/btree.js.map +1 -0
  28. package/dist/src/btree/independent-trunk.d.ts +17 -0
  29. package/dist/src/btree/independent-trunk.d.ts.map +1 -0
  30. package/dist/src/btree/independent-trunk.js +41 -0
  31. package/dist/src/btree/independent-trunk.js.map +1 -0
  32. package/dist/src/btree/index.d.ts +6 -0
  33. package/dist/src/btree/index.d.ts.map +1 -0
  34. package/dist/src/btree/index.js +6 -0
  35. package/dist/src/btree/index.js.map +1 -0
  36. package/dist/src/btree/key-range.d.ts +13 -0
  37. package/dist/src/btree/key-range.d.ts.map +1 -0
  38. package/dist/src/btree/key-range.js +20 -0
  39. package/dist/src/btree/key-range.js.map +1 -0
  40. package/dist/src/btree/keyset.d.ts +4 -0
  41. package/dist/src/btree/keyset.d.ts.map +1 -0
  42. package/dist/src/btree/keyset.js +4 -0
  43. package/dist/src/btree/keyset.js.map +1 -0
  44. package/dist/src/btree/nodes.d.ts +16 -0
  45. package/dist/src/btree/nodes.d.ts.map +1 -0
  46. package/dist/src/btree/nodes.js +9 -0
  47. package/dist/src/btree/nodes.js.map +1 -0
  48. package/dist/src/btree/path.d.ts +22 -0
  49. package/dist/src/btree/path.d.ts.map +1 -0
  50. package/dist/src/btree/path.js +39 -0
  51. package/dist/src/btree/path.js.map +1 -0
  52. package/dist/src/btree/tree-block.d.ts +7 -0
  53. package/dist/src/btree/tree-block.d.ts.map +1 -0
  54. package/dist/src/btree/tree-block.js +5 -0
  55. package/dist/src/btree/tree-block.js.map +1 -0
  56. package/dist/src/btree/trunk.d.ts +13 -0
  57. package/dist/src/btree/trunk.d.ts.map +1 -0
  58. package/dist/src/btree/trunk.js +2 -0
  59. package/dist/src/btree/trunk.js.map +1 -0
  60. package/dist/src/chain/chain-nodes.d.ts +18 -0
  61. package/dist/src/chain/chain-nodes.d.ts.map +1 -0
  62. package/dist/src/chain/chain-nodes.js +10 -0
  63. package/dist/src/chain/chain-nodes.js.map +1 -0
  64. package/dist/src/chain/chain.d.ts +75 -0
  65. package/dist/src/chain/chain.d.ts.map +1 -0
  66. package/dist/src/chain/chain.js +268 -0
  67. package/dist/src/chain/chain.js.map +1 -0
  68. package/dist/src/chain/index.d.ts +2 -0
  69. package/dist/src/chain/index.d.ts.map +1 -0
  70. package/dist/src/chain/index.js +2 -0
  71. package/dist/src/chain/index.js.map +1 -0
  72. package/dist/src/cluster/i-cluster.d.ts +5 -0
  73. package/dist/src/cluster/i-cluster.d.ts.map +1 -0
  74. package/dist/src/cluster/i-cluster.js +2 -0
  75. package/dist/src/cluster/i-cluster.js.map +1 -0
  76. package/dist/src/cluster/index.d.ts +3 -0
  77. package/dist/src/cluster/index.d.ts.map +1 -0
  78. package/dist/src/cluster/index.js +3 -0
  79. package/dist/src/cluster/index.js.map +1 -0
  80. package/dist/src/cluster/structs.d.ts +47 -0
  81. package/dist/src/cluster/structs.d.ts.map +1 -0
  82. package/dist/src/cluster/structs.js +2 -0
  83. package/dist/src/cluster/structs.js.map +1 -0
  84. package/dist/src/collection/action.d.ts +26 -0
  85. package/dist/src/collection/action.d.ts.map +1 -0
  86. package/dist/src/collection/action.js +2 -0
  87. package/dist/src/collection/action.js.map +1 -0
  88. package/dist/src/collection/collection.d.ts +48 -0
  89. package/dist/src/collection/collection.d.ts.map +1 -0
  90. package/dist/src/collection/collection.js +175 -0
  91. package/dist/src/collection/collection.js.map +1 -0
  92. package/dist/src/collection/index.d.ts +4 -0
  93. package/dist/src/collection/index.d.ts.map +1 -0
  94. package/dist/src/collection/index.js +4 -0
  95. package/dist/src/collection/index.js.map +1 -0
  96. package/dist/src/collection/struct.d.ts +16 -0
  97. package/dist/src/collection/struct.d.ts.map +1 -0
  98. package/dist/src/collection/struct.js +2 -0
  99. package/dist/src/collection/struct.js.map +1 -0
  100. package/dist/src/collections/diary/diary.d.ts +9 -0
  101. package/dist/src/collections/diary/diary.d.ts.map +1 -0
  102. package/dist/src/collections/diary/diary.js +37 -0
  103. package/dist/src/collections/diary/diary.js.map +1 -0
  104. package/dist/src/collections/diary/index.d.ts +3 -0
  105. package/dist/src/collections/diary/index.d.ts.map +1 -0
  106. package/dist/src/collections/diary/index.js +3 -0
  107. package/dist/src/collections/diary/index.js.map +1 -0
  108. package/dist/src/collections/diary/struct.d.ts +2 -0
  109. package/dist/src/collections/diary/struct.d.ts.map +1 -0
  110. package/dist/src/collections/diary/struct.js +3 -0
  111. package/dist/src/collections/diary/struct.js.map +1 -0
  112. package/dist/src/collections/index.d.ts +3 -0
  113. package/dist/src/collections/index.d.ts.map +1 -0
  114. package/dist/src/collections/index.js +3 -0
  115. package/dist/src/collections/index.js.map +1 -0
  116. package/dist/src/collections/tree/collection-trunk.d.ts +11 -0
  117. package/dist/src/collections/tree/collection-trunk.d.ts.map +1 -0
  118. package/dist/src/collections/tree/collection-trunk.js +22 -0
  119. package/dist/src/collections/tree/collection-trunk.js.map +1 -0
  120. package/dist/src/collections/tree/index.d.ts +3 -0
  121. package/dist/src/collections/tree/index.d.ts.map +1 -0
  122. package/dist/src/collections/tree/index.js +3 -0
  123. package/dist/src/collections/tree/index.js.map +1 -0
  124. package/dist/src/collections/tree/struct.d.ts +12 -0
  125. package/dist/src/collections/tree/struct.d.ts.map +1 -0
  126. package/dist/src/collections/tree/struct.js +4 -0
  127. package/dist/src/collections/tree/struct.js.map +1 -0
  128. package/dist/src/collections/tree/tree.d.ts +34 -0
  129. package/dist/src/collections/tree/tree.d.ts.map +1 -0
  130. package/dist/src/collections/tree/tree.js +100 -0
  131. package/dist/src/collections/tree/tree.js.map +1 -0
  132. package/dist/src/index.d.ts +18 -0
  133. package/dist/src/index.d.ts.map +1 -0
  134. package/dist/src/index.js +18 -0
  135. package/dist/src/index.js.map +1 -0
  136. package/dist/src/log/index.d.ts +3 -0
  137. package/dist/src/log/index.d.ts.map +1 -0
  138. package/dist/src/log/index.js +3 -0
  139. package/dist/src/log/index.js.map +1 -0
  140. package/dist/src/log/log.d.ts +57 -0
  141. package/dist/src/log/log.d.ts.map +1 -0
  142. package/dist/src/log/log.js +131 -0
  143. package/dist/src/log/log.js.map +1 -0
  144. package/dist/src/log/struct.d.ts +36 -0
  145. package/dist/src/log/struct.d.ts.map +1 -0
  146. package/dist/src/log/struct.js +3 -0
  147. package/dist/src/log/struct.js.map +1 -0
  148. package/dist/src/network/i-key-network.d.ts +21 -0
  149. package/dist/src/network/i-key-network.d.ts.map +1 -0
  150. package/dist/src/network/i-key-network.js +2 -0
  151. package/dist/src/network/i-key-network.js.map +1 -0
  152. package/dist/src/network/i-peer-network.d.ts +8 -0
  153. package/dist/src/network/i-peer-network.d.ts.map +1 -0
  154. package/dist/src/network/i-peer-network.js +2 -0
  155. package/dist/src/network/i-peer-network.js.map +1 -0
  156. package/dist/src/network/i-repo.d.ts +17 -0
  157. package/dist/src/network/i-repo.d.ts.map +1 -0
  158. package/dist/src/network/i-repo.js +2 -0
  159. package/dist/src/network/i-repo.js.map +1 -0
  160. package/dist/src/network/index.d.ts +6 -0
  161. package/dist/src/network/index.d.ts.map +1 -0
  162. package/dist/src/network/index.js +6 -0
  163. package/dist/src/network/index.js.map +1 -0
  164. package/dist/src/network/repo-protocol.d.ts +19 -0
  165. package/dist/src/network/repo-protocol.d.ts.map +1 -0
  166. package/dist/src/network/repo-protocol.js +2 -0
  167. package/dist/src/network/repo-protocol.js.map +1 -0
  168. package/dist/src/network/struct.d.ts +115 -0
  169. package/dist/src/network/struct.d.ts.map +1 -0
  170. package/dist/src/network/struct.js +2 -0
  171. package/dist/src/network/struct.js.map +1 -0
  172. package/dist/src/transaction/actions-engine.d.ts +37 -0
  173. package/dist/src/transaction/actions-engine.d.ts.map +1 -0
  174. package/dist/src/transaction/actions-engine.js +67 -0
  175. package/dist/src/transaction/actions-engine.js.map +1 -0
  176. package/dist/src/transaction/context.d.ts +60 -0
  177. package/dist/src/transaction/context.d.ts.map +1 -0
  178. package/dist/src/transaction/context.js +91 -0
  179. package/dist/src/transaction/context.js.map +1 -0
  180. package/dist/src/transaction/coordinator.d.ts +118 -0
  181. package/dist/src/transaction/coordinator.d.ts.map +1 -0
  182. package/dist/src/transaction/coordinator.js +417 -0
  183. package/dist/src/transaction/coordinator.js.map +1 -0
  184. package/dist/src/transaction/index.d.ts +10 -0
  185. package/dist/src/transaction/index.d.ts.map +1 -0
  186. package/dist/src/transaction/index.js +7 -0
  187. package/dist/src/transaction/index.js.map +1 -0
  188. package/dist/src/transaction/session.d.ts +80 -0
  189. package/dist/src/transaction/session.d.ts.map +1 -0
  190. package/dist/src/transaction/session.js +161 -0
  191. package/dist/src/transaction/session.js.map +1 -0
  192. package/dist/src/transaction/transaction.d.ts +156 -0
  193. package/dist/src/transaction/transaction.d.ts.map +1 -0
  194. package/dist/src/transaction/transaction.js +31 -0
  195. package/dist/src/transaction/transaction.js.map +1 -0
  196. package/dist/src/transaction/validator.d.ts +46 -0
  197. package/dist/src/transaction/validator.d.ts.map +1 -0
  198. package/dist/src/transaction/validator.js +97 -0
  199. package/dist/src/transaction/validator.js.map +1 -0
  200. package/dist/src/transactor/index.d.ts +4 -0
  201. package/dist/src/transactor/index.d.ts.map +1 -0
  202. package/dist/src/transactor/index.js +4 -0
  203. package/dist/src/transactor/index.js.map +1 -0
  204. package/dist/src/transactor/network-transactor.d.ts +36 -0
  205. package/dist/src/transactor/network-transactor.d.ts.map +1 -0
  206. package/dist/src/transactor/network-transactor.js +297 -0
  207. package/dist/src/transactor/network-transactor.js.map +1 -0
  208. package/dist/src/transactor/transactor-source.d.ts +24 -0
  209. package/dist/src/transactor/transactor-source.d.ts.map +1 -0
  210. package/dist/src/transactor/transactor-source.js +62 -0
  211. package/dist/src/transactor/transactor-source.js.map +1 -0
  212. package/dist/src/transactor/transactor.d.ts +38 -0
  213. package/dist/src/transactor/transactor.d.ts.map +1 -0
  214. package/dist/src/transactor/transactor.js +2 -0
  215. package/dist/src/transactor/transactor.js.map +1 -0
  216. package/dist/src/transform/atomic.d.ts +8 -0
  217. package/dist/src/transform/atomic.d.ts.map +1 -0
  218. package/dist/src/transform/atomic.js +14 -0
  219. package/dist/src/transform/atomic.js.map +1 -0
  220. package/dist/src/transform/cache-source.d.ts +13 -0
  221. package/dist/src/transform/cache-source.d.ts.map +1 -0
  222. package/dist/src/transform/cache-source.js +52 -0
  223. package/dist/src/transform/cache-source.js.map +1 -0
  224. package/dist/src/transform/helpers.d.ts +25 -0
  225. package/dist/src/transform/helpers.d.ts.map +1 -0
  226. package/dist/src/transform/helpers.js +105 -0
  227. package/dist/src/transform/helpers.js.map +1 -0
  228. package/dist/src/transform/index.d.ts +6 -0
  229. package/dist/src/transform/index.d.ts.map +1 -0
  230. package/dist/src/transform/index.js +6 -0
  231. package/dist/src/transform/index.js.map +1 -0
  232. package/dist/src/transform/struct.d.ts +19 -0
  233. package/dist/src/transform/struct.d.ts.map +1 -0
  234. package/dist/src/transform/struct.js +2 -0
  235. package/dist/src/transform/struct.js.map +1 -0
  236. package/dist/src/transform/tracker.d.ts +22 -0
  237. package/dist/src/transform/tracker.d.ts.map +1 -0
  238. package/dist/src/transform/tracker.js +64 -0
  239. package/dist/src/transform/tracker.js.map +1 -0
  240. package/dist/src/utility/actor.d.ts +11 -0
  241. package/dist/src/utility/actor.d.ts.map +1 -0
  242. package/dist/src/utility/actor.js +39 -0
  243. package/dist/src/utility/actor.js.map +1 -0
  244. package/dist/src/utility/batch-coordinator.d.ts +56 -0
  245. package/dist/src/utility/batch-coordinator.d.ts.map +1 -0
  246. package/dist/src/utility/batch-coordinator.js +127 -0
  247. package/dist/src/utility/batch-coordinator.js.map +1 -0
  248. package/dist/src/utility/block-id-to-bytes.d.ts +3 -0
  249. package/dist/src/utility/block-id-to-bytes.d.ts.map +1 -0
  250. package/dist/src/utility/block-id-to-bytes.js +7 -0
  251. package/dist/src/utility/block-id-to-bytes.js.map +1 -0
  252. package/dist/src/utility/ensured.d.ts +3 -0
  253. package/dist/src/utility/ensured.d.ts.map +1 -0
  254. package/dist/src/utility/ensured.js +24 -0
  255. package/dist/src/utility/ensured.js.map +1 -0
  256. package/dist/src/utility/groupby.d.ts +8 -0
  257. package/dist/src/utility/groupby.d.ts.map +1 -0
  258. package/dist/src/utility/groupby.js +15 -0
  259. package/dist/src/utility/groupby.js.map +1 -0
  260. package/dist/src/utility/is-record-empty.d.ts +3 -0
  261. package/dist/src/utility/is-record-empty.d.ts.map +1 -0
  262. package/dist/src/utility/is-record-empty.js +7 -0
  263. package/dist/src/utility/is-record-empty.js.map +1 -0
  264. package/dist/src/utility/latches.d.ts +11 -0
  265. package/dist/src/utility/latches.d.ts.map +1 -0
  266. package/dist/src/utility/latches.js +36 -0
  267. package/dist/src/utility/latches.js.map +1 -0
  268. package/dist/src/utility/nameof.d.ts +3 -0
  269. package/dist/src/utility/nameof.d.ts.map +1 -0
  270. package/dist/src/utility/nameof.js +5 -0
  271. package/dist/src/utility/nameof.js.map +1 -0
  272. package/dist/src/utility/pending.d.ts +13 -0
  273. package/dist/src/utility/pending.d.ts.map +1 -0
  274. package/dist/src/utility/pending.js +37 -0
  275. package/dist/src/utility/pending.js.map +1 -0
  276. package/package.json +56 -0
  277. package/src/blocks/block-store.ts +13 -0
  278. package/src/blocks/block-types.ts +11 -0
  279. package/src/blocks/helpers.ts +13 -0
  280. package/src/blocks/index.ts +5 -0
  281. package/src/blocks/structs.ts +17 -0
  282. package/src/btree/btree.ts +804 -0
  283. package/src/btree/independent-trunk.ts +54 -0
  284. package/src/btree/index.ts +5 -0
  285. package/src/btree/key-range.ts +15 -0
  286. package/src/btree/keyset.ts +6 -0
  287. package/src/btree/nodes.ts +25 -0
  288. package/src/btree/path.ts +37 -0
  289. package/src/btree/tree-block.ts +11 -0
  290. package/src/btree/trunk.ts +14 -0
  291. package/src/chain/chain-nodes.ts +24 -0
  292. package/src/chain/chain.ts +324 -0
  293. package/src/chain/index.ts +2 -0
  294. package/src/cluster/i-cluster.ts +6 -0
  295. package/src/cluster/index.ts +2 -0
  296. package/src/cluster/structs.ts +46 -0
  297. package/src/collection/action.ts +31 -0
  298. package/src/collection/collection.ts +200 -0
  299. package/src/collection/index.ts +3 -0
  300. package/src/collection/struct.ts +20 -0
  301. package/src/collections/diary/diary.ts +43 -0
  302. package/src/collections/diary/index.ts +2 -0
  303. package/src/collections/diary/struct.ts +3 -0
  304. package/src/collections/index.ts +2 -0
  305. package/src/collections/tree/collection-trunk.ts +25 -0
  306. package/src/collections/tree/index.ts +2 -0
  307. package/src/collections/tree/readme.md +19 -0
  308. package/src/collections/tree/struct.ts +18 -0
  309. package/src/collections/tree/tree.ts +124 -0
  310. package/src/index.ts +17 -0
  311. package/src/log/index.ts +2 -0
  312. package/src/log/log.ts +155 -0
  313. package/src/log/struct.ts +40 -0
  314. package/src/network/i-key-network.ts +24 -0
  315. package/src/network/i-peer-network.ts +8 -0
  316. package/src/network/i-repo.ts +19 -0
  317. package/src/network/index.ts +5 -0
  318. package/src/network/repo-protocol.ts +12 -0
  319. package/src/network/struct.ts +137 -0
  320. package/src/transaction/actions-engine.ts +83 -0
  321. package/src/transaction/context.ts +103 -0
  322. package/src/transaction/coordinator.ts +583 -0
  323. package/src/transaction/index.ts +30 -0
  324. package/src/transaction/session.ts +182 -0
  325. package/src/transaction/transaction.ts +205 -0
  326. package/src/transaction/validator.ts +150 -0
  327. package/src/transactor/index.ts +4 -0
  328. package/src/transactor/network-transactor.ts +435 -0
  329. package/src/transactor/transactor-source.ts +65 -0
  330. package/src/transactor/transactor.ts +44 -0
  331. package/src/transform/atomic.ts +16 -0
  332. package/src/transform/cache-source.ts +57 -0
  333. package/src/transform/helpers.ts +117 -0
  334. package/src/transform/index.ts +5 -0
  335. package/src/transform/struct.ts +22 -0
  336. package/src/transform/tracker.ts +70 -0
  337. package/src/utility/actor.ts +62 -0
  338. package/src/utility/batch-coordinator.ts +174 -0
  339. package/src/utility/block-id-to-bytes.ts +8 -0
  340. package/src/utility/ensured.ts +32 -0
  341. package/src/utility/groupby.ts +18 -0
  342. package/src/utility/is-record-empty.ts +5 -0
  343. package/src/utility/latches.ts +42 -0
  344. package/src/utility/nameof.ts +7 -0
  345. package/src/utility/pending.ts +41 -0
@@ -0,0 +1,583 @@
1
+ import type { ITransactor, BlockId, CollectionId, Transforms, PendRequest, CommitRequest, ActionId, IBlock, BlockOperations } from "../index.js";
2
+ import type { Transaction, ExecutionResult, ITransactionEngine, CollectionActions } from "./transaction.js";
3
+ import type { PeerId } from "@libp2p/interface";
4
+ import type { Collection } from "../collection/collection.js";
5
+ import { TransactionContext } from "./context.js";
6
+ import { createActionsStatements } from "./actions-engine.js";
7
+ import { createTransactionStamp, createTransactionId } from "./transaction.js";
8
+ import { Log, blockIdsForTransforms, transformsFromTransform } from "../index.js";
9
+
10
+ /**
11
+ * Represents an operation on a block within a collection.
12
+ */
13
+ type Operation =
14
+ | { readonly type: 'insert'; readonly collectionId: CollectionId; readonly blockId: BlockId; readonly block: IBlock }
15
+ | { readonly type: 'update'; readonly collectionId: CollectionId; readonly blockId: BlockId; readonly operations: BlockOperations }
16
+ | { readonly type: 'delete'; readonly collectionId: CollectionId; readonly blockId: BlockId };
17
+
18
+ /**
19
+ * Coordinates multi-collection transactions.
20
+ *
21
+ * This is the ONLY interface for all mutations (single or multi-collection).
22
+ *
23
+ * Responsibilities:
24
+ * - Manage collections (create as needed)
25
+ * - Apply actions to collections (run handlers, write to logs)
26
+ * - Commit transactions by running consensus phases (GATHER, PEND, COMMIT)
27
+ */
28
+ export class TransactionCoordinator {
29
+ constructor(
30
+ private readonly transactor: ITransactor,
31
+ private readonly collections: Map<CollectionId, Collection<any>>
32
+ ) {}
33
+
34
+ /**
35
+ * Apply actions to collections (called by engines during statement execution).
36
+ *
37
+ * This is the core method that engines call to apply actions to collections.
38
+ * Actions are tagged with the stamp ID and executed immediately through collections
39
+ * to update the local snapshot.
40
+ *
41
+ * @param actions - The actions to apply (per collection)
42
+ * @param stampId - The transaction stamp ID to tag actions with
43
+ */
44
+ async applyActions(
45
+ actions: CollectionActions[],
46
+ stampId: string
47
+ ): Promise<void> {
48
+ for (const { collectionId, actions: collectionActions } of actions) {
49
+ // Get collection
50
+ const collection = this.collections.get(collectionId);
51
+ if (!collection) {
52
+ throw new Error(`Collection not found: ${collectionId}`);
53
+ }
54
+
55
+ // Apply each action (tagged with stampId)
56
+ for (const action of collectionActions) {
57
+ const taggedAction = { ...(action as any), transaction: stampId };
58
+ await collection.act(taggedAction);
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Commit a transaction (actions already applied, orchestrate PEND/COMMIT).
65
+ *
66
+ * This is called by TransactionSession.commit() after all statements have been executed.
67
+ * Actions have already been applied to collections via applyActions(), so this method
68
+ * just orchestrates the distributed consensus.
69
+ *
70
+ * @param transaction - The transaction to commit
71
+ */
72
+ async commit(transaction: Transaction): Promise<void> {
73
+ // Collect transforms and determine critical blocks for each affected collection
74
+ const collectionData = Array.from(this.collections.entries())
75
+ .map(([collectionId, collection]) => ({
76
+ collectionId,
77
+ collection,
78
+ transforms: collection.tracker.transforms
79
+ }))
80
+ .filter(({ transforms }) =>
81
+ Object.keys(transforms.inserts).length +
82
+ Object.keys(transforms.updates).length +
83
+ transforms.deletes.length > 0
84
+ );
85
+
86
+ if (collectionData.length === 0) {
87
+ return; // Nothing to commit
88
+ }
89
+
90
+ // Get critical block IDs (log tail) for each affected collection
91
+ // The critical block is the current log tail that must participate in consensus
92
+ const collectionTransforms = new Map<CollectionId, Transforms>();
93
+ const criticalBlocks = new Map<CollectionId, BlockId>();
94
+
95
+ for (const { collectionId, collection, transforms } of collectionData) {
96
+ collectionTransforms.set(collectionId, transforms);
97
+
98
+ // Get the current log tail block ID (critical block)
99
+ const log = await Log.open(collection.tracker, collectionId);
100
+ if (!log) {
101
+ throw new Error(`Log not found for collection ${collectionId}`);
102
+ }
103
+
104
+ const tailPath = await (log as unknown as { chain: { getTail: () => Promise<{ block: { header: { id: BlockId } } } | undefined> } }).chain.getTail();
105
+ if (tailPath) {
106
+ criticalBlocks.set(collectionId, tailPath.block.header.id);
107
+ }
108
+ }
109
+
110
+ // Compute hash of ALL operations across ALL collections
111
+ // This hash is used for validation - validators re-execute the transaction
112
+ // and compare their computed operations hash with this one
113
+ const allOperations = collectionData.flatMap(({ collectionId, transforms }) => [
114
+ ...Object.entries(transforms.inserts).map(([blockId, block]) =>
115
+ ({ type: 'insert' as const, collectionId, blockId, block })
116
+ ),
117
+ ...Object.entries(transforms.updates).map(([blockId, operations]) =>
118
+ ({ type: 'update' as const, collectionId, blockId, operations })
119
+ ),
120
+ ...transforms.deletes.map(blockId =>
121
+ ({ type: 'delete' as const, collectionId, blockId })
122
+ )
123
+ ]);
124
+
125
+ const operationsHash = this.hashOperations(allOperations);
126
+
127
+ // Execute consensus phases (GATHER, PEND, COMMIT)
128
+ const coordResult = await this.coordinateTransaction(
129
+ transaction,
130
+ operationsHash,
131
+ collectionTransforms,
132
+ criticalBlocks
133
+ );
134
+
135
+ if (!coordResult.success) {
136
+ throw new Error(`Transaction commit failed: ${coordResult.error}`);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Rollback a transaction (undo applied actions).
142
+ *
143
+ * This is called by TransactionSession.rollback() to undo all actions
144
+ * that were applied via applyActions().
145
+ *
146
+ * @param _stampId - The transaction stamp ID to rollback (currently unused - we clear all trackers)
147
+ */
148
+ async rollback(_stampId: string): Promise<void> {
149
+ // Clear trackers for all collections
150
+ // This discards all pending changes that were applied via applyActions()
151
+ // TODO: In the future, we may want to track which collections were affected by
152
+ // a specific stampId and only reset those trackers
153
+ for (const collection of this.collections.values()) {
154
+ // Reset the tracker to discard all pending transforms
155
+ collection.tracker.reset();
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Compute hash of all operations in a transaction.
161
+ * This hash is used for validation - validators re-execute the transaction
162
+ * and compare their computed operations hash with this one.
163
+ */
164
+ private hashOperations(operations: readonly Operation[]): string {
165
+ const operationsData = JSON.stringify(operations);
166
+ const hash = Array.from(operationsData).reduce((acc, char) => {
167
+ const charCode = char.charCodeAt(0);
168
+ return ((acc << 5) - acc + charCode) & acc;
169
+ }, 0);
170
+ return `ops:${Math.abs(hash).toString(36)}`;
171
+ }
172
+
173
+ /**
174
+ * Commit a transaction context.
175
+ *
176
+ * @deprecated Use TransactionSession instead of TransactionContext
177
+ * This is called by TransactionContext.commit().
178
+ *
179
+ * @param context - The transaction context to commit
180
+ * @returns Execution result with actions and results
181
+ */
182
+ async commitTransaction(context: TransactionContext): Promise<ExecutionResult> {
183
+ const collectionActions = Array.from(context.getCollectionActions().entries()).map(
184
+ ([collectionId, actions]) => ({ collectionId, actions })
185
+ );
186
+
187
+ if (collectionActions.length === 0) {
188
+ return { success: true }; // Nothing to commit
189
+ }
190
+
191
+ // Create transaction statements
192
+ const statements = createActionsStatements(collectionActions);
193
+ const reads = context.getReads();
194
+
195
+ // Create stamp from context
196
+ const stamp = createTransactionStamp(
197
+ 'local', // TODO: Get from context or coordinator
198
+ Date.now(),
199
+ '', // TODO: Get from engine
200
+ context.engine
201
+ );
202
+
203
+ const transaction: Transaction = {
204
+ stamp,
205
+ statements,
206
+ reads,
207
+ id: createTransactionId(stamp.id, statements, reads)
208
+ };
209
+
210
+ // Create ActionsEngine for execution (TransactionContext only supports actions)
211
+ const { ActionsEngine } = await import('./actions-engine.js');
212
+ const engine = new ActionsEngine(this);
213
+
214
+ // Execute through standard path
215
+ return await this.execute(transaction, engine);
216
+ }
217
+
218
+ /**
219
+ * Execute a fully-formed transaction.
220
+ *
221
+ * This can be called directly with a complete transaction (e.g., from Quereus),
222
+ * or indirectly via commitTransaction().
223
+ *
224
+ * @param transaction - The transaction to execute
225
+ * @param engine - The engine to use for executing the transaction
226
+ * @returns Execution result with actions and results
227
+ */
228
+ async execute(transaction: Transaction, engine: ITransactionEngine): Promise<ExecutionResult> {
229
+ // 1. Validate engine matches transaction
230
+ // Note: We don't enforce this strictly since the engine is passed in explicitly
231
+ // The caller is responsible for ensuring the correct engine is used
232
+
233
+ const result = await engine.execute(transaction);
234
+ if (!result.success) {
235
+ return result;
236
+ }
237
+
238
+ if (!result.actions || result.actions.length === 0) {
239
+ return { success: true }; // Nothing to do
240
+ }
241
+
242
+ // 2. Apply actions to collections and collect transforms
243
+ const collectionTransforms = new Map<CollectionId, Transforms>();
244
+ const criticalBlocks = new Map<CollectionId, BlockId>();
245
+ const actionResults = new Map<CollectionId, any[]>();
246
+ const allCollectionIds = result.actions.map(ca => ca.collectionId);
247
+
248
+ for (const collectionActions of result.actions) {
249
+ const applyResult = await this.applyActionsToCollection(
250
+ collectionActions,
251
+ transaction,
252
+ allCollectionIds
253
+ );
254
+
255
+ if (!applyResult.success) {
256
+ return { success: false, error: applyResult.error };
257
+ }
258
+
259
+ collectionTransforms.set(collectionActions.collectionId, applyResult.transforms!);
260
+ criticalBlocks.set(collectionActions.collectionId, applyResult.logTailBlockId!);
261
+ actionResults.set(collectionActions.collectionId, applyResult.results!);
262
+ }
263
+
264
+ // 3. Compute operations hash for validation
265
+ const allOperations = Array.from(collectionTransforms.entries()).flatMap(([collectionId, transforms]) => [
266
+ ...Object.entries(transforms.inserts).map(([blockId, block]) =>
267
+ ({ type: 'insert' as const, collectionId, blockId, block })
268
+ ),
269
+ ...Object.entries(transforms.updates).map(([blockId, operations]) =>
270
+ ({ type: 'update' as const, collectionId, blockId, operations })
271
+ ),
272
+ ...transforms.deletes.map(blockId =>
273
+ ({ type: 'delete' as const, collectionId, blockId })
274
+ )
275
+ ]);
276
+ const operationsHash = this.hashOperations(allOperations);
277
+
278
+ // 4. Coordinate (GATHER if multi-collection)
279
+ const coordResult = await this.coordinateTransaction(
280
+ transaction,
281
+ operationsHash,
282
+ collectionTransforms,
283
+ criticalBlocks
284
+ );
285
+
286
+ if (!coordResult.success) {
287
+ return coordResult;
288
+ }
289
+
290
+ // 4. Return results from actions
291
+ return {
292
+ success: true,
293
+ actions: result.actions,
294
+ results: actionResults
295
+ };
296
+ }
297
+
298
+ /**
299
+ * Apply actions to a collection.
300
+ *
301
+ * This runs the action handlers, writes to the log, and collects transforms.
302
+ */
303
+ private async applyActionsToCollection(
304
+ collectionActions: CollectionActions,
305
+ transaction: Transaction,
306
+ allCollectionIds: CollectionId[]
307
+ ): Promise<{
308
+ success: boolean;
309
+ transforms?: Transforms;
310
+ logTailBlockId?: BlockId;
311
+ results?: any[];
312
+ error?: string;
313
+ }> {
314
+ const collection = this.collections.get(collectionActions.collectionId);
315
+ if (!collection) {
316
+ return {
317
+ success: false,
318
+ error: `Collection not found: ${collectionActions.collectionId}`
319
+ };
320
+ }
321
+
322
+ // At this point, actions have already been executed through collection.act()
323
+ // when they were added to the TransactionContext. The collection's tracker
324
+ // already has the transforms, and the actions are in the pending buffer.
325
+
326
+ // Get transforms from the collection's tracker
327
+ const transforms = collection.tracker.transforms;
328
+
329
+ // Write actions to the collection's log to get the log tail block ID
330
+ const log = await Log.open(collection.tracker, collectionActions.collectionId);
331
+ if (!log) {
332
+ return {
333
+ success: false,
334
+ error: `Log not found for collection ${collectionActions.collectionId}`
335
+ };
336
+ }
337
+
338
+ // Generate action ID from transaction ID
339
+ const actionId = transaction.id;
340
+ const newRev = (collection['source'].actionContext?.rev ?? 0) + 1;
341
+
342
+ // Add actions to log (this updates the tracker with log block changes)
343
+ const addResult = await log.addActions(
344
+ collectionActions.actions,
345
+ actionId,
346
+ newRev,
347
+ () => blockIdsForTransforms(transforms),
348
+ allCollectionIds
349
+ );
350
+
351
+ // Return the transforms and log tail block ID
352
+ return {
353
+ success: true,
354
+ transforms,
355
+ logTailBlockId: addResult.tailPath.block.header.id,
356
+ results: [] // TODO: Collect results from action handlers when we support read operations
357
+ };
358
+ }
359
+
360
+ /**
361
+ * Coordinate a transaction across multiple collections.
362
+ *
363
+ * @param transaction - The transaction to coordinate
364
+ * @param operationsHash - Hash of all operations for validation
365
+ * @param collectionTransforms - Map of collectionId to its transforms
366
+ * @param criticalBlocks - Map of collectionId to its log tail blockId
367
+ */
368
+ private async coordinateTransaction(
369
+ transaction: Transaction,
370
+ operationsHash: string,
371
+ collectionTransforms: Map<CollectionId, Transforms>,
372
+ criticalBlocks: Map<CollectionId, BlockId>
373
+ ): Promise<{ success: boolean; error?: string }> {
374
+ // 1. GATHER phase: collect critical cluster nominees (skip if single collection)
375
+ const criticalBlockIds = Array.from(criticalBlocks.values());
376
+ const superclusterNominees = await this.gatherPhase(criticalBlockIds);
377
+
378
+ // 2. PEND phase: distribute to all block clusters
379
+ const pendResult = await this.pendPhase(
380
+ transaction,
381
+ operationsHash,
382
+ collectionTransforms,
383
+ superclusterNominees
384
+ );
385
+ if (!pendResult.success) {
386
+ return pendResult;
387
+ }
388
+
389
+ // 3. COMMIT phase: commit to all critical blocks
390
+ const commitResult = await this.commitPhase(
391
+ transaction.id as ActionId,
392
+ criticalBlockIds,
393
+ pendResult.pendedBlockIds!
394
+ );
395
+ if (!commitResult.success) {
396
+ // Cancel pending actions on failure
397
+ await this.cancelPhase(transaction.id as ActionId, collectionTransforms);
398
+ return commitResult;
399
+ }
400
+
401
+ // 4. PROPAGATE and CHECKPOINT phases are handled by clusters automatically
402
+ // (as per user's note: "managed by each cluster, the client doesn't have to worry about them")
403
+
404
+ return { success: true };
405
+ }
406
+
407
+ /**
408
+ * GATHER phase: Collect nominees from critical clusters.
409
+ *
410
+ * Skip if only one collection affected (single-collection consensus).
411
+ *
412
+ * @param criticalBlockIds - Block IDs of all log tails
413
+ * @returns Set of peer IDs to use for consensus, or null for single-collection
414
+ */
415
+ private async gatherPhase(
416
+ criticalBlockIds: readonly BlockId[]
417
+ ): Promise<ReadonlySet<PeerId> | null> {
418
+ // Skip GATHER if only one collection affected
419
+ if (criticalBlockIds.length === 1) {
420
+ return null; // Use normal single-collection consensus
421
+ }
422
+
423
+ // Check if transactor supports cluster queries (optional method)
424
+ if (!this.transactor.queryClusterNominees) {
425
+ // Transactor doesn't support cluster queries - proceed without supercluster
426
+ return null;
427
+ }
428
+
429
+ // Query each critical cluster for their nominees and merge into supercluster
430
+ const nomineePromises = criticalBlockIds.map(blockId =>
431
+ this.transactor.queryClusterNominees!(blockId)
432
+ );
433
+ const results = await Promise.all(nomineePromises);
434
+
435
+ // Merge all nominees into a single set
436
+ const supercluster = results.reduce(
437
+ (acc, result) => {
438
+ result.nominees.forEach(nominee => acc.add(nominee));
439
+ return acc;
440
+ },
441
+ new Set<PeerId>()
442
+ );
443
+
444
+ return supercluster;
445
+ }
446
+
447
+ /**
448
+ * PEND phase: Distribute transaction to all affected block clusters.
449
+ *
450
+ * @param transaction - The full transaction for replay/validation
451
+ * @param operationsHash - Hash of all operations for validation
452
+ * @param collectionTransforms - Map of collectionId to its transforms
453
+ * @param superclusterNominees - Nominees for multi-collection consensus (null for single-collection)
454
+ */
455
+ private async pendPhase(
456
+ transaction: Transaction,
457
+ operationsHash: string,
458
+ collectionTransforms: ReadonlyMap<CollectionId, Transforms>,
459
+ superclusterNominees: ReadonlySet<PeerId> | null
460
+ ): Promise<{ success: boolean; error?: string; pendedBlockIds?: Map<CollectionId, BlockId[]> }> {
461
+ if (collectionTransforms.size === 0) {
462
+ return { success: false, error: 'No transforms to pend' };
463
+ }
464
+
465
+ const pendedBlockIds = new Map<CollectionId, BlockId[]>();
466
+ const actionId = transaction.id as ActionId;
467
+ const nominees = superclusterNominees ? Array.from(superclusterNominees) : undefined;
468
+
469
+ // Pend each collection's transforms
470
+ for (const [collectionId, transforms] of collectionTransforms.entries()) {
471
+ const collection = this.collections.get(collectionId);
472
+ if (!collection) {
473
+ return { success: false, error: `Collection not found: ${collectionId}` };
474
+ }
475
+
476
+ // Get revision from the collection's source
477
+ const rev = (collection['source'].actionContext?.rev ?? 0) + 1;
478
+
479
+ // Create pend request with transaction and operations hash for validation
480
+ const pendRequest: PendRequest = {
481
+ actionId,
482
+ rev,
483
+ transforms,
484
+ policy: 'r', // Return policy: fail but return pending actions
485
+ transaction,
486
+ operationsHash,
487
+ superclusterNominees: nominees
488
+ };
489
+
490
+ // Pend the transaction
491
+ const pendResult = await this.transactor.pend(pendRequest);
492
+ if (!pendResult.success) {
493
+ return {
494
+ success: false,
495
+ error: `Pend failed for collection ${collectionId}: ${pendResult.reason}`
496
+ };
497
+ }
498
+
499
+ // Store the pended block IDs for commit phase
500
+ pendedBlockIds.set(collectionId, pendResult.blockIds);
501
+ }
502
+
503
+ return { success: true, pendedBlockIds };
504
+ }
505
+
506
+ /**
507
+ * COMMIT phase: Commit to all critical blocks.
508
+ */
509
+ private async commitPhase(
510
+ actionId: ActionId,
511
+ criticalBlockIds: BlockId[],
512
+ pendedBlockIds: Map<CollectionId, BlockId[]>
513
+ ): Promise<{ success: boolean; error?: string }> {
514
+ // Commit each collection's transaction
515
+ for (const [collectionId, blockIds] of pendedBlockIds.entries()) {
516
+ const collection = this.collections.get(collectionId);
517
+ if (!collection) {
518
+ return { success: false, error: `Collection not found: ${collectionId}` };
519
+ }
520
+
521
+ // Get revision
522
+ const rev = (collection['source'].actionContext?.rev ?? 0) + 1;
523
+
524
+ // Find the critical block (log tail) for this collection
525
+ const logTailBlockId = criticalBlockIds.find(blockId =>
526
+ blockIds.includes(blockId)
527
+ );
528
+
529
+ if (!logTailBlockId) {
530
+ return {
531
+ success: false,
532
+ error: `Log tail block not found for collection ${collectionId}`
533
+ };
534
+ }
535
+
536
+ // Create commit request
537
+ const commitRequest: CommitRequest = {
538
+ actionId,
539
+ blockIds,
540
+ tailId: logTailBlockId,
541
+ rev
542
+ };
543
+
544
+ // Commit the transaction
545
+ const commitResult = await this.transactor.commit(commitRequest);
546
+ if (!commitResult.success) {
547
+ return {
548
+ success: false,
549
+ error: `Commit failed for collection ${collectionId}`
550
+ };
551
+ }
552
+ }
553
+
554
+ return { success: true };
555
+ }
556
+
557
+ /**
558
+ * CANCEL phase: Cancel pending actions on all affected blocks.
559
+ */
560
+ private async cancelPhase(
561
+ actionId: ActionId,
562
+ collectionTransforms: Map<CollectionId, Transforms>
563
+ ): Promise<void> {
564
+ // Cancel each collection's pending transaction
565
+ for (const [collectionId, transforms] of collectionTransforms.entries()) {
566
+ const collection = this.collections.get(collectionId);
567
+ if (!collection) {
568
+ continue; // Skip if collection not found
569
+ }
570
+
571
+ // Get the block IDs from transforms
572
+ const blockIds = blockIdsForTransforms(transforms);
573
+
574
+ // Cancel the transaction
575
+ await this.transactor.cancel({
576
+ actionId,
577
+ blockIds
578
+ });
579
+ }
580
+ }
581
+
582
+ }
583
+
@@ -0,0 +1,30 @@
1
+ export type {
2
+ Transaction,
3
+ TransactionStamp,
4
+ ReadDependency,
5
+ TransactionRef,
6
+ ITransactionEngine,
7
+ ExecutionResult,
8
+ CollectionActions,
9
+ ValidationResult,
10
+ ITransactionValidator
11
+ } from './transaction.js';
12
+
13
+ export {
14
+ createTransactionStamp,
15
+ createTransactionId
16
+ } from './transaction.js';
17
+
18
+ export {
19
+ ActionsEngine,
20
+ ACTIONS_ENGINE_ID,
21
+ createActionsStatements
22
+ } from './actions-engine.js';
23
+
24
+ export type { ActionsStatement } from './actions-engine.js';
25
+
26
+ export { TransactionCoordinator } from './coordinator.js';
27
+ export { TransactionContext } from './context.js';
28
+ export { TransactionSession } from './session.js';
29
+ export { TransactionValidator } from './validator.js';
30
+ export type { EngineRegistration, ValidationCoordinatorFactory } from './validator.js';