@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,200 @@
1
+ import type { IBlock, Action, ActionType, ActionHandler, BlockId, ITransactor, ActionEntry, BlockStore } from "../index.js";
2
+ import { Log, Atomic, Tracker, copyTransforms, CacheSource, isTransformsEmpty, TransactorSource, blockIdsForTransforms, transformsFromTransform } from "../index.js";
3
+ import type { CollectionHeaderBlock, CollectionId, ICollection } from "./index.js";
4
+ import { randomBytes } from '@libp2p/crypto';
5
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
6
+ import { Latches } from "../utility/latches.js";
7
+
8
+ const PendingRetryDelayMs = 100;
9
+
10
+ export type CollectionInitOptions<TAction> = {
11
+ modules: Record<ActionType, ActionHandler<TAction>>;
12
+ createHeaderBlock: (id: BlockId, store: BlockStore<IBlock>) => IBlock;
13
+ /** Called for each local action that is potentially in conflict with a remote action.
14
+ * @param action - The local action to check
15
+ * @param potential - The remote action that is potentially in conflict
16
+ * @returns The original action, a replacement action (return a new instance; will be
17
+ * applied through act()), or undefined to discard this action
18
+ */
19
+ filterConflict?: (action: Action<TAction>, potential: Action<TAction>[]) => Action<TAction> | undefined
20
+ }
21
+
22
+ export class Collection<TAction> implements ICollection<TAction> {
23
+ private pending: Action<TAction>[] = [];
24
+
25
+ protected constructor(
26
+ public readonly id: CollectionId,
27
+ public readonly transactor: ITransactor,
28
+ private readonly handlers: Record<ActionType, ActionHandler<TAction>>,
29
+ private readonly source: TransactorSource<IBlock>,
30
+ /** Cache of unmodified blocks from the source */
31
+ private readonly sourceCache: CacheSource<IBlock>,
32
+ /** Tracked Changes */
33
+ public readonly tracker: Tracker<IBlock>,
34
+ private readonly filterConflict?: (action: Action<TAction>, potential: Action<TAction>[]) => Action<TAction> | undefined,
35
+ ) {
36
+ }
37
+
38
+ static async createOrOpen<TAction>(transactor: ITransactor, id: CollectionId, init: CollectionInitOptions<TAction>) {
39
+ // Start with a context that has an infinite revision number to ensure that we always fetch the latest log information
40
+ const source = new TransactorSource(id, transactor, undefined);
41
+ const sourceCache = new CacheSource(source);
42
+ const tracker = new Tracker(sourceCache);
43
+ const header = await source.tryGet(id) as CollectionHeaderBlock | undefined;
44
+
45
+ if (header) { // Collection already exists
46
+ const log = (await Log.open<Action<TAction>>(tracker, id))!;
47
+ source.actionContext = await log.getActionContext();
48
+ } else { // Collection does not exist
49
+ const headerBlock = init.createHeaderBlock(id, tracker);
50
+ tracker.insert(headerBlock);
51
+ source.actionContext = undefined;
52
+ await Log.open<Action<TAction>>(tracker, id);
53
+ }
54
+
55
+ return new Collection(id, transactor, init.modules, source, sourceCache, tracker, init.filterConflict);
56
+ }
57
+
58
+ async act(...actions: Action<TAction>[]) {
59
+ await this.internalTransact(...actions);
60
+ this.pending.push(...actions);
61
+ }
62
+
63
+ private async internalTransact(...actions: Action<TAction>[]) {
64
+ const atomic = new Atomic(this.tracker);
65
+
66
+ for (const action of actions) {
67
+ const handler = this.handlers[action.type];
68
+ if (!handler) {
69
+ throw new Error(`No handler for action type ${action.type}`);
70
+ }
71
+ await handler(action, atomic);
72
+ }
73
+
74
+ atomic.commit();
75
+ }
76
+
77
+ /** Load external changes and update our context to the latest log revision - resolve any conflicts with our pending actions. */
78
+ async update() {
79
+ // Start with a context that can see to the end of the log
80
+ const source = new TransactorSource(this.id, this.transactor, undefined);
81
+ const tracker = new Tracker(source);
82
+
83
+ // Get the latest entries from the log, starting from where we left off
84
+ const actionContext = this.source.actionContext;
85
+ const log = await Log.open<Action<TAction>>(tracker, this.id);
86
+ const latest = log ? await log.getFrom(actionContext?.rev ?? 0) : undefined;
87
+
88
+ // Process the entries and track the blocks they affect
89
+ let anyConflicts = false;
90
+ for (const entry of latest?.entries ?? []) {
91
+ // Filter any pending actions that conflict with the remote actions
92
+ this.pending = this.pending.map(p => this.doFilterConflict(p, entry.actions) ? p : undefined)
93
+ .filter(Boolean) as Action<TAction>[];
94
+ this.sourceCache.clear(entry.blockIds);
95
+ anyConflicts = anyConflicts || tracker.conflicts(new Set(entry.blockIds)).length > 0;
96
+ }
97
+
98
+ // On conflicts, clear related caching and block-tracking and replay logical operations
99
+ if (anyConflicts) {
100
+ await this.replayActions();
101
+ }
102
+
103
+ // Update our context to the latest
104
+ this.source.actionContext = latest?.context;
105
+ }
106
+
107
+ /** Push our pending actions to the transactor */
108
+ async sync() {
109
+ const lockId = `Collection.sync:${this.id}`;
110
+ const release = await Latches.acquire(lockId);
111
+ try {
112
+ const bytes = randomBytes(16);
113
+ const actionId = uint8ArrayToString(bytes, 'base64url');
114
+
115
+ while (this.pending.length || !isTransformsEmpty(this.tracker.transforms)) {
116
+ // Snapshot the pending actions so that any new actions aren't assumed to be part of this action
117
+ const pending = [...this.pending];
118
+
119
+ // Create a snapshot tracker for the action, so that we can ditch the log changes if we have to retry the action
120
+ const snapshot = copyTransforms(this.tracker.transforms);
121
+ const tracker = new Tracker(this.sourceCache, snapshot);
122
+
123
+ // Add the action to the log (in local tracking space)
124
+ const log = await Log.open<Action<TAction>>(tracker, this.id);
125
+ if (!log) {
126
+ throw new Error(`Log not found for collection ${this.id}`);
127
+ }
128
+ const newRev = (this.source.actionContext?.rev ?? 0) + 1;
129
+ const addResult = await log.addActions(pending, actionId, newRev, () => tracker.transformedBlockIds());
130
+
131
+ // Commit the action to the transactor
132
+ const staleFailure = await this.source.transact(tracker.transforms, actionId, newRev, this.id, addResult.tailPath.block.header.id);
133
+ if (staleFailure) {
134
+ if (staleFailure.pending) {
135
+ // Wait for short time to allow the pending actions to commit (bounded backoff)
136
+ await new Promise(resolve => setTimeout(resolve, PendingRetryDelayMs));
137
+ }
138
+ await this.update();
139
+ } else {
140
+ // Clear the pending actions that were part of this action
141
+ this.pending = this.pending.slice(pending.length);
142
+ // Reset cache and replay any actions that were added during the action
143
+ const transforms = tracker.reset();
144
+ await this.replayActions();
145
+ this.sourceCache.transformCache(transforms);
146
+ this.source.actionContext = this.source.actionContext
147
+ ? { committed: [...this.source.actionContext.committed, { actionId, rev: newRev }], rev: newRev }
148
+ : { committed: [{ actionId, rev: newRev }], rev: newRev };
149
+ }
150
+ }
151
+ } finally {
152
+ release();
153
+ }
154
+ }
155
+
156
+ async updateAndSync() {
157
+ // TODO: introduce timer and potentially change stats to determine when to sync, rather than always syncing
158
+ await this.update();
159
+ await this.sync();
160
+ }
161
+
162
+ async *selectLog(forward = true): AsyncIterableIterator<Action<TAction>> {
163
+ const log = await Log.open<Action<TAction>>(this.tracker, this.id);
164
+ if (!log) {
165
+ throw new Error(`Log not found for collection ${this.id}`);
166
+ }
167
+ for await (const entry of log.select(undefined, forward)) {
168
+ if (entry.action) {
169
+ yield* forward ? entry.action.actions : entry.action.actions.reverse();
170
+ }
171
+ }
172
+ }
173
+
174
+ private async replayActions() {
175
+ this.tracker.reset();
176
+ // Because pending could be appended while we're async, we need to snapshot and repeat until empty
177
+ while (this.pending.length) {
178
+ const pending = [...this.pending];
179
+ this.pending = [];
180
+ await this.internalTransact(...pending);
181
+ }
182
+ }
183
+
184
+ /** Called for each local action that may be in conflict with a remote action.
185
+ * @param action - The local action to check
186
+ * @param potential - The remote action that is potentially in conflict
187
+ * @returns true if the action should be kept, false to discard it
188
+ */
189
+ protected doFilterConflict(action: Action<TAction>, potential: Action<TAction>[]) {
190
+ if (this.filterConflict) {
191
+ const replacement = this.filterConflict(action, potential);
192
+ if (!replacement) {
193
+ return false;
194
+ } else if (replacement !== action) {
195
+ this.act(replacement);
196
+ }
197
+ }
198
+ return true;
199
+ }
200
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./collection.js";
2
+ export * from "./struct.js";
3
+ export * from "./action.js";
@@ -0,0 +1,20 @@
1
+ import type { IBlock, BlockId, Action } from "../index.js";
2
+
3
+ export type CollectionId = BlockId;
4
+
5
+ export type CollectionHeaderType = 'CH';
6
+
7
+ export type CollectionHeaderBlock = IBlock & {
8
+ header: {
9
+ type: CollectionHeaderType;
10
+ };
11
+ };
12
+
13
+ export interface ICollection<TAction> {
14
+ update(): Promise<void>;
15
+ sync(): Promise<void>;
16
+ }
17
+
18
+ export type CreateCollectionAction = Action<void> & {
19
+ type: "create";
20
+ };
@@ -0,0 +1,43 @@
1
+ import { Collection } from "../../index.js";
2
+ import type { ITransactor, Action, BlockId, BlockStore, IBlock, CollectionInitOptions, CollectionId } from "../../index.js";
3
+ import { DiaryHeaderBlockType } from "./index.js";
4
+
5
+ export class Diary<TEntry> {
6
+ private constructor(
7
+ private readonly collection: Collection<TEntry>
8
+ ) {
9
+ }
10
+
11
+ static async create<TEntry>(network: ITransactor, id: CollectionId): Promise<Diary<TEntry>> {
12
+ const init: CollectionInitOptions<TEntry> = {
13
+ modules: {
14
+ "append": async (action, trx) => {
15
+ // Append-only diary doesn't need to modify any blocks
16
+ // All entries are stored in the log
17
+ }
18
+ },
19
+ createHeaderBlock: (id: BlockId, store: BlockStore<IBlock>) => ({
20
+ header: store.createBlockHeader(DiaryHeaderBlockType, id)
21
+ })
22
+ };
23
+
24
+ const collection = await Collection.createOrOpen(network, id, init);
25
+ return new Diary<TEntry>(collection);
26
+ }
27
+
28
+ async append(data: TEntry): Promise<void> {
29
+ const action: Action<TEntry> = {
30
+ type: "append",
31
+ data: data
32
+ };
33
+
34
+ await this.collection.act(action);
35
+ await this.collection.updateAndSync();
36
+ }
37
+
38
+ async *select(forward = true): AsyncIterableIterator<TEntry> {
39
+ for await (const entry of this.collection.selectLog(forward)) {
40
+ yield entry.data;
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./diary.js";
2
+ export * from "./struct.js";
@@ -0,0 +1,3 @@
1
+ import { registerBlockType } from "../../index.js";
2
+
3
+ export const DiaryHeaderBlockType = registerBlockType("DIH", "DiaryHeaderBlock");
@@ -0,0 +1,2 @@
1
+ export * from "./diary/index.js";
2
+ export * from "./tree/index.js";
@@ -0,0 +1,25 @@
1
+ import { apply, get } from "../../index.js";
2
+ import type { BlockId, BlockStore, IBlock, ITreeTrunk } from "../../index.js";
3
+ import type { ITreeNode } from "../../btree/nodes.js";
4
+ import { rootId$, type TreeCollectionHeaderBlock } from "./struct.js";
5
+
6
+ export class CollectionTrunk implements ITreeTrunk {
7
+ constructor(
8
+ private readonly store: BlockStore<IBlock>,
9
+ private readonly collectionId: BlockId,
10
+ ) {}
11
+
12
+ async get(): Promise<ITreeNode> {
13
+ return await get(this.store, await this.getId());
14
+ }
15
+
16
+ async set(node: ITreeNode): Promise<void> {
17
+ const header = await get(this.store, this.collectionId) as TreeCollectionHeaderBlock;
18
+ apply(this.store, header, [rootId$, 0, 1, node.header.id]);
19
+ }
20
+
21
+ async getId(): Promise<BlockId> {
22
+ const header = await get(this.store, this.collectionId) as TreeCollectionHeaderBlock;
23
+ return header.rootId;
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./tree.js";
2
+ export * from "./struct.js";
@@ -0,0 +1,19 @@
1
+ # Tree Collection
2
+
3
+ The Tree Collection combines the logical transaction logging of a collection, with a BTree data structure.
4
+
5
+ ## CollectionTrunk
6
+
7
+ To avoid having both a collection header and a btree header, we enable the collection header to double as the btree header. This is accomplished using the "...Trunk" abstraction provided by the BTree. The trunk interface abstracts accessing or updating the root of the tree, from the btree proper. For this implementation, we introduce a CollectionTrunk class that implements the trunk interface. The CollectionTrunk reads the root id from the collection header, and updates the collection header with the new root id when the root changes.
8
+
9
+ ## Replace Action
10
+
11
+ The replace action is a unit of change to the tree collection. It is a list of key-value pairs, where the key is the key to replace, and the value is the new value to replace the old value with. If the value is not provided, the key is deleted.
12
+
13
+ ```ts
14
+ tree.replace([
15
+ { key: 5, value: { a: 1, b: 'Value' } },
16
+ { key: 10 },
17
+ ]);
18
+ ```
19
+ Replaces the value at key 5 with { a: 1, b: 'Value' }, and deletes the value at key 10.
@@ -0,0 +1,18 @@
1
+ import { type BlockId, type CollectionHeaderBlock, nameof, registerBlockType } from "../../index.js";
2
+
3
+ export const TreeHeaderBlockType = registerBlockType("TRE", "TreeHeaderBlock");
4
+
5
+ export type TreeCollectionHeaderBlock = CollectionHeaderBlock & {
6
+ rootId: BlockId;
7
+ };
8
+
9
+ export const rootId$ = nameof<TreeCollectionHeaderBlock>("rootId");
10
+
11
+ /** Represents a unit of change to a tree collection. */
12
+ export type TreeReplaceAction<TKey, TEntry> = [
13
+ // The key to replace
14
+ key: TKey,
15
+ // The new entry to replace the old entry with (if not provided, the key is deleted)
16
+ entry?: TEntry,
17
+ ][];
18
+
@@ -0,0 +1,124 @@
1
+ import { Collection, type CollectionInitOptions, type CollectionId } from "../../collection/index.js";
2
+ import type { ITransactor, BlockId, BlockStore, IBlock } from "../../index.js";
3
+ import { BTree, type Path, type KeyRange } from "../../btree/index.js";
4
+ import { CollectionTrunk } from "./collection-trunk.js";
5
+ import { TreeHeaderBlockType, type TreeReplaceAction } from "./struct.js";
6
+
7
+ export class Tree<TKey, TEntry> {
8
+
9
+ private constructor(
10
+ private readonly collection: Collection<TreeReplaceAction<TKey, TEntry>>,
11
+ private readonly btree: BTree<TKey, TEntry>,
12
+ ) {
13
+ }
14
+
15
+ static async createOrOpen<TKey, TEntry>(
16
+ network: ITransactor,
17
+ id: CollectionId,
18
+ keyFromEntry = (entry: TEntry) => entry as unknown as TKey,
19
+ compare = (a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0,
20
+ ): Promise<Tree<TKey, TEntry>> {
21
+ // Tricky bootstrapping here:
22
+ // We need the root id to initialize the collection header, so we create the btree in the create collection header callback.
23
+ let btree: BTree<TKey, TEntry> | undefined;
24
+ const init: CollectionInitOptions<TreeReplaceAction<TKey, TEntry>> = {
25
+ modules: {
26
+ "replace": async ({ data: actions }, trx) => {
27
+ for (const [key, entry] of actions) {
28
+ if (entry) {
29
+ await btree!.upsert(entry);
30
+ } else {
31
+ await btree!.deleteAt((await btree!.find(key)));
32
+ }
33
+ }
34
+ }
35
+ },
36
+ createHeaderBlock: (id: BlockId, store: BlockStore<IBlock>) => { // Only called if the collection does not exist
37
+ let rootId: BlockId;
38
+ btree = BTree.create<TKey, TEntry>(store, (s, r) => {
39
+ rootId = r;
40
+ return new CollectionTrunk(store, id);
41
+ }, keyFromEntry, compare);
42
+ return {
43
+ header: store.createBlockHeader(TreeHeaderBlockType, id),
44
+ rootId: rootId!,
45
+ }
46
+ }
47
+ };
48
+
49
+ const collection = await Collection.createOrOpen<TreeReplaceAction<TKey, TEntry>>(network, id, init);
50
+ btree = btree ?? new BTree<TKey, TEntry>(collection.tracker, new CollectionTrunk(collection.tracker, collection.id), keyFromEntry, compare);
51
+ return new Tree<TKey, TEntry>(collection, btree);
52
+ }
53
+
54
+ async replace(data: TreeReplaceAction<TKey, TEntry>): Promise<void> {
55
+ await this.collection.act({ type: "replace", data });
56
+ await this.collection.updateAndSync();
57
+ }
58
+
59
+ /**
60
+ * Update the local state from the network.
61
+ * Call this before reading to ensure you have the latest data.
62
+ */
63
+ async update(): Promise<void> {
64
+ await this.collection.update();
65
+ }
66
+
67
+ // Read actions
68
+
69
+ async first(): Promise<Path<TKey, TEntry>> {
70
+ return await this.btree.first();
71
+ }
72
+
73
+ async last(): Promise<Path<TKey, TEntry>> {
74
+ return await this.btree.last();
75
+ }
76
+
77
+ async find(key: TKey): Promise<Path<TKey, TEntry>> {
78
+ return await this.btree.find(key);
79
+ }
80
+
81
+ async get(key: TKey): Promise<TEntry | undefined> {
82
+ return await this.btree.get(key);
83
+ }
84
+
85
+ at(path: Path<TKey, TEntry>): TEntry | undefined {
86
+ return this.btree.at(path);
87
+ }
88
+
89
+ range(range: KeyRange<TKey>): AsyncIterableIterator<Path<TKey, TEntry>> {
90
+ return this.btree.range(range);
91
+ }
92
+
93
+ ascending(path: Path<TKey, TEntry>): AsyncIterableIterator<Path<TKey, TEntry>> {
94
+ return this.btree.ascending(path);
95
+ }
96
+
97
+ descending(path: Path<TKey, TEntry>): AsyncIterableIterator<Path<TKey, TEntry>> {
98
+ return this.btree.descending(path);
99
+ }
100
+
101
+ async getCount(from?: { path: Path<TKey, TEntry>, ascending?: boolean }): Promise<number> {
102
+ return await this.btree.getCount(from);
103
+ }
104
+
105
+ async next(path: Path<TKey, TEntry>): Promise<Path<TKey, TEntry>> {
106
+ return await this.btree.next(path);
107
+ }
108
+
109
+ async moveNext(path: Path<TKey, TEntry>): Promise<void> {
110
+ await this.btree.moveNext(path);
111
+ }
112
+
113
+ async prior(path: Path<TKey, TEntry>): Promise<Path<TKey, TEntry>> {
114
+ return await this.btree.prior(path);
115
+ }
116
+
117
+ async movePrior(path: Path<TKey, TEntry>): Promise<void> {
118
+ await this.btree.movePrior(path);
119
+ }
120
+
121
+ isValid(path: Path<TKey, TEntry>): boolean {
122
+ return this.btree.isValid(path);
123
+ }
124
+ }
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ export * from "./blocks/index.js";
2
+ export * from "./btree/index.js";
3
+ export * from "./chain/index.js";
4
+ export * from "./cluster/index.js";
5
+ export * from "./collection/index.js";
6
+ export * from "./collections/index.js";
7
+ export * from "./log/index.js";
8
+ export * from "./network/index.js";
9
+ export * from "./transaction/index.js";
10
+ export * from "./transactor/index.js";
11
+ export * from "./transform/index.js";
12
+ export * from "./utility/groupby.js";
13
+ export * from "./utility/latches.js";
14
+ export * from "./utility/nameof.js";
15
+ export * from "./utility/ensured.js";
16
+ export * from "./utility/pending.js";
17
+ export * from "./utility/block-id-to-bytes.js";
@@ -0,0 +1,2 @@
1
+ export * from "./log.js";
2
+ export * from "./struct.js";
package/src/log/log.ts ADDED
@@ -0,0 +1,155 @@
1
+ import { sha256 } from 'multiformats/hashes/sha2'
2
+ import { Chain, entryAt, nameof } from "../index.js";
3
+ import type { IBlock, BlockId, ActionId, CollectionId, ChainPath, ActionRev, ActionContext, ChainInitOptions, BlockStore } from "../index.js";
4
+ import type { ChainHeaderBlockType, ChainDataNode } from '../chain/chain-nodes.js';
5
+ import type { LogEntry, ActionEntry } from "./index.js";
6
+ import { LogDataBlockType, LogHeaderBlockType } from "./index.js";
7
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
8
+ import type { GetFromResult } from './struct.js';
9
+
10
+ export type LogBlock<TAction> = ChainDataNode<LogEntry<TAction>>
11
+ & {
12
+ /** Base64url encoded Sha256 hash of the next block - present on every block except the head */
13
+ priorHash?: string,
14
+ };
15
+
16
+ export const priorHash$ = nameof<LogBlock<any>>("priorHash");
17
+
18
+ export class Log<TAction> {
19
+ protected constructor(
20
+ private readonly chain: Chain<LogEntry<TAction>>,
21
+ ) {
22
+ }
23
+
24
+ get id() {
25
+ return this.chain.id;
26
+ }
27
+
28
+ /** Opens a presumably existing log. */
29
+ static async open<TAction>(store: BlockStore<IBlock>, id: BlockId): Promise<Log<TAction> | undefined> {
30
+ const chain = await Chain.open<LogEntry<TAction>>(store, id, Log.getChainOptions(store));
31
+ return chain ? new Log<TAction>(chain) : undefined;
32
+ }
33
+
34
+ /** Creates a new log. */
35
+ static async create<TAction>(store: BlockStore<IBlock>, newId?: BlockId) {
36
+ return new Log<TAction>(await Chain.create<LogEntry<TAction>>(store, { ...Log.getChainOptions(store), newId }));
37
+ }
38
+
39
+ /** Adds a new entry to the log. */
40
+ async addActions(actions: TAction[], actionId: ActionId, rev: number, getBlockIds: () => BlockId[], collectionIds: CollectionId[] = [], timestamp: number = Date.now()) {
41
+ const entry = { timestamp, rev, action: { actionId, actions, blockIds: [], collectionIds } } as LogEntry<TAction>;
42
+ const tailPath = await this.chain.add(entry);
43
+ const entryWithBlockIds = { ...entry, action: { ...entry.action!, blockIds: getBlockIds() } };
44
+ this.chain.updateAt(tailPath, entryWithBlockIds);
45
+ return { entry: entryWithBlockIds, tailPath };
46
+ }
47
+
48
+ /** Adds a checkpoint to the log. */
49
+ async addCheckpoint(pendings: ActionRev[], rev: number, timestamp: number = Date.now()) {
50
+ const entry = { timestamp, rev, checkpoint: { pendings } };
51
+ const tailPath = await this.chain.add(entry);
52
+ return { entry, tailPath };
53
+ }
54
+
55
+ /** Gets the action context of the log. */
56
+ async getActionContext(): Promise<ActionContext | undefined> {
57
+ const tailPath = await this.chain.getTail();
58
+ if (!tailPath) {
59
+ return undefined;
60
+ }
61
+ const checkpoint = await this.findCheckpoint(tailPath);
62
+ return {
63
+ committed:
64
+ checkpoint
65
+
66
+ ? [...checkpoint.pendings, ...await this.pendingFrom(checkpoint.path)]
67
+ : [],
68
+ rev: checkpoint?.rev ?? 0,
69
+ };
70
+ }
71
+
72
+ /** Gets the actions from startRev (exclusive), to latest in the log. */
73
+ async getFrom(startRev: number | undefined): Promise<GetFromResult<TAction>> {
74
+ const entries: ActionEntry<TAction>[] = [];
75
+ const pendings: ActionRev[] = [];
76
+ let rev: number | undefined;
77
+ let checkpointPath: ChainPath<LogEntry<TAction>> | undefined;
78
+ // Step through collecting both pending and entries until a checkpoint is found
79
+ for await (const path of this.chain.select(undefined, false)) {
80
+ const entry = entryAt<LogEntry<TAction>>(path)!;
81
+ rev = rev ?? entry.rev;
82
+ if (entry.checkpoint) {
83
+ checkpointPath = path;
84
+ pendings.unshift(...entry.checkpoint.pendings);
85
+ break;
86
+ }
87
+ pendings.unshift({ actionId: entry.action!.actionId, rev: entry.rev });
88
+ if (startRev !== undefined && entry.rev > startRev) {
89
+ entries.unshift(entry.action!);
90
+ } // Can't stop at rev, because we need to collect all pending actions for the context
91
+ }
92
+ // Continue stepping past the checkpoint until the given rev is reached
93
+ if (checkpointPath) {
94
+ for await (const path of this.chain.select(checkpointPath, false)) {
95
+ const entry = entryAt<LogEntry<TAction>>(path)!;
96
+ if (startRev !== undefined && entry.rev > startRev) {
97
+ if (entry.action) {
98
+ entries.unshift(entry.action!);
99
+ }
100
+ } else {
101
+ break;
102
+ }
103
+ }
104
+ }
105
+ return { context: rev ? { committed: pendings, rev } : undefined, entries };
106
+ }
107
+
108
+ /** Enumerates log entries from the given starting path or end if undefined, in forward (from tail to head) or reverse (from head to tail) order. */
109
+ async *select(starting?: ChainPath<LogEntry<TAction>>, forward = true) {
110
+ for await (const path of this.chain.select(starting, forward)) {
111
+ yield entryAt<LogEntry<TAction>>(path)!;
112
+ }
113
+ }
114
+
115
+ /** Returns the set of pending transactions in the most recent checkpoint, at or preceding the given path. */
116
+ private async findCheckpoint(starting: ChainPath<LogEntry<TAction>>) {
117
+ let lastPath: ChainPath<LogEntry<TAction>> | undefined;
118
+ let rev: number | undefined;
119
+ for await (const path of this.chain.select(starting, false)) {
120
+ const entry = entryAt<LogEntry<TAction>>(path)!;
121
+ rev = rev ?? entry.rev;
122
+ if (entry.checkpoint) {
123
+ return { path, pendings: entry.checkpoint.pendings, rev };
124
+ }
125
+ lastPath = path;
126
+ }
127
+ return lastPath ? { path: lastPath, pendings: [], rev } : undefined;
128
+ }
129
+
130
+ /** Returns the set of pending actions following, the given checkpoint path. */
131
+ private async pendingFrom(starting: ChainPath<LogEntry<TAction>>) {
132
+ const pendings: ActionRev[] = [];
133
+ for await (const actionPath of this.chain.select(starting)) {
134
+ const entry = entryAt<LogEntry<TAction>>(actionPath);
135
+ if (entry?.action) {
136
+ pendings.push({ actionId: entry.action.actionId, rev: entry.rev });
137
+ }
138
+ }
139
+ return pendings;
140
+ }
141
+
142
+ private static getChainOptions<TAction>(store: BlockStore<IBlock>) {
143
+ return {
144
+ createDataBlock: () => ({ header: store.createBlockHeader(LogDataBlockType) }),
145
+ createHeaderBlock: (id?: BlockId) => ({ header: store.createBlockHeader(LogHeaderBlockType, id) }),
146
+ newBlock: async (newTail: LogBlock<TAction>, oldTail: LogBlock<TAction> | undefined) => {
147
+ if (oldTail) {
148
+ const hash = await sha256.digest(new TextEncoder().encode(JSON.stringify(oldTail)));
149
+ newTail.priorHash = uint8ArrayToString(hash.digest, 'base64url');
150
+ }
151
+ },
152
+ } as ChainInitOptions<LogEntry<TAction>>;
153
+
154
+ }
155
+ }