@fluid-experimental/tree 0.58.2002 → 0.58.3000-61081

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 (815) hide show
  1. package/README.md +9 -9
  2. package/dist/ChangeCompression.d.ts +39 -0
  3. package/dist/ChangeCompression.d.ts.map +1 -0
  4. package/dist/ChangeCompression.js +117 -0
  5. package/dist/ChangeCompression.js.map +1 -0
  6. package/{lib/default-edits/PersistedTypes.d.ts → dist/ChangeTypes.d.ts} +58 -100
  7. package/dist/ChangeTypes.d.ts.map +1 -0
  8. package/dist/{default-edits/PersistedTypes.js → ChangeTypes.js} +21 -76
  9. package/dist/ChangeTypes.js.map +1 -0
  10. package/dist/Checkout.d.ts +39 -27
  11. package/dist/Checkout.d.ts.map +1 -1
  12. package/dist/Checkout.js +59 -31
  13. package/dist/Checkout.js.map +1 -1
  14. package/dist/Common.d.ts +175 -38
  15. package/dist/Common.d.ts.map +1 -1
  16. package/dist/Common.js +240 -103
  17. package/dist/Common.js.map +1 -1
  18. package/dist/EagerCheckout.d.ts +24 -0
  19. package/dist/EagerCheckout.d.ts.map +1 -0
  20. package/dist/{BasicCheckout.js → EagerCheckout.js} +9 -6
  21. package/dist/EagerCheckout.js.map +1 -0
  22. package/dist/EditLog.d.ts +77 -63
  23. package/dist/EditLog.d.ts.map +1 -1
  24. package/dist/EditLog.js +85 -48
  25. package/dist/EditLog.js.map +1 -1
  26. package/dist/EditUtilities.d.ts +168 -0
  27. package/dist/EditUtilities.d.ts.map +1 -0
  28. package/dist/EditUtilities.js +373 -0
  29. package/dist/EditUtilities.js.map +1 -0
  30. package/dist/EventTypes.d.ts +73 -0
  31. package/dist/EventTypes.d.ts.map +1 -0
  32. package/dist/EventTypes.js +78 -0
  33. package/dist/EventTypes.js.map +1 -0
  34. package/dist/Forest.d.ts +29 -7
  35. package/dist/Forest.d.ts.map +1 -1
  36. package/dist/Forest.js +60 -36
  37. package/dist/Forest.js.map +1 -1
  38. package/dist/HistoryEditFactory.d.ts +20 -0
  39. package/dist/HistoryEditFactory.d.ts.map +1 -0
  40. package/dist/HistoryEditFactory.js +226 -0
  41. package/dist/HistoryEditFactory.js.map +1 -0
  42. package/dist/IdConversion.d.ts +12 -0
  43. package/dist/IdConversion.d.ts.map +1 -0
  44. package/dist/IdConversion.js +98 -0
  45. package/dist/IdConversion.js.map +1 -0
  46. package/dist/Identifiers.d.ts +89 -2
  47. package/dist/Identifiers.d.ts.map +1 -1
  48. package/dist/Identifiers.js +10 -0
  49. package/dist/Identifiers.js.map +1 -1
  50. package/dist/InitialTree.d.ts +2 -2
  51. package/dist/InitialTree.d.ts.map +1 -1
  52. package/dist/InitialTree.js +2 -1
  53. package/dist/InitialTree.js.map +1 -1
  54. package/dist/LazyCheckout.d.ts +28 -0
  55. package/dist/LazyCheckout.d.ts.map +1 -0
  56. package/dist/LazyCheckout.js +44 -0
  57. package/dist/LazyCheckout.js.map +1 -0
  58. package/dist/LogViewer.d.ts +130 -85
  59. package/dist/LogViewer.d.ts.map +1 -1
  60. package/dist/LogViewer.js +110 -85
  61. package/dist/LogViewer.js.map +1 -1
  62. package/dist/MergeHealth.d.ts +221 -0
  63. package/dist/MergeHealth.d.ts.map +1 -0
  64. package/dist/MergeHealth.js +263 -0
  65. package/dist/MergeHealth.js.map +1 -0
  66. package/dist/NodeIdUtilities.d.ts +90 -0
  67. package/dist/NodeIdUtilities.d.ts.map +1 -0
  68. package/dist/NodeIdUtilities.js +60 -0
  69. package/dist/NodeIdUtilities.js.map +1 -0
  70. package/dist/PayloadUtilities.d.ts +42 -0
  71. package/dist/PayloadUtilities.d.ts.map +1 -0
  72. package/dist/PayloadUtilities.js +114 -0
  73. package/dist/PayloadUtilities.js.map +1 -0
  74. package/dist/ReconciliationPath.d.ts +18 -13
  75. package/dist/ReconciliationPath.d.ts.map +1 -1
  76. package/dist/ReconciliationPath.js.map +1 -1
  77. package/dist/RevisionValueCache.d.ts +11 -2
  78. package/dist/RevisionValueCache.d.ts.map +1 -1
  79. package/dist/RevisionValueCache.js +2 -3
  80. package/dist/RevisionValueCache.js.map +1 -1
  81. package/dist/RevisionView.d.ts +83 -0
  82. package/dist/RevisionView.d.ts.map +1 -0
  83. package/dist/RevisionView.js +182 -0
  84. package/dist/RevisionView.js.map +1 -0
  85. package/dist/SerializationUtilities.d.ts +36 -0
  86. package/dist/SerializationUtilities.d.ts.map +1 -0
  87. package/dist/SerializationUtilities.js +102 -0
  88. package/dist/SerializationUtilities.js.map +1 -0
  89. package/dist/SharedTree.d.ts +400 -0
  90. package/dist/SharedTree.d.ts.map +1 -0
  91. package/dist/SharedTree.js +1064 -0
  92. package/dist/SharedTree.js.map +1 -0
  93. package/dist/SharedTreeEncoder.d.ts +102 -0
  94. package/dist/SharedTreeEncoder.d.ts.map +1 -0
  95. package/dist/SharedTreeEncoder.js +313 -0
  96. package/dist/SharedTreeEncoder.js.map +1 -0
  97. package/dist/StringInterner.d.ts +46 -0
  98. package/dist/StringInterner.d.ts.map +1 -0
  99. package/dist/StringInterner.js +61 -0
  100. package/dist/StringInterner.js.map +1 -0
  101. package/dist/Summary.d.ts +40 -0
  102. package/dist/Summary.d.ts.map +1 -0
  103. package/dist/Summary.js +23 -0
  104. package/dist/Summary.js.map +1 -0
  105. package/dist/SummaryBackCompatibility.d.ts +22 -22
  106. package/dist/SummaryBackCompatibility.d.ts.map +1 -1
  107. package/dist/SummaryBackCompatibility.js +30 -33
  108. package/dist/SummaryBackCompatibility.js.map +1 -1
  109. package/dist/SummaryTestUtilities.d.ts +31 -0
  110. package/dist/SummaryTestUtilities.d.ts.map +1 -0
  111. package/dist/SummaryTestUtilities.js +37 -0
  112. package/dist/SummaryTestUtilities.js.map +1 -0
  113. package/dist/Transaction.d.ts +53 -0
  114. package/dist/Transaction.d.ts.map +1 -0
  115. package/dist/Transaction.js +76 -0
  116. package/dist/Transaction.js.map +1 -0
  117. package/dist/TransactionInternal.d.ts +543 -0
  118. package/dist/TransactionInternal.d.ts.map +1 -0
  119. package/dist/TransactionInternal.js +622 -0
  120. package/dist/TransactionInternal.js.map +1 -0
  121. package/dist/TreeCompressor.d.ts +37 -0
  122. package/dist/TreeCompressor.d.ts.map +1 -0
  123. package/dist/TreeCompressor.js +132 -0
  124. package/dist/TreeCompressor.js.map +1 -0
  125. package/dist/TreeNodeHandle.d.ts +12 -18
  126. package/dist/TreeNodeHandle.d.ts.map +1 -1
  127. package/dist/TreeNodeHandle.js +13 -23
  128. package/dist/TreeNodeHandle.js.map +1 -1
  129. package/dist/TreeView.d.ts +166 -0
  130. package/dist/TreeView.d.ts.map +1 -0
  131. package/dist/TreeView.js +217 -0
  132. package/dist/TreeView.js.map +1 -0
  133. package/dist/TreeViewUtilities.d.ts +21 -0
  134. package/dist/TreeViewUtilities.d.ts.map +1 -0
  135. package/dist/TreeViewUtilities.js +77 -0
  136. package/dist/TreeViewUtilities.js.map +1 -0
  137. package/dist/{default-edits/UndoRedoHandler.d.ts → UndoRedoHandler.d.ts} +2 -2
  138. package/dist/UndoRedoHandler.d.ts.map +1 -0
  139. package/dist/{default-edits/UndoRedoHandler.js → UndoRedoHandler.js} +5 -9
  140. package/dist/UndoRedoHandler.js.map +1 -0
  141. package/dist/id-compressor/AppendOnlySortedMap.d.ts +127 -0
  142. package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -0
  143. package/dist/id-compressor/AppendOnlySortedMap.js +283 -0
  144. package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -0
  145. package/dist/id-compressor/IdCompressor.d.ts +389 -0
  146. package/dist/id-compressor/IdCompressor.d.ts.map +1 -0
  147. package/dist/id-compressor/IdCompressor.js +1353 -0
  148. package/dist/id-compressor/IdCompressor.js.map +1 -0
  149. package/dist/id-compressor/IdRange.d.ts +11 -0
  150. package/dist/id-compressor/IdRange.d.ts.map +1 -0
  151. package/dist/id-compressor/IdRange.js +29 -0
  152. package/dist/id-compressor/IdRange.js.map +1 -0
  153. package/dist/id-compressor/NumericUuid.d.ts +63 -0
  154. package/dist/id-compressor/NumericUuid.d.ts.map +1 -0
  155. package/dist/id-compressor/NumericUuid.js +377 -0
  156. package/dist/id-compressor/NumericUuid.js.map +1 -0
  157. package/dist/id-compressor/index.d.ts +12 -0
  158. package/dist/id-compressor/index.d.ts.map +1 -0
  159. package/dist/id-compressor/index.js +26 -0
  160. package/dist/id-compressor/index.js.map +1 -0
  161. package/dist/id-compressor/persisted-types/0.0.1.d.ts +156 -0
  162. package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -0
  163. package/dist/id-compressor/persisted-types/0.0.1.js +7 -0
  164. package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -0
  165. package/dist/id-compressor/persisted-types/index.d.ts +6 -0
  166. package/dist/id-compressor/persisted-types/index.d.ts.map +1 -0
  167. package/dist/id-compressor/persisted-types/index.js +18 -0
  168. package/dist/id-compressor/persisted-types/index.js.map +1 -0
  169. package/dist/index.d.ts +29 -9
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +50 -35
  172. package/dist/index.js.map +1 -1
  173. package/dist/persisted-types/0.0.2.d.ts +385 -0
  174. package/dist/persisted-types/0.0.2.d.ts.map +1 -0
  175. package/dist/persisted-types/0.0.2.js +113 -0
  176. package/dist/persisted-types/0.0.2.js.map +1 -0
  177. package/dist/persisted-types/0.1.1.d.ts +314 -0
  178. package/dist/persisted-types/0.1.1.d.ts.map +1 -0
  179. package/dist/persisted-types/0.1.1.js +153 -0
  180. package/dist/persisted-types/0.1.1.js.map +1 -0
  181. package/dist/persisted-types/index.d.ts +7 -0
  182. package/dist/persisted-types/index.d.ts.map +1 -0
  183. package/dist/persisted-types/index.js +20 -0
  184. package/dist/persisted-types/index.js.map +1 -0
  185. package/docs/0-1-1-Compression.md +228 -0
  186. package/docs/Breaking-Change-Migration.md +52 -0
  187. package/docs/Compression.md +2 -2
  188. package/docs/Telemetry.md +43 -0
  189. package/lib/ChangeCompression.d.ts +39 -0
  190. package/lib/ChangeCompression.d.ts.map +1 -0
  191. package/lib/ChangeCompression.js +111 -0
  192. package/lib/ChangeCompression.js.map +1 -0
  193. package/{dist/default-edits/PersistedTypes.d.ts → lib/ChangeTypes.d.ts} +58 -100
  194. package/lib/ChangeTypes.d.ts.map +1 -0
  195. package/lib/{default-edits/PersistedTypes.js → ChangeTypes.js} +15 -68
  196. package/lib/ChangeTypes.js.map +1 -0
  197. package/lib/Checkout.d.ts +39 -27
  198. package/lib/Checkout.d.ts.map +1 -1
  199. package/lib/Checkout.js +51 -23
  200. package/lib/Checkout.js.map +1 -1
  201. package/lib/Common.d.ts +175 -38
  202. package/lib/Common.d.ts.map +1 -1
  203. package/lib/Common.js +226 -101
  204. package/lib/Common.js.map +1 -1
  205. package/lib/EagerCheckout.d.ts +24 -0
  206. package/lib/EagerCheckout.d.ts.map +1 -0
  207. package/lib/{BasicCheckout.js → EagerCheckout.js} +7 -4
  208. package/lib/EagerCheckout.js.map +1 -0
  209. package/lib/EditLog.d.ts +77 -63
  210. package/lib/EditLog.d.ts.map +1 -1
  211. package/lib/EditLog.js +83 -47
  212. package/lib/EditLog.js.map +1 -1
  213. package/lib/EditUtilities.d.ts +168 -0
  214. package/lib/EditUtilities.d.ts.map +1 -0
  215. package/lib/EditUtilities.js +353 -0
  216. package/lib/EditUtilities.js.map +1 -0
  217. package/lib/EventTypes.d.ts +73 -0
  218. package/lib/EventTypes.d.ts.map +1 -0
  219. package/lib/EventTypes.js +75 -0
  220. package/lib/EventTypes.js.map +1 -0
  221. package/lib/Forest.d.ts +29 -7
  222. package/lib/Forest.d.ts.map +1 -1
  223. package/lib/Forest.js +58 -35
  224. package/lib/Forest.js.map +1 -1
  225. package/lib/HistoryEditFactory.d.ts +20 -0
  226. package/lib/HistoryEditFactory.d.ts.map +1 -0
  227. package/lib/{default-edits/HistoryEditFactory.js → HistoryEditFactory.js} +78 -39
  228. package/lib/HistoryEditFactory.js.map +1 -0
  229. package/lib/IdConversion.d.ts +12 -0
  230. package/lib/IdConversion.d.ts.map +1 -0
  231. package/lib/IdConversion.js +91 -0
  232. package/lib/IdConversion.js.map +1 -0
  233. package/lib/Identifiers.d.ts +89 -2
  234. package/lib/Identifiers.d.ts.map +1 -1
  235. package/lib/Identifiers.js +8 -1
  236. package/lib/Identifiers.js.map +1 -1
  237. package/lib/InitialTree.d.ts +2 -2
  238. package/lib/InitialTree.d.ts.map +1 -1
  239. package/lib/InitialTree.js +2 -1
  240. package/lib/InitialTree.js.map +1 -1
  241. package/lib/LazyCheckout.d.ts +28 -0
  242. package/lib/LazyCheckout.d.ts.map +1 -0
  243. package/lib/LazyCheckout.js +40 -0
  244. package/lib/LazyCheckout.js.map +1 -0
  245. package/lib/LogViewer.d.ts +130 -85
  246. package/lib/LogViewer.d.ts.map +1 -1
  247. package/lib/LogViewer.js +102 -77
  248. package/lib/LogViewer.js.map +1 -1
  249. package/lib/MergeHealth.d.ts +221 -0
  250. package/lib/MergeHealth.d.ts.map +1 -0
  251. package/lib/MergeHealth.js +258 -0
  252. package/lib/MergeHealth.js.map +1 -0
  253. package/lib/NodeIdUtilities.d.ts +90 -0
  254. package/lib/NodeIdUtilities.d.ts.map +1 -0
  255. package/lib/NodeIdUtilities.js +53 -0
  256. package/lib/NodeIdUtilities.js.map +1 -0
  257. package/lib/PayloadUtilities.d.ts +42 -0
  258. package/lib/PayloadUtilities.d.ts.map +1 -0
  259. package/lib/PayloadUtilities.js +110 -0
  260. package/lib/PayloadUtilities.js.map +1 -0
  261. package/lib/ReconciliationPath.d.ts +18 -13
  262. package/lib/ReconciliationPath.d.ts.map +1 -1
  263. package/lib/ReconciliationPath.js.map +1 -1
  264. package/lib/RevisionValueCache.d.ts +11 -2
  265. package/lib/RevisionValueCache.d.ts.map +1 -1
  266. package/lib/RevisionValueCache.js +2 -3
  267. package/lib/RevisionValueCache.js.map +1 -1
  268. package/lib/RevisionView.d.ts +83 -0
  269. package/lib/RevisionView.d.ts.map +1 -0
  270. package/lib/RevisionView.js +175 -0
  271. package/lib/RevisionView.js.map +1 -0
  272. package/lib/SerializationUtilities.d.ts +36 -0
  273. package/lib/SerializationUtilities.d.ts.map +1 -0
  274. package/lib/SerializationUtilities.js +95 -0
  275. package/lib/SerializationUtilities.js.map +1 -0
  276. package/lib/SharedTree.d.ts +400 -0
  277. package/lib/SharedTree.d.ts.map +1 -0
  278. package/lib/SharedTree.js +1059 -0
  279. package/lib/SharedTree.js.map +1 -0
  280. package/lib/SharedTreeEncoder.d.ts +102 -0
  281. package/lib/SharedTreeEncoder.d.ts.map +1 -0
  282. package/lib/SharedTreeEncoder.js +308 -0
  283. package/lib/SharedTreeEncoder.js.map +1 -0
  284. package/lib/StringInterner.d.ts +46 -0
  285. package/lib/StringInterner.d.ts.map +1 -0
  286. package/lib/StringInterner.js +57 -0
  287. package/lib/StringInterner.js.map +1 -0
  288. package/lib/Summary.d.ts +40 -0
  289. package/lib/Summary.d.ts.map +1 -0
  290. package/lib/Summary.js +19 -0
  291. package/lib/Summary.js.map +1 -0
  292. package/lib/SummaryBackCompatibility.d.ts +22 -22
  293. package/lib/SummaryBackCompatibility.d.ts.map +1 -1
  294. package/lib/SummaryBackCompatibility.js +29 -32
  295. package/lib/SummaryBackCompatibility.js.map +1 -1
  296. package/lib/SummaryTestUtilities.d.ts +31 -0
  297. package/lib/SummaryTestUtilities.d.ts.map +1 -0
  298. package/lib/SummaryTestUtilities.js +32 -0
  299. package/lib/SummaryTestUtilities.js.map +1 -0
  300. package/lib/Transaction.d.ts +53 -0
  301. package/lib/Transaction.d.ts.map +1 -0
  302. package/lib/Transaction.js +72 -0
  303. package/lib/Transaction.js.map +1 -0
  304. package/lib/TransactionInternal.d.ts +543 -0
  305. package/lib/TransactionInternal.d.ts.map +1 -0
  306. package/lib/TransactionInternal.js +618 -0
  307. package/lib/TransactionInternal.js.map +1 -0
  308. package/lib/TreeCompressor.d.ts +37 -0
  309. package/lib/TreeCompressor.d.ts.map +1 -0
  310. package/lib/TreeCompressor.js +128 -0
  311. package/lib/TreeCompressor.js.map +1 -0
  312. package/lib/TreeNodeHandle.d.ts +12 -18
  313. package/lib/TreeNodeHandle.d.ts.map +1 -1
  314. package/lib/TreeNodeHandle.js +14 -24
  315. package/lib/TreeNodeHandle.js.map +1 -1
  316. package/lib/TreeView.d.ts +166 -0
  317. package/lib/TreeView.d.ts.map +1 -0
  318. package/lib/TreeView.js +213 -0
  319. package/lib/TreeView.js.map +1 -0
  320. package/lib/TreeViewUtilities.d.ts +21 -0
  321. package/lib/TreeViewUtilities.d.ts.map +1 -0
  322. package/lib/TreeViewUtilities.js +71 -0
  323. package/lib/TreeViewUtilities.js.map +1 -0
  324. package/lib/{default-edits/UndoRedoHandler.d.ts → UndoRedoHandler.d.ts} +2 -2
  325. package/lib/UndoRedoHandler.d.ts.map +1 -0
  326. package/lib/{default-edits/UndoRedoHandler.js → UndoRedoHandler.js} +3 -7
  327. package/lib/UndoRedoHandler.js.map +1 -0
  328. package/lib/id-compressor/AppendOnlySortedMap.d.ts +127 -0
  329. package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -0
  330. package/lib/id-compressor/AppendOnlySortedMap.js +278 -0
  331. package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -0
  332. package/lib/id-compressor/IdCompressor.d.ts +389 -0
  333. package/lib/id-compressor/IdCompressor.d.ts.map +1 -0
  334. package/lib/id-compressor/IdCompressor.js +1343 -0
  335. package/lib/id-compressor/IdCompressor.js.map +1 -0
  336. package/lib/id-compressor/IdRange.d.ts +11 -0
  337. package/lib/id-compressor/IdRange.d.ts.map +1 -0
  338. package/lib/id-compressor/IdRange.js +25 -0
  339. package/lib/id-compressor/IdRange.js.map +1 -0
  340. package/lib/id-compressor/NumericUuid.d.ts +63 -0
  341. package/lib/id-compressor/NumericUuid.d.ts.map +1 -0
  342. package/lib/id-compressor/NumericUuid.js +365 -0
  343. package/lib/id-compressor/NumericUuid.js.map +1 -0
  344. package/lib/id-compressor/index.d.ts +12 -0
  345. package/lib/id-compressor/index.d.ts.map +1 -0
  346. package/lib/id-compressor/index.js +12 -0
  347. package/lib/id-compressor/index.js.map +1 -0
  348. package/lib/id-compressor/persisted-types/0.0.1.d.ts +156 -0
  349. package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -0
  350. package/lib/{test/Snapshot.tests.d.ts → id-compressor/persisted-types/0.0.1.js} +1 -1
  351. package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -0
  352. package/lib/id-compressor/persisted-types/index.d.ts +6 -0
  353. package/lib/id-compressor/persisted-types/index.d.ts.map +1 -0
  354. package/lib/id-compressor/persisted-types/index.js +6 -0
  355. package/lib/id-compressor/persisted-types/index.js.map +1 -0
  356. package/lib/index.d.ts +29 -9
  357. package/lib/index.d.ts.map +1 -1
  358. package/lib/index.js +23 -6
  359. package/lib/index.js.map +1 -1
  360. package/lib/persisted-types/0.0.2.d.ts +385 -0
  361. package/lib/persisted-types/0.0.2.d.ts.map +1 -0
  362. package/lib/persisted-types/0.0.2.js +110 -0
  363. package/lib/persisted-types/0.0.2.js.map +1 -0
  364. package/lib/persisted-types/0.1.1.d.ts +314 -0
  365. package/lib/persisted-types/0.1.1.d.ts.map +1 -0
  366. package/lib/persisted-types/0.1.1.js +150 -0
  367. package/lib/persisted-types/0.1.1.js.map +1 -0
  368. package/lib/persisted-types/index.d.ts +7 -0
  369. package/lib/persisted-types/index.d.ts.map +1 -0
  370. package/lib/persisted-types/index.js +8 -0
  371. package/lib/persisted-types/index.js.map +1 -0
  372. package/lib/test/AppendOnlySortedMap.tests.d.ts +6 -0
  373. package/lib/test/AppendOnlySortedMap.tests.d.ts.map +1 -0
  374. package/lib/test/AppendOnlySortedMap.tests.js +169 -0
  375. package/lib/test/AppendOnlySortedMap.tests.js.map +1 -0
  376. package/lib/test/{SnapshotUtilities.tests.d.ts → ChangeCompression.tests.d.ts} +1 -1
  377. package/lib/test/ChangeCompression.tests.d.ts.map +1 -0
  378. package/lib/test/ChangeCompression.tests.js +145 -0
  379. package/lib/test/ChangeCompression.tests.js.map +1 -0
  380. package/lib/test/Checkout.tests.d.ts +2 -3
  381. package/lib/test/Checkout.tests.d.ts.map +1 -1
  382. package/lib/test/Checkout.tests.js +126 -69
  383. package/lib/test/Checkout.tests.js.map +1 -1
  384. package/lib/test/Common.tests.js +60 -2
  385. package/lib/test/Common.tests.js.map +1 -1
  386. package/lib/test/{BasicCheckout.tests.d.ts → EagerCheckout.tests.d.ts} +1 -1
  387. package/lib/test/EagerCheckout.tests.d.ts.map +1 -0
  388. package/lib/test/EagerCheckout.tests.js +20 -0
  389. package/lib/test/EagerCheckout.tests.js.map +1 -0
  390. package/lib/test/Edit.tests.js +22 -14
  391. package/lib/test/Edit.tests.js.map +1 -1
  392. package/lib/test/{Anchors.glassBox.tests.d.ts → EditLog.perf.tests.d.ts} +1 -1
  393. package/lib/test/EditLog.perf.tests.d.ts.map +1 -0
  394. package/lib/test/EditLog.perf.tests.js +30 -0
  395. package/lib/test/EditLog.perf.tests.js.map +1 -0
  396. package/lib/test/EditLog.tests.js +10 -6
  397. package/lib/test/EditLog.tests.js.map +1 -1
  398. package/lib/test/EditUtilities.tests.d.ts +6 -0
  399. package/lib/test/EditUtilities.tests.d.ts.map +1 -0
  400. package/lib/test/EditUtilities.tests.js +503 -0
  401. package/lib/test/EditUtilities.tests.js.map +1 -0
  402. package/lib/test/Forest.perf.tests.d.ts +6 -0
  403. package/lib/test/Forest.perf.tests.d.ts.map +1 -0
  404. package/lib/test/Forest.perf.tests.js +133 -0
  405. package/lib/test/Forest.perf.tests.js.map +1 -0
  406. package/lib/test/Forest.tests.js +54 -27
  407. package/lib/test/Forest.tests.js.map +1 -1
  408. package/lib/test/GenericTransaction.tests.js +12 -3
  409. package/lib/test/GenericTransaction.tests.js.map +1 -1
  410. package/lib/test/HistoryEditFactory.tests.d.ts +6 -0
  411. package/lib/test/HistoryEditFactory.tests.d.ts.map +1 -0
  412. package/lib/test/HistoryEditFactory.tests.js +90 -0
  413. package/lib/test/HistoryEditFactory.tests.js.map +1 -0
  414. package/lib/test/IdCompressor.perf.tests.d.ts +6 -0
  415. package/lib/test/IdCompressor.perf.tests.d.ts.map +1 -0
  416. package/lib/test/IdCompressor.perf.tests.js +304 -0
  417. package/lib/test/IdCompressor.perf.tests.js.map +1 -0
  418. package/lib/test/IdCompressor.tests.d.ts +6 -0
  419. package/lib/test/IdCompressor.tests.d.ts.map +1 -0
  420. package/lib/test/IdCompressor.tests.js +1075 -0
  421. package/lib/test/IdCompressor.tests.js.map +1 -0
  422. package/lib/test/IdConversion.tests.d.ts +6 -0
  423. package/lib/test/IdConversion.tests.d.ts.map +1 -0
  424. package/lib/test/IdConversion.tests.js +36 -0
  425. package/lib/test/IdConversion.tests.js.map +1 -0
  426. package/lib/test/LazyCheckout.tests.d.ts +6 -0
  427. package/lib/test/LazyCheckout.tests.d.ts.map +1 -0
  428. package/lib/test/LazyCheckout.tests.js +22 -0
  429. package/lib/test/LazyCheckout.tests.js.map +1 -0
  430. package/lib/test/LogViewer.tests.js +269 -187
  431. package/lib/test/LogViewer.tests.js.map +1 -1
  432. package/lib/test/{SharedTreeWithAnchors.tests.d.ts → MergeHealthTelemetryHeartbeat.tests.d.ts} +1 -1
  433. package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts.map +1 -0
  434. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js +342 -0
  435. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js.map +1 -0
  436. package/lib/test/NumericUuid.perf.tests.d.ts +6 -0
  437. package/lib/test/NumericUuid.perf.tests.d.ts.map +1 -0
  438. package/lib/test/NumericUuid.perf.tests.js +68 -0
  439. package/lib/test/NumericUuid.perf.tests.js.map +1 -0
  440. package/lib/test/NumericUuid.tests.d.ts +6 -0
  441. package/lib/test/NumericUuid.tests.d.ts.map +1 -0
  442. package/lib/test/NumericUuid.tests.js +191 -0
  443. package/lib/test/NumericUuid.tests.js.map +1 -0
  444. package/lib/test/RevisionView.tests.d.ts +6 -0
  445. package/lib/test/RevisionView.tests.d.ts.map +1 -0
  446. package/lib/test/RevisionView.tests.js +133 -0
  447. package/lib/test/RevisionView.tests.js.map +1 -0
  448. package/lib/test/SharedTree.perf.tests.d.ts +6 -0
  449. package/lib/test/SharedTree.perf.tests.d.ts.map +1 -0
  450. package/lib/test/SharedTree.perf.tests.js +39 -0
  451. package/lib/test/SharedTree.perf.tests.js.map +1 -0
  452. package/lib/test/SharedTree.tests.js +15 -3
  453. package/lib/test/SharedTree.tests.js.map +1 -1
  454. package/lib/test/StringInterner.tests.d.ts +6 -0
  455. package/lib/test/StringInterner.tests.d.ts.map +1 -0
  456. package/lib/test/StringInterner.tests.js +71 -0
  457. package/lib/test/StringInterner.tests.js.map +1 -0
  458. package/lib/test/Summary.tests.d.ts +8 -0
  459. package/lib/test/Summary.tests.d.ts.map +1 -0
  460. package/lib/test/Summary.tests.js +407 -0
  461. package/lib/test/Summary.tests.js.map +1 -0
  462. package/lib/test/Transaction.tests.js +76 -330
  463. package/lib/test/Transaction.tests.js.map +1 -1
  464. package/lib/test/TransactionInternal.tests.d.ts +6 -0
  465. package/lib/test/TransactionInternal.tests.d.ts.map +1 -0
  466. package/lib/test/TransactionInternal.tests.js +568 -0
  467. package/lib/test/TransactionInternal.tests.js.map +1 -0
  468. package/lib/test/TreeCompression.tests.d.ts +6 -0
  469. package/lib/test/TreeCompression.tests.d.ts.map +1 -0
  470. package/lib/test/TreeCompression.tests.js +292 -0
  471. package/lib/test/TreeCompression.tests.js.map +1 -0
  472. package/lib/test/TreeView.tests.d.ts +6 -0
  473. package/lib/test/TreeView.tests.d.ts.map +1 -0
  474. package/lib/test/TreeView.tests.js +147 -0
  475. package/lib/test/TreeView.tests.js.map +1 -0
  476. package/lib/test/UndoRedoHandler.tests.js +2 -2
  477. package/lib/test/UndoRedoHandler.tests.js.map +1 -1
  478. package/lib/test/Virtualization.tests.js +147 -62
  479. package/lib/test/Virtualization.tests.js.map +1 -1
  480. package/lib/test/fuzz/Generators.d.ts +19 -0
  481. package/lib/test/fuzz/Generators.d.ts.map +1 -0
  482. package/lib/test/fuzz/Generators.js +420 -0
  483. package/lib/test/fuzz/Generators.js.map +1 -0
  484. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +20 -0
  485. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -0
  486. package/lib/test/fuzz/SharedTreeFuzzTests.js +200 -0
  487. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -0
  488. package/lib/test/fuzz/Types.d.ts +133 -0
  489. package/lib/test/fuzz/Types.d.ts.map +1 -0
  490. package/lib/test/{GenericTransactionWithAnchors.tests.d.ts → fuzz/Types.js} +2 -2
  491. package/lib/test/fuzz/Types.js.map +1 -0
  492. package/lib/test/utilities/IdCompressorTestUtilities.d.ts +180 -0
  493. package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -0
  494. package/lib/test/utilities/IdCompressorTestUtilities.js +528 -0
  495. package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -0
  496. package/lib/test/utilities/MockTransaction.d.ts +26 -7
  497. package/lib/test/utilities/MockTransaction.d.ts.map +1 -1
  498. package/lib/test/utilities/MockTransaction.js +40 -11
  499. package/lib/test/utilities/MockTransaction.js.map +1 -1
  500. package/lib/test/utilities/PendingLocalStateTests.d.ts +12 -0
  501. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -0
  502. package/lib/test/utilities/PendingLocalStateTests.js +105 -0
  503. package/lib/test/utilities/PendingLocalStateTests.js.map +1 -0
  504. package/lib/test/utilities/SharedTreeTests.d.ts +3 -4
  505. package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
  506. package/lib/test/utilities/SharedTreeTests.js +696 -439
  507. package/lib/test/utilities/SharedTreeTests.js.map +1 -1
  508. package/lib/test/utilities/SharedTreeVersioningTests.d.ts +11 -0
  509. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -0
  510. package/lib/test/utilities/SharedTreeVersioningTests.js +345 -0
  511. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -0
  512. package/lib/test/utilities/SummaryLoadPerfTests.d.ts +10 -0
  513. package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -0
  514. package/lib/test/utilities/SummaryLoadPerfTests.js +102 -0
  515. package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -0
  516. package/lib/test/utilities/SummarySizeTests.d.ts +11 -0
  517. package/lib/test/utilities/SummarySizeTests.d.ts.map +1 -0
  518. package/lib/test/utilities/SummarySizeTests.js +158 -0
  519. package/lib/test/utilities/SummarySizeTests.js.map +1 -0
  520. package/lib/test/utilities/TestCommon.d.ts +9 -0
  521. package/lib/test/utilities/TestCommon.d.ts.map +1 -0
  522. package/lib/test/utilities/TestCommon.js +13 -0
  523. package/lib/test/utilities/TestCommon.js.map +1 -0
  524. package/lib/test/utilities/TestNode.d.ts +140 -0
  525. package/lib/test/utilities/TestNode.d.ts.map +1 -0
  526. package/lib/test/utilities/TestNode.js +292 -0
  527. package/lib/test/utilities/TestNode.js.map +1 -0
  528. package/lib/test/utilities/TestUtilities.d.ts +84 -70
  529. package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
  530. package/lib/test/utilities/TestUtilities.js +218 -143
  531. package/lib/test/utilities/TestUtilities.js.map +1 -1
  532. package/lib/test/utilities/UndoRedoTests.d.ts +4 -5
  533. package/lib/test/utilities/UndoRedoTests.d.ts.map +1 -1
  534. package/lib/test/utilities/UndoRedoTests.js +138 -149
  535. package/lib/test/utilities/UndoRedoTests.js.map +1 -1
  536. package/package.json +19 -14
  537. package/src/ChangeCompression.ts +159 -0
  538. package/src/{default-edits/PersistedTypes.ts → ChangeTypes.ts} +62 -120
  539. package/src/Checkout.ts +81 -52
  540. package/src/Common.ts +317 -117
  541. package/src/EagerCheckout.ts +38 -0
  542. package/src/EditLog.ts +153 -100
  543. package/src/EditUtilities.ts +559 -0
  544. package/src/EventTypes.ts +74 -0
  545. package/src/Forest.ts +81 -73
  546. package/src/{default-edits/HistoryEditFactory.ts → HistoryEditFactory.ts} +103 -53
  547. package/src/IdConversion.ts +125 -0
  548. package/src/Identifiers.ts +101 -1
  549. package/src/InitialTree.ts +5 -4
  550. package/src/LazyCheckout.ts +51 -0
  551. package/src/LogViewer.ts +242 -166
  552. package/src/MergeHealth.ts +447 -0
  553. package/src/NodeIdUtilities.ts +141 -0
  554. package/src/PayloadUtilities.ts +124 -0
  555. package/src/ReconciliationPath.ts +18 -13
  556. package/src/RevisionValueCache.ts +14 -5
  557. package/src/RevisionView.ts +252 -0
  558. package/src/SerializationUtilities.ts +130 -0
  559. package/src/SharedTree.ts +1448 -0
  560. package/src/SharedTreeEncoder.ts +493 -0
  561. package/src/StringInterner.ts +72 -0
  562. package/src/Summary.ts +48 -0
  563. package/src/SummaryBackCompatibility.ts +47 -57
  564. package/src/SummaryTestUtilities.ts +54 -0
  565. package/src/Transaction.ts +94 -0
  566. package/src/TransactionInternal.ts +1088 -0
  567. package/src/TreeCompressor.ts +222 -0
  568. package/src/TreeNodeHandle.ts +19 -32
  569. package/src/TreeView.ts +321 -0
  570. package/src/TreeViewUtilities.ts +77 -0
  571. package/src/{default-edits/UndoRedoHandler.ts → UndoRedoHandler.ts} +8 -13
  572. package/src/id-compressor/AppendOnlySortedMap.ts +325 -0
  573. package/src/id-compressor/IdCompressor.md +3 -0
  574. package/src/id-compressor/IdCompressor.ts +1848 -0
  575. package/src/id-compressor/IdRange.ts +33 -0
  576. package/src/id-compressor/NumericUuid.ts +414 -0
  577. package/src/id-compressor/index.ts +13 -0
  578. package/src/id-compressor/persisted-types/0.0.1.ts +179 -0
  579. package/src/id-compressor/persisted-types/README.md +3 -0
  580. package/src/id-compressor/persisted-types/index.ts +6 -0
  581. package/src/index.ts +119 -59
  582. package/src/persisted-types/0.0.2.ts +442 -0
  583. package/src/persisted-types/0.1.1.ts +476 -0
  584. package/src/persisted-types/README.md +22 -0
  585. package/src/persisted-types/index.ts +9 -0
  586. package/.mocharc.js +0 -41
  587. package/api/tree.api.md +0 -729
  588. package/dist/BasicCheckout.d.ts +0 -23
  589. package/dist/BasicCheckout.d.ts.map +0 -1
  590. package/dist/BasicCheckout.js.map +0 -1
  591. package/dist/Snapshot.d.ts +0 -198
  592. package/dist/Snapshot.d.ts.map +0 -1
  593. package/dist/Snapshot.js +0 -267
  594. package/dist/Snapshot.js.map +0 -1
  595. package/dist/SnapshotUtilities.d.ts +0 -29
  596. package/dist/SnapshotUtilities.d.ts.map +0 -1
  597. package/dist/SnapshotUtilities.js +0 -73
  598. package/dist/SnapshotUtilities.js.map +0 -1
  599. package/dist/anchored-edits/AnchorResolution.d.ts +0 -144
  600. package/dist/anchored-edits/AnchorResolution.d.ts.map +0 -1
  601. package/dist/anchored-edits/AnchorResolution.js +0 -162
  602. package/dist/anchored-edits/AnchorResolution.js.map +0 -1
  603. package/dist/anchored-edits/Factory.d.ts +0 -56
  604. package/dist/anchored-edits/Factory.d.ts.map +0 -1
  605. package/dist/anchored-edits/Factory.js +0 -79
  606. package/dist/anchored-edits/Factory.js.map +0 -1
  607. package/dist/anchored-edits/PersistedTypes.d.ts +0 -245
  608. package/dist/anchored-edits/PersistedTypes.d.ts.map +0 -1
  609. package/dist/anchored-edits/PersistedTypes.js +0 -131
  610. package/dist/anchored-edits/PersistedTypes.js.map +0 -1
  611. package/dist/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
  612. package/dist/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
  613. package/dist/anchored-edits/SharedTreeWithAnchors.js +0 -115
  614. package/dist/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
  615. package/dist/anchored-edits/TransactionWithAnchors.d.ts +0 -28
  616. package/dist/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
  617. package/dist/anchored-edits/TransactionWithAnchors.js +0 -36
  618. package/dist/anchored-edits/TransactionWithAnchors.js.map +0 -1
  619. package/dist/anchored-edits/index.d.ts +0 -10
  620. package/dist/anchored-edits/index.d.ts.map +0 -1
  621. package/dist/anchored-edits/index.js +0 -34
  622. package/dist/anchored-edits/index.js.map +0 -1
  623. package/dist/default-edits/EditUtilities.d.ts +0 -57
  624. package/dist/default-edits/EditUtilities.d.ts.map +0 -1
  625. package/dist/default-edits/EditUtilities.js +0 -192
  626. package/dist/default-edits/EditUtilities.js.map +0 -1
  627. package/dist/default-edits/Factory.d.ts +0 -56
  628. package/dist/default-edits/Factory.d.ts.map +0 -1
  629. package/dist/default-edits/Factory.js +0 -79
  630. package/dist/default-edits/Factory.js.map +0 -1
  631. package/dist/default-edits/HistoryEditFactory.d.ts +0 -19
  632. package/dist/default-edits/HistoryEditFactory.d.ts.map +0 -1
  633. package/dist/default-edits/HistoryEditFactory.js +0 -187
  634. package/dist/default-edits/HistoryEditFactory.js.map +0 -1
  635. package/dist/default-edits/PersistedTypes.d.ts.map +0 -1
  636. package/dist/default-edits/PersistedTypes.js.map +0 -1
  637. package/dist/default-edits/SharedTree.d.ts +0 -111
  638. package/dist/default-edits/SharedTree.d.ts.map +0 -1
  639. package/dist/default-edits/SharedTree.js +0 -124
  640. package/dist/default-edits/SharedTree.js.map +0 -1
  641. package/dist/default-edits/Summary.d.ts +0 -15
  642. package/dist/default-edits/Summary.d.ts.map +0 -1
  643. package/dist/default-edits/Summary.js +0 -35
  644. package/dist/default-edits/Summary.js.map +0 -1
  645. package/dist/default-edits/Transaction.d.ts +0 -41
  646. package/dist/default-edits/Transaction.d.ts.map +0 -1
  647. package/dist/default-edits/Transaction.js +0 -225
  648. package/dist/default-edits/Transaction.js.map +0 -1
  649. package/dist/default-edits/UndoRedoHandler.d.ts.map +0 -1
  650. package/dist/default-edits/UndoRedoHandler.js.map +0 -1
  651. package/dist/default-edits/index.d.ts +0 -13
  652. package/dist/default-edits/index.d.ts.map +0 -1
  653. package/dist/default-edits/index.js +0 -41
  654. package/dist/default-edits/index.js.map +0 -1
  655. package/dist/generic/GenericEditUtilities.d.ts +0 -26
  656. package/dist/generic/GenericEditUtilities.d.ts.map +0 -1
  657. package/dist/generic/GenericEditUtilities.js +0 -45
  658. package/dist/generic/GenericEditUtilities.js.map +0 -1
  659. package/dist/generic/GenericSharedTree.d.ts +0 -221
  660. package/dist/generic/GenericSharedTree.d.ts.map +0 -1
  661. package/dist/generic/GenericSharedTree.js +0 -447
  662. package/dist/generic/GenericSharedTree.js.map +0 -1
  663. package/dist/generic/GenericTransaction.d.ts +0 -87
  664. package/dist/generic/GenericTransaction.d.ts.map +0 -1
  665. package/dist/generic/GenericTransaction.js +0 -144
  666. package/dist/generic/GenericTransaction.js.map +0 -1
  667. package/dist/generic/PersistedTypes.d.ts +0 -194
  668. package/dist/generic/PersistedTypes.d.ts.map +0 -1
  669. package/dist/generic/PersistedTypes.js +0 -42
  670. package/dist/generic/PersistedTypes.js.map +0 -1
  671. package/dist/generic/Summary.d.ts +0 -63
  672. package/dist/generic/Summary.d.ts.map +0 -1
  673. package/dist/generic/Summary.js +0 -64
  674. package/dist/generic/Summary.js.map +0 -1
  675. package/dist/generic/index.d.ts +0 -10
  676. package/dist/generic/index.d.ts.map +0 -1
  677. package/dist/generic/index.js +0 -26
  678. package/dist/generic/index.js.map +0 -1
  679. package/lib/BasicCheckout.d.ts +0 -23
  680. package/lib/BasicCheckout.d.ts.map +0 -1
  681. package/lib/BasicCheckout.js.map +0 -1
  682. package/lib/Snapshot.d.ts +0 -198
  683. package/lib/Snapshot.d.ts.map +0 -1
  684. package/lib/Snapshot.js +0 -263
  685. package/lib/Snapshot.js.map +0 -1
  686. package/lib/SnapshotUtilities.d.ts +0 -29
  687. package/lib/SnapshotUtilities.d.ts.map +0 -1
  688. package/lib/SnapshotUtilities.js +0 -67
  689. package/lib/SnapshotUtilities.js.map +0 -1
  690. package/lib/anchored-edits/AnchorResolution.d.ts +0 -144
  691. package/lib/anchored-edits/AnchorResolution.d.ts.map +0 -1
  692. package/lib/anchored-edits/AnchorResolution.js +0 -152
  693. package/lib/anchored-edits/AnchorResolution.js.map +0 -1
  694. package/lib/anchored-edits/Factory.d.ts +0 -56
  695. package/lib/anchored-edits/Factory.d.ts.map +0 -1
  696. package/lib/anchored-edits/Factory.js +0 -74
  697. package/lib/anchored-edits/Factory.js.map +0 -1
  698. package/lib/anchored-edits/PersistedTypes.d.ts +0 -245
  699. package/lib/anchored-edits/PersistedTypes.d.ts.map +0 -1
  700. package/lib/anchored-edits/PersistedTypes.js +0 -128
  701. package/lib/anchored-edits/PersistedTypes.js.map +0 -1
  702. package/lib/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
  703. package/lib/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
  704. package/lib/anchored-edits/SharedTreeWithAnchors.js +0 -110
  705. package/lib/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
  706. package/lib/anchored-edits/TransactionWithAnchors.d.ts +0 -28
  707. package/lib/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
  708. package/lib/anchored-edits/TransactionWithAnchors.js +0 -32
  709. package/lib/anchored-edits/TransactionWithAnchors.js.map +0 -1
  710. package/lib/anchored-edits/index.d.ts +0 -10
  711. package/lib/anchored-edits/index.d.ts.map +0 -1
  712. package/lib/anchored-edits/index.js +0 -11
  713. package/lib/anchored-edits/index.js.map +0 -1
  714. package/lib/default-edits/EditUtilities.d.ts +0 -57
  715. package/lib/default-edits/EditUtilities.d.ts.map +0 -1
  716. package/lib/default-edits/EditUtilities.js +0 -181
  717. package/lib/default-edits/EditUtilities.js.map +0 -1
  718. package/lib/default-edits/Factory.d.ts +0 -56
  719. package/lib/default-edits/Factory.d.ts.map +0 -1
  720. package/lib/default-edits/Factory.js +0 -74
  721. package/lib/default-edits/Factory.js.map +0 -1
  722. package/lib/default-edits/HistoryEditFactory.d.ts +0 -19
  723. package/lib/default-edits/HistoryEditFactory.d.ts.map +0 -1
  724. package/lib/default-edits/HistoryEditFactory.js.map +0 -1
  725. package/lib/default-edits/PersistedTypes.d.ts.map +0 -1
  726. package/lib/default-edits/PersistedTypes.js.map +0 -1
  727. package/lib/default-edits/SharedTree.d.ts +0 -111
  728. package/lib/default-edits/SharedTree.d.ts.map +0 -1
  729. package/lib/default-edits/SharedTree.js +0 -100
  730. package/lib/default-edits/SharedTree.js.map +0 -1
  731. package/lib/default-edits/Summary.d.ts +0 -15
  732. package/lib/default-edits/Summary.d.ts.map +0 -1
  733. package/lib/default-edits/Summary.js +0 -31
  734. package/lib/default-edits/Summary.js.map +0 -1
  735. package/lib/default-edits/Transaction.d.ts +0 -41
  736. package/lib/default-edits/Transaction.d.ts.map +0 -1
  737. package/lib/default-edits/Transaction.js +0 -221
  738. package/lib/default-edits/Transaction.js.map +0 -1
  739. package/lib/default-edits/UndoRedoHandler.d.ts.map +0 -1
  740. package/lib/default-edits/UndoRedoHandler.js.map +0 -1
  741. package/lib/default-edits/index.d.ts +0 -13
  742. package/lib/default-edits/index.d.ts.map +0 -1
  743. package/lib/default-edits/index.js +0 -14
  744. package/lib/default-edits/index.js.map +0 -1
  745. package/lib/generic/GenericEditUtilities.d.ts +0 -26
  746. package/lib/generic/GenericEditUtilities.d.ts.map +0 -1
  747. package/lib/generic/GenericEditUtilities.js +0 -38
  748. package/lib/generic/GenericEditUtilities.js.map +0 -1
  749. package/lib/generic/GenericSharedTree.d.ts +0 -221
  750. package/lib/generic/GenericSharedTree.d.ts.map +0 -1
  751. package/lib/generic/GenericSharedTree.js +0 -443
  752. package/lib/generic/GenericSharedTree.js.map +0 -1
  753. package/lib/generic/GenericTransaction.d.ts +0 -87
  754. package/lib/generic/GenericTransaction.d.ts.map +0 -1
  755. package/lib/generic/GenericTransaction.js +0 -140
  756. package/lib/generic/GenericTransaction.js.map +0 -1
  757. package/lib/generic/PersistedTypes.d.ts +0 -194
  758. package/lib/generic/PersistedTypes.d.ts.map +0 -1
  759. package/lib/generic/PersistedTypes.js +0 -39
  760. package/lib/generic/PersistedTypes.js.map +0 -1
  761. package/lib/generic/Summary.d.ts +0 -63
  762. package/lib/generic/Summary.d.ts.map +0 -1
  763. package/lib/generic/Summary.js +0 -58
  764. package/lib/generic/Summary.js.map +0 -1
  765. package/lib/generic/index.d.ts +0 -10
  766. package/lib/generic/index.d.ts.map +0 -1
  767. package/lib/generic/index.js +0 -11
  768. package/lib/generic/index.js.map +0 -1
  769. package/lib/test/Anchors.glassBox.tests.d.ts.map +0 -1
  770. package/lib/test/Anchors.glassBox.tests.js +0 -410
  771. package/lib/test/Anchors.glassBox.tests.js.map +0 -1
  772. package/lib/test/BasicCheckout.tests.d.ts.map +0 -1
  773. package/lib/test/BasicCheckout.tests.js +0 -8
  774. package/lib/test/BasicCheckout.tests.js.map +0 -1
  775. package/lib/test/GenericTransactionWithAnchors.tests.d.ts.map +0 -1
  776. package/lib/test/GenericTransactionWithAnchors.tests.js +0 -25
  777. package/lib/test/GenericTransactionWithAnchors.tests.js.map +0 -1
  778. package/lib/test/SharedTreeWithAnchors.tests.d.ts.map +0 -1
  779. package/lib/test/SharedTreeWithAnchors.tests.js +0 -420
  780. package/lib/test/SharedTreeWithAnchors.tests.js.map +0 -1
  781. package/lib/test/Snapshot.tests.d.ts.map +0 -1
  782. package/lib/test/Snapshot.tests.js +0 -96
  783. package/lib/test/Snapshot.tests.js.map +0 -1
  784. package/lib/test/SnapshotUtilities.tests.d.ts.map +0 -1
  785. package/lib/test/SnapshotUtilities.tests.js +0 -168
  786. package/lib/test/SnapshotUtilities.tests.js.map +0 -1
  787. package/lib/test/undoRedoStackManager.d.ts +0 -26
  788. package/lib/test/undoRedoStackManager.d.ts.map +0 -1
  789. package/lib/test/undoRedoStackManager.js +0 -176
  790. package/lib/test/undoRedoStackManager.js.map +0 -1
  791. package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts +0 -13
  792. package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts.map +0 -1
  793. package/lib/test/utilities/SummaryFormatCompatibilityTests.js +0 -154
  794. package/lib/test/utilities/SummaryFormatCompatibilityTests.js.map +0 -1
  795. package/src/BasicCheckout.ts +0 -34
  796. package/src/Snapshot.ts +0 -363
  797. package/src/SnapshotUtilities.ts +0 -88
  798. package/src/anchored-edits/AnchorResolution.ts +0 -442
  799. package/src/anchored-edits/Factory.ts +0 -94
  800. package/src/anchored-edits/PersistedTypes.ts +0 -310
  801. package/src/anchored-edits/SharedTreeWithAnchors.ts +0 -200
  802. package/src/anchored-edits/TransactionWithAnchors.ts +0 -39
  803. package/src/anchored-edits/index.ts +0 -21
  804. package/src/default-edits/EditUtilities.ts +0 -220
  805. package/src/default-edits/Factory.ts +0 -94
  806. package/src/default-edits/SharedTree.ts +0 -174
  807. package/src/default-edits/Summary.ts +0 -44
  808. package/src/default-edits/Transaction.ts +0 -262
  809. package/src/default-edits/index.ts +0 -29
  810. package/src/generic/GenericEditUtilities.ts +0 -46
  811. package/src/generic/GenericSharedTree.ts +0 -593
  812. package/src/generic/GenericTransaction.ts +0 -194
  813. package/src/generic/PersistedTypes.ts +0 -221
  814. package/src/generic/Summary.ts +0 -113
  815. package/src/generic/index.ts +0 -41
@@ -0,0 +1,1448 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { bufferToString, IsoBuffer } from '@fluidframework/common-utils';
7
+ import { IFluidHandle } from '@fluidframework/core-interfaces';
8
+ import { ISequencedDocumentMessage } from '@fluidframework/protocol-definitions';
9
+ import {
10
+ IFluidDataStoreRuntime,
11
+ IChannelStorageService,
12
+ IChannelFactory,
13
+ IChannelAttributes,
14
+ IChannelServices,
15
+ IChannel,
16
+ } from '@fluidframework/datastore-definitions';
17
+ import { AttachState } from '@fluidframework/container-definitions';
18
+ import {
19
+ createSingleBlobSummary,
20
+ IFluidSerializer,
21
+ ISharedObjectEvents,
22
+ serializeHandles,
23
+ SharedObject,
24
+ } from '@fluidframework/shared-object-base';
25
+ import { ITelemetryLogger, ITelemetryProperties } from '@fluidframework/common-definitions';
26
+ import { ChildLogger, ITelemetryLoggerPropertyBags, PerformanceEvent } from '@fluidframework/telemetry-utils';
27
+ import { ISummaryTreeWithStats } from '@fluidframework/runtime-definitions';
28
+ import { assert, assertNotUndefined, fail, copyPropertyIfDefined } from './Common';
29
+ import { EditHandle, EditLog, getNumberOfHandlesFromEditLogSummary, OrderedEditSet } from './EditLog';
30
+ import { EditId, NodeId, StableNodeId, DetachedSequenceId, OpSpaceNodeId, isDetachedSequenceId } from './Identifiers';
31
+ import { initialTree } from './InitialTree';
32
+ import {
33
+ CachingLogViewer,
34
+ EditCacheEntry,
35
+ EditStatusCallback,
36
+ LogViewer,
37
+ SequencedEditResult,
38
+ SequencedEditResultCallback,
39
+ } from './LogViewer';
40
+ import { deserialize, getSummaryStatistics } from './SummaryBackCompatibility';
41
+ import { ReconciliationPath } from './ReconciliationPath';
42
+ import {
43
+ BuildNodeInternal,
44
+ ChangeInternal,
45
+ ChangeNode,
46
+ ChangeTypeInternal,
47
+ ConstraintInternal,
48
+ DetachInternal,
49
+ Edit,
50
+ EditLogSummary,
51
+ EditChunkContents,
52
+ EditStatus,
53
+ EditWithoutId,
54
+ reservedIdCount,
55
+ SharedTreeEditOp,
56
+ SharedTreeEditOp_0_0_2,
57
+ SharedTreeHandleOp,
58
+ SharedTreeNoOp,
59
+ SharedTreeOp,
60
+ SharedTreeOpType,
61
+ SharedTreeOp_0_0_2,
62
+ SharedTreeSummary,
63
+ SharedTreeSummaryBase,
64
+ SharedTreeSummary_0_0_2,
65
+ TreeNode,
66
+ ghostSessionId,
67
+ WriteFormat,
68
+ TreeNodeSequence,
69
+ } from './persisted-types';
70
+ import { serialize, SummaryContents } from './Summary';
71
+ import {
72
+ areRevisionViewsSemanticallyEqual,
73
+ convertTreeNodes,
74
+ deepCloneStablePlace,
75
+ deepCloneStableRange,
76
+ internalizeBuildNode,
77
+ newEditId,
78
+ walkTree,
79
+ } from './EditUtilities';
80
+ import { getNodeIdContext, NodeIdContext, NodeIdNormalizer, sequencedIdNormalizer } from './NodeIdUtilities';
81
+ import { SharedTreeDiagnosticEvent, SharedTreeEvent } from './EventTypes';
82
+ import { RevisionView } from './RevisionView';
83
+ import { SharedTreeEncoder_0_0_2, SharedTreeEncoder_0_1_1 } from './SharedTreeEncoder';
84
+ import { revert } from './HistoryEditFactory';
85
+ import { BuildNode, BuildTreeNode, Change, ChangeType } from './ChangeTypes';
86
+ import { TransactionInternal } from './TransactionInternal';
87
+ import { IdCompressor, createSessionId } from './id-compressor';
88
+ import { convertEditIds } from './IdConversion';
89
+ import { MutableStringInterner } from './StringInterner';
90
+
91
+ /**
92
+ * Factory for SharedTree.
93
+ * Includes history in the summary.
94
+ * @public
95
+ */
96
+ export class SharedTreeFactory implements IChannelFactory {
97
+ /**
98
+ * {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory."type"}
99
+ */
100
+ public static Type = 'SharedTree';
101
+
102
+ /**
103
+ * {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory.attributes}
104
+ */
105
+ public static Attributes: IChannelAttributes = {
106
+ type: SharedTreeFactory.Type,
107
+ snapshotFormatVersion: '0.1',
108
+ packageVersion: '0.1',
109
+ };
110
+
111
+ /**
112
+ * Get a factory for SharedTree to register with the data store.
113
+ * @param writeFormat - Determines the format version the SharedTree will write summaries in.
114
+ * This format may be updated to a newer (supported) version at runtime if a collaborating shared-tree
115
+ * that was initialized with a newer write version connects to the session. Care must be taken when changing this value,
116
+ * as a staged rollout must have occurred such that all collaborating clients must have the code to read at least the version
117
+ * written.
118
+ * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
119
+ * See docs/Breaking-Change-Migration for more details on this scheme.
120
+ * @param expensiveValidation - Enables expensive asserts on SharedTree.
121
+ * @returns A factory that creates `SharedTree`s and loads them from storage.
122
+ */
123
+ constructor(
124
+ private readonly writeFormat: WriteFormat,
125
+ private readonly summarizeHistory: false | { uploadEditChunks: boolean } = false,
126
+ private expensiveValidation = false
127
+ ) {}
128
+
129
+ /**
130
+ * {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory."type"}
131
+ */
132
+ public get type(): string {
133
+ return SharedTreeFactory.Type;
134
+ }
135
+
136
+ /**
137
+ * {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory.attributes}
138
+ */
139
+ public get attributes(): IChannelAttributes {
140
+ return SharedTreeFactory.Attributes;
141
+ }
142
+
143
+ /**
144
+ * {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory.load}
145
+ */
146
+ public async load(
147
+ runtime: IFluidDataStoreRuntime,
148
+ id: string,
149
+ services: IChannelServices,
150
+ _channelAttributes: Readonly<IChannelAttributes>
151
+ ): Promise<IChannel> {
152
+ const sharedTree = this.createSharedTree(runtime, id);
153
+ await sharedTree.load(services);
154
+ return sharedTree;
155
+ }
156
+
157
+ /**
158
+ * Create a new SharedTree.
159
+ * @param runtime - data store runtime that owns the new SharedTree
160
+ * @param id - optional name for the SharedTree
161
+ */
162
+ public create(runtime: IFluidDataStoreRuntime, id: string, expensiveValidation: boolean = false): SharedTree {
163
+ this.expensiveValidation = expensiveValidation;
164
+ const sharedTree = this.createSharedTree(runtime, id);
165
+ sharedTree.initializeLocal();
166
+ return sharedTree;
167
+ }
168
+
169
+ private createSharedTree(runtime: IFluidDataStoreRuntime, id: string): SharedTree {
170
+ const sharedTree = new SharedTree(
171
+ runtime,
172
+ id,
173
+ this.writeFormat,
174
+ this.summarizeHistory,
175
+ this.expensiveValidation
176
+ );
177
+ return sharedTree;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Filename where the snapshot is stored.
183
+ */
184
+ const snapshotFileName = 'header';
185
+
186
+ /**
187
+ * Used for version comparison.
188
+ */
189
+ const sortedWriteVersions = [WriteFormat.v0_0_2, WriteFormat.v0_1_1];
190
+
191
+ /**
192
+ * The arguments included when the EditCommitted SharedTreeEvent is emitted.
193
+ * @public
194
+ */
195
+ export interface EditCommittedEventArguments {
196
+ /** The ID of the edit committed. */
197
+ readonly editId: EditId;
198
+ /** Whether or not this is a local edit. */
199
+ readonly local: boolean;
200
+ /** The tree the edit was committed on. Required for local edit events handled by SharedTreeUndoRedoHandler. */
201
+ readonly tree: SharedTree;
202
+ }
203
+
204
+ /**
205
+ * The arguments included when the {@link SharedTreeEvent.SequencedEditApplied} SharedTreeEvent is emitted.
206
+ * @public
207
+ */
208
+ export interface SequencedEditAppliedEventArguments {
209
+ /** The ID of the edit committed. */
210
+ readonly edit: Edit<ChangeInternal>;
211
+ /** Whether or not this was a local edit. */
212
+ readonly wasLocal: boolean;
213
+ /** The tree the edit was applied to. */
214
+ readonly tree: SharedTree;
215
+ /** The telemetry logger associated with sequenced edit application. */
216
+ readonly logger: ITelemetryLogger;
217
+ /** The reconciliation path for the edit. See {@link ReconciliationPath} for details. */
218
+ readonly reconciliationPath: ReconciliationPath;
219
+ /** The outcome of the sequenced edit being applied. */
220
+ readonly outcome: EditApplicationOutcome;
221
+ }
222
+
223
+ /**
224
+ * The outcome of an edit.
225
+ * @public
226
+ */
227
+ export type EditApplicationOutcome =
228
+ | {
229
+ /**
230
+ * The revision view resulting from the edit.
231
+ */
232
+ readonly view: RevisionView;
233
+ /**
234
+ * The status code for the edit that produced the revision.
235
+ */
236
+ readonly status: EditStatus.Applied;
237
+ }
238
+ | {
239
+ /**
240
+ * The revision view resulting from the edit.
241
+ */
242
+ readonly failure: TransactionInternal.Failure;
243
+ /**
244
+ * The status code for the edit that produced the revision.
245
+ */
246
+ readonly status: EditStatus.Invalid | EditStatus.Malformed;
247
+ };
248
+
249
+ /**
250
+ * Events which may be emitted by `SharedTree`. See {@link SharedTreeEvent} for documentation of event semantics.
251
+ * @public
252
+ */
253
+ export interface ISharedTreeEvents extends ISharedObjectEvents {
254
+ (event: 'committedEdit', listener: EditCommittedHandler);
255
+ (event: 'appliedSequencedEdit', listener: SequencedEditAppliedHandler);
256
+ }
257
+
258
+ /**
259
+ * Expected type for a handler of the `EditCommitted` event.
260
+ * @public
261
+ */
262
+ export type EditCommittedHandler = (args: EditCommittedEventArguments) => void;
263
+
264
+ /**
265
+ * Expected type for a handler of the {@link SharedTreeEvent.SequencedEditApplied} event.
266
+ * @public
267
+ */
268
+ export type SequencedEditAppliedHandler = (args: SequencedEditAppliedEventArguments) => void;
269
+
270
+ const sharedTreeTelemetryProperties: ITelemetryLoggerPropertyBags = { all: { isSharedTreeEvent: true } };
271
+
272
+ /**
273
+ * A distributed tree.
274
+ * @public
275
+ */
276
+ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeIdContext {
277
+ /**
278
+ * Create a new SharedTree. It will contain the default value (see initialTree).
279
+ */
280
+ public static create(runtime: IFluidDataStoreRuntime, id?: string): SharedTree {
281
+ return runtime.createChannel(id, SharedTreeFactory.Type) as SharedTree;
282
+ }
283
+
284
+ /**
285
+ * Get a factory for SharedTree to register with the data store.
286
+ * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
287
+ * On 0.1.1 documents, due to current code limitations, this parameter is only impactful for newly created documents.
288
+ * `SharedTree`s which load existing documents will summarize history if and only if the loaded summary included history.
289
+ *
290
+ * The technical limitations here relate to clients with mixed versions collaborating.
291
+ * In the future we may allow modification of whether or not a particular document saves history, but only via a consensus mechanism.
292
+ * See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
293
+ * See docs/Breaking-Change-Migration for more details on the consensus scheme.
294
+ * @param writeFormat - Determines the format version the SharedTree will write summaries in.
295
+ * This format may be updated to a newer (supported) version at runtime if a collaborating shared-tree
296
+ * that was initialized with a newer write version connects to the session. Care must be taken when changing this value,
297
+ * as a staged rollout must of occurred such that all collaborating clients must have the code to read at least the version
298
+ * written.
299
+ * @returns A factory that creates `SharedTree`s and loads them from storage.
300
+ */
301
+ public static getFactory(
302
+ writeFormat: WriteFormat,
303
+ summarizeHistory: false | { uploadEditChunks: boolean } = false
304
+ ): SharedTreeFactory {
305
+ return new SharedTreeFactory(writeFormat, summarizeHistory);
306
+ }
307
+
308
+ private idCompressor: IdCompressor = new IdCompressor(createSessionId(), reservedIdCount);
309
+ private readonly idNormalizer: NodeIdNormalizer<OpSpaceNodeId> & { tree: SharedTree } = {
310
+ tree: this,
311
+ get localSessionId() {
312
+ return this.tree.idCompressor.localSessionId;
313
+ },
314
+ normalizeToOpSpace: (id) => this.idCompressor.normalizeToOpSpace(id) as OpSpaceNodeId,
315
+ normalizeToSessionSpace: (id, sessionId) => this.idCompressor.normalizeToSessionSpace(id, sessionId) as NodeId,
316
+ };
317
+
318
+ // The initial tree's definition isn't included in any op by default but it should still be interned. Including it here ensures that.
319
+ private interner: MutableStringInterner = new MutableStringInterner([initialTree.definition]);
320
+
321
+ /**
322
+ * The log of completed edits for this SharedTree.
323
+ */
324
+ private editLog: EditLog<ChangeInternal>;
325
+
326
+ /**
327
+ * As an implementation detail, SharedTree uses a log viewer that caches views of different revisions.
328
+ * It is not exposed to avoid accidental correctness issues, but `logViewer` is exposed in order to give clients a way
329
+ * to access the revision history.
330
+ */
331
+ private cachingLogViewer: CachingLogViewer;
332
+
333
+ /**
334
+ * Viewer for trees defined by editLog. This allows access to views of the tree at different revisions (various points in time).
335
+ */
336
+ public get logViewer(): LogViewer {
337
+ return this.cachingLogViewer;
338
+ }
339
+
340
+ protected readonly logger: ITelemetryLogger;
341
+ private readonly sequencedEditAppliedLogger: ITelemetryLogger;
342
+
343
+ private readonly encoder_0_0_2: SharedTreeEncoder_0_0_2;
344
+ private encoder_0_1_1: SharedTreeEncoder_0_1_1;
345
+
346
+ /** Indicates if the client is the oldest member of the quorum. */
347
+ private currentIsOldest: boolean;
348
+
349
+ private readonly processEditResult = (editResult: EditStatus, editId: EditId): void => {
350
+ // TODO:#44859: Invalid results should be handled by the app
351
+ this.emit(SharedTree.eventFromEditResult(editResult), editId);
352
+ };
353
+
354
+ private readonly processSequencedEditResult = ({
355
+ edit,
356
+ wasLocal,
357
+ result,
358
+ reconciliationPath,
359
+ }: SequencedEditResult): void => {
360
+ const eventArguments: SequencedEditAppliedEventArguments = {
361
+ edit,
362
+ wasLocal,
363
+ tree: this,
364
+ logger: this.sequencedEditAppliedLogger,
365
+ reconciliationPath,
366
+ outcome: result,
367
+ };
368
+ this.emit(SharedTreeEvent.SequencedEditApplied, eventArguments);
369
+ };
370
+
371
+ public readonly transactionFactory = TransactionInternal.factory;
372
+
373
+ private summarizeHistory: boolean;
374
+ private uploadEditChunks: boolean;
375
+
376
+ /**
377
+ * Create a new SharedTreeFactory.
378
+ * @param runtime - The runtime the SharedTree will be associated with
379
+ * @param id - Unique ID for the SharedTree
380
+ * @param writeFormat - Determines the format version the SharedTree will write summaries in.
381
+ * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
382
+ * @param expensiveValidation - Enable expensive asserts.
383
+ */
384
+ public constructor(
385
+ runtime: IFluidDataStoreRuntime,
386
+ id: string,
387
+ private writeFormat: WriteFormat,
388
+ summarizeHistory: false | { uploadEditChunks: boolean } = false,
389
+ private readonly expensiveValidation = false
390
+ ) {
391
+ super(id, runtime, SharedTreeFactory.Attributes);
392
+ this.summarizeHistory = summarizeHistory === false ? false : true;
393
+ this.uploadEditChunks = summarizeHistory === false ? false : summarizeHistory.uploadEditChunks;
394
+
395
+ // This code is somewhat duplicated from OldestClientObserver because it currently depends on the container runtime
396
+ // which SharedTree does not have access to.
397
+ // TODO:#55900: Get rid of copy-pasted OldestClientObserver code
398
+ const quorum = this.runtime.getQuorum();
399
+ this.currentIsOldest = this.computeIsOldest();
400
+ quorum.on('addMember', this.updateOldest);
401
+ quorum.on('removeMember', this.updateOldest);
402
+ runtime.on('connected', this.updateOldest);
403
+ runtime.on('disconnected', this.updateOldest);
404
+
405
+ this.logger = ChildLogger.create(runtime.logger, 'SharedTree', sharedTreeTelemetryProperties);
406
+ this.sequencedEditAppliedLogger = ChildLogger.create(
407
+ this.logger,
408
+ 'SequencedEditApplied',
409
+ sharedTreeTelemetryProperties
410
+ );
411
+
412
+ const { editLog, cachingLogViewer } = this.initializeNewEditLogFromSummary(
413
+ {
414
+ editChunks: [],
415
+ editIds: [],
416
+ },
417
+ undefined,
418
+ this.idCompressor, // TODO: Attribution info
419
+ this.processEditResult,
420
+ this.processSequencedEditResult,
421
+ WriteFormat.v0_1_1
422
+ );
423
+ this.editLog = editLog;
424
+ this.cachingLogViewer = cachingLogViewer;
425
+ this.encoder_0_0_2 = new SharedTreeEncoder_0_0_2(this.summarizeHistory);
426
+ this.encoder_0_1_1 = new SharedTreeEncoder_0_1_1(this.summarizeHistory);
427
+ }
428
+
429
+ public getWriteFormat(): WriteFormat {
430
+ return this.writeFormat;
431
+ }
432
+
433
+ /**
434
+ * Re-computes currentIsOldest and emits an event if it has changed.
435
+ * TODO:#55900: Get rid of copy-pasted OldestClientObserver code
436
+ */
437
+ private readonly updateOldest = () => {
438
+ const oldest = this.computeIsOldest();
439
+ if (this.currentIsOldest !== oldest) {
440
+ this.currentIsOldest = oldest;
441
+ if (oldest) {
442
+ this.emit('becameOldest');
443
+ this.logger.sendTelemetryEvent({ eventName: 'BecameOldestClient' });
444
+ } else {
445
+ this.emit('lostOldest');
446
+ }
447
+ }
448
+ };
449
+
450
+ /**
451
+ * Computes the oldest client in the quorum, true by default if the container is detached and false by default if the client isn't connected.
452
+ * TODO:#55900: Get rid of copy-pasted OldestClientObserver code
453
+ */
454
+ private computeIsOldest(): boolean {
455
+ // If the container is detached, we are the only ones that know about it and are the oldest by default.
456
+ if (this.runtime.attachState === AttachState.Detached) {
457
+ return true;
458
+ }
459
+
460
+ // If we're not connected we can't be the oldest connected client.
461
+ if (!this.runtime.connected) {
462
+ return false;
463
+ }
464
+
465
+ assert(this.runtime.clientId !== undefined, 'Client id should be set if connected.');
466
+
467
+ const quorum = this.runtime.getQuorum();
468
+ const selfSequencedClient = quorum.getMember(this.runtime.clientId);
469
+ // When in readonly mode our clientId will not be present in the quorum.
470
+ if (selfSequencedClient === undefined) {
471
+ return false;
472
+ }
473
+
474
+ const members = quorum.getMembers();
475
+ for (const sequencedClient of members.values()) {
476
+ if (sequencedClient.sequenceNumber < selfSequencedClient.sequenceNumber) {
477
+ return false;
478
+ }
479
+ }
480
+
481
+ // No member of the quorum was older
482
+ return true;
483
+ }
484
+
485
+ /**
486
+ * @returns the current view of the tree.
487
+ */
488
+ public get currentView(): RevisionView {
489
+ return this.logViewer.getRevisionViewInSession(Number.POSITIVE_INFINITY);
490
+ }
491
+
492
+ /**
493
+ * {@inheritdoc NodeIdGenerator.generateNodeId}
494
+ * @public
495
+ */
496
+ public generateNodeId(override?: string): NodeId {
497
+ return this.idCompressor.generateCompressedId(override) as NodeId;
498
+ }
499
+
500
+ /** {@inheritdoc NodeIdConverter.convertToStableNodeId} */
501
+ public convertToStableNodeId(id: NodeId): StableNodeId {
502
+ return (this.idCompressor.tryDecompress(id) as StableNodeId) ?? fail('Node id is not known to this SharedTree');
503
+ }
504
+
505
+ /** {@inheritdoc NodeIdConverter.tryConvertToStableNodeId} */
506
+ public tryConvertToStableNodeId(id: NodeId): StableNodeId | undefined {
507
+ return this.idCompressor.tryDecompress(id) as StableNodeId | undefined;
508
+ }
509
+
510
+ /** {@inheritdoc NodeIdConverter.convertToNodeId} */
511
+ public convertToNodeId(id: StableNodeId): NodeId {
512
+ return (
513
+ (this.idCompressor.tryRecompress(id) as NodeId) ?? fail('Stable node id is not known to this SharedTree')
514
+ );
515
+ }
516
+
517
+ /** {@inheritdoc NodeIdConverter.tryConvertToNodeId} */
518
+ public tryConvertToNodeId(id: StableNodeId): NodeId | undefined {
519
+ return this.idCompressor.tryRecompress(id) as NodeId | undefined;
520
+ }
521
+
522
+ /**
523
+ * @returns the edit history of the tree.
524
+ * @public
525
+ */
526
+ public get edits(): OrderedEditSet {
527
+ return this.editLog;
528
+ }
529
+
530
+ /**
531
+ * @returns the edit history of the tree. The format of the contents of edits are subject to change and should not be relied upon.
532
+ * @internal
533
+ */
534
+ public get editsInternal(): OrderedEditSet<ChangeInternal> {
535
+ return this.editLog;
536
+ }
537
+
538
+ private deserializeHandle(serializedHandle: string): IFluidHandle<ArrayBufferLike> {
539
+ const deserializeHandle = this.serializer.parse(serializedHandle);
540
+ assert(typeof deserializeHandle === 'object');
541
+ return deserializeHandle as IFluidHandle<ArrayBufferLike>;
542
+ }
543
+
544
+ /**
545
+ * Uploads the edit chunk and sends the chunk starting revision along with the resulting handle as an op.
546
+ */
547
+ private async uploadEditChunk(
548
+ edits: readonly EditWithoutId<ChangeInternal>[],
549
+ startRevision: number
550
+ ): Promise<void> {
551
+ assert(this.writeFormat !== WriteFormat.v0_0_2, 'Edit chunking is not supported in v0_0_2');
552
+ // SPO attachment blob upload limit is set here:
553
+ // https://onedrive.visualstudio.com/SharePoint%20Online/_git/SPO?path=%2Fsts%2Fstsom%2FPrague%2FSPPragueProtocolConfig.cs&version=GBmaster&line=82&lineEnd=82&lineStartColumn=29&lineEndColumn=116&lineStyle=plain&_a=contents
554
+ // TODO:#59754: Create chunks based on data buffer size instead of number of edits
555
+ const blobUploadSizeLimit = 4194304;
556
+
557
+ try {
558
+ const chunkContents = this.encoder_0_1_1.encodeEditChunk(
559
+ edits,
560
+ sequencedIdNormalizer(this.idNormalizer),
561
+ this.interner
562
+ );
563
+ const serializedContents = serializeHandles(chunkContents, this.serializer, this.handle);
564
+ const buffer = IsoBuffer.from(serializedContents);
565
+ const bufferSize = buffer.byteLength;
566
+ assert(
567
+ bufferSize <= blobUploadSizeLimit,
568
+ `Edit chunk size ${bufferSize} is larger than blob upload size limit of ${blobUploadSizeLimit} bytes.`
569
+ );
570
+ const editHandle = await this.runtime.uploadBlob(buffer);
571
+ const handleOp: SharedTreeHandleOp = {
572
+ editHandle:
573
+ serializeHandles(editHandle, this.serializer, this.handle) ??
574
+ fail('Edit chunk handle could not be serialized.'),
575
+ startRevision,
576
+ type: SharedTreeOpType.Handle,
577
+ version: this.writeFormat,
578
+ };
579
+ this.submitOp(handleOp);
580
+ this.emit(SharedTreeDiagnosticEvent.EditChunkUploaded);
581
+ } catch (error) {
582
+ // If chunk load fails, we will try again later in loadCore on the oldest client so we log the error instead of throwing.
583
+ this.logger.sendErrorEvent(
584
+ {
585
+ eventName: 'EditChunkUploadFailure',
586
+ },
587
+ error
588
+ );
589
+ }
590
+ }
591
+
592
+ /**
593
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.summarizeCore}
594
+ */
595
+ public summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {
596
+ return createSingleBlobSummary(snapshotFileName, this.saveSerializedSummary({ serializer }));
597
+ }
598
+
599
+ /**
600
+ * Saves this SharedTree into a serialized summary. This is used for testing.
601
+ *
602
+ * @param summarizer - Optional summarizer to use. If not passed in, SharedTree's summarizer is used.
603
+ * @internal
604
+ */
605
+ public saveSerializedSummary(options?: { serializer?: IFluidSerializer }): string {
606
+ const { serializer } = options || {};
607
+ return serialize(this.saveSummary(), serializer ?? this.serializer, this.handle);
608
+ }
609
+
610
+ /**
611
+ * Initialize shared tree with a serialized summary. This is used for testing.
612
+ * @returns - statistics about the loaded summary.
613
+ * @internal
614
+ */
615
+ public loadSerializedSummary(blobData: string): ITelemetryProperties {
616
+ const summary = deserialize(blobData, this.serializer);
617
+ this.loadSummary(summary);
618
+ return getSummaryStatistics(summary);
619
+ }
620
+
621
+ /**
622
+ * Saves this SharedTree into a deserialized summary.
623
+ * @internal
624
+ */
625
+ public saveSummary(): SharedTreeSummaryBase {
626
+ // If local changes exist, emulate the sequencing of those changes.
627
+ // Doing so is necessary so edits created during DataObject.initializingFirstTime are included.
628
+ // Doing so is safe because it is guaranteed that the DDS has not yet been attached. This is because summary creation is only
629
+ // ever invoked on a DataObject containing local changes when it is attached for the first time. In post-attach flows, an extra
630
+ // instance of the DataObject is created for generating summaries and will never have local edits.
631
+ if (this.editLog.numberOfLocalEdits > 0) {
632
+ assert(
633
+ this.runtime.attachState !== AttachState.Attached,
634
+ 'Summarizing should not occur with local edits except on first attach.'
635
+ );
636
+ if (this.writeFormat === WriteFormat.v0_1_1) {
637
+ // Since we're the first client to attach, we can safely finalize ourselves since we're the only ones who have made IDs.
638
+ this.idCompressor.finalizeCreationRange(this.idCompressor.takeNextCreationRange());
639
+ for (const edit of this.editLog.getLocalEdits()) {
640
+ this.internStringsFromEdit(edit);
641
+ }
642
+ }
643
+ this.editLog.sequenceLocalEdits();
644
+ }
645
+
646
+ assert(this.editLog.numberOfLocalEdits === 0, 'generateSummary must not be called with local edits');
647
+ return this.generateSummary();
648
+ }
649
+
650
+ /**
651
+ * Generates a SharedTree summary for the current state of the tree.
652
+ * Will never be called when local edits are present.
653
+ */
654
+ private generateSummary(): SharedTreeSummaryBase {
655
+ try {
656
+ switch (this.writeFormat) {
657
+ case WriteFormat.v0_0_2:
658
+ return this.encoder_0_0_2.encodeSummary(this.editLog, this.currentView, this);
659
+ case WriteFormat.v0_1_1:
660
+ return this.encoder_0_1_1.encodeSummary(
661
+ this.editLog,
662
+ this.currentView,
663
+ this,
664
+ this.idNormalizer,
665
+ this.interner,
666
+ this.idCompressor.serialize(false)
667
+ );
668
+ default:
669
+ fail('Unknown version');
670
+ }
671
+ } catch (error) {
672
+ this.logger?.sendErrorEvent({
673
+ eventName: 'UnsupportedSummaryWriteFormat',
674
+ formatVersion: this.writeFormat,
675
+ });
676
+ throw error;
677
+ }
678
+ }
679
+
680
+ /**
681
+ * Initialize shared tree with a deserialized summary.
682
+ * @internal
683
+ */
684
+ public loadSummary(summary: SharedTreeSummaryBase): void {
685
+ const { version: loadedSummaryVersion } = summary;
686
+
687
+ if (isUpdateRequired(loadedSummaryVersion, this.writeFormat)) {
688
+ this.submitOp({ type: SharedTreeOpType.Update, version: this.writeFormat });
689
+ this.logger.sendTelemetryEvent({
690
+ eventName: 'RequestVersionUpdate',
691
+ versionFrom: loadedSummaryVersion,
692
+ versionTo: this.writeFormat,
693
+ });
694
+ }
695
+
696
+ if (compareSummaryFormatVersions(loadedSummaryVersion, this.writeFormat) !== 0) {
697
+ // Write whatever format the loaded summary uses (this is the current agreed-upon format: it may be updated by an update op)
698
+ this.changeWriteFormat(loadedSummaryVersion);
699
+ }
700
+
701
+ assert(
702
+ this.idCompressor.getAllIdsFromLocalSession().next().done === true,
703
+ 'Summary load should not be executed after local state is created.'
704
+ );
705
+
706
+ let convertedSummary: SummaryContents;
707
+ switch (loadedSummaryVersion) {
708
+ case WriteFormat.v0_0_2:
709
+ convertedSummary = this.encoder_0_0_2.decodeSummary(summary as SharedTreeSummary_0_0_2);
710
+ break;
711
+ case WriteFormat.v0_1_1: {
712
+ const typedSummary = summary as SharedTreeSummary;
713
+ // See comment in factory constructor--ensure we write a consistent type of summary as how the document began.
714
+ const loadedSummaryIncludesHistory = typedSummary.currentTree !== undefined;
715
+ if (loadedSummaryIncludesHistory !== this.summarizeHistory) {
716
+ this.summarizeHistory = loadedSummaryIncludesHistory;
717
+ this.uploadEditChunks = loadedSummaryIncludesHistory;
718
+ this.encoder_0_1_1 = new SharedTreeEncoder_0_1_1(this.summarizeHistory);
719
+ }
720
+
721
+ convertedSummary = this.encoder_0_1_1.decodeSummary(summary as SharedTreeSummary);
722
+ break;
723
+ }
724
+ default:
725
+ fail('Unknown version');
726
+ }
727
+
728
+ const { editHistory, currentTree, idCompressor, interner } = convertedSummary;
729
+ this.interner = interner;
730
+ this.interner.getOrCreateInternedId(initialTree.definition);
731
+ if (compareSummaryFormatVersions(loadedSummaryVersion, WriteFormat.v0_1_1) < 0) {
732
+ const { editIds, editChunks } = editHistory;
733
+ this.logger.sendTelemetryEvent({
734
+ eventName: 'SummaryConversion',
735
+ formatVersion: WriteFormat.v0_1_1,
736
+ historySize: editIds.length,
737
+ totalNumberOfChunks: editChunks.length,
738
+ uploadedChunks: getNumberOfHandlesFromEditLogSummary(editHistory),
739
+ });
740
+ }
741
+
742
+ this.initializeNewEditLogFromSummary(
743
+ editHistory,
744
+ currentTree,
745
+ idCompressor,
746
+ this.processEditResult,
747
+ this.processSequencedEditResult,
748
+ summary.version
749
+ );
750
+
751
+ if (this.runtime.connected) {
752
+ const noChunksReadyForUpload = this.editLog.getEditChunksReadyForUpload()[Symbol.iterator]().next().done;
753
+ if (noChunksReadyForUpload === undefined || !noChunksReadyForUpload) {
754
+ // A client does not become a member of the quorum until it is within the collaboration window.
755
+ //
756
+ // The collaboration window is the range from the minimum sequence number enforced by the server and head.
757
+ // When a client sends an op, they include the last sequence number the client has processed. We call this the reference
758
+ // sequence number.
759
+ //
760
+ // If there are no members in the quorum, we send a no op op in order to have this client added as a member to the quorum.
761
+ // This is required so we can ensure only the oldest client will upload blobs during summary load.
762
+ if (this.runtime.getQuorum().getMembers().size === 0) {
763
+ const noop: SharedTreeNoOp = {
764
+ type: SharedTreeOpType.NoOp,
765
+ version: this.writeFormat,
766
+ };
767
+
768
+ this.submitOp(noop);
769
+ this.logger.sendTelemetryEvent({ eventName: 'NoOpSent' });
770
+ } else if (this.currentIsOldest) {
771
+ this.uploadCatchUpBlobs();
772
+ }
773
+ }
774
+
775
+ // If this client becomes the oldest, it should take care of uploading catch up blobs.
776
+ this.on('becameOldest', () => this.uploadCatchUpBlobs());
777
+ }
778
+ }
779
+
780
+ private static eventFromEditResult(editStatus: EditStatus): SharedTreeDiagnosticEvent {
781
+ switch (editStatus) {
782
+ case EditStatus.Applied:
783
+ return SharedTreeDiagnosticEvent.AppliedEdit;
784
+ case EditStatus.Invalid:
785
+ return SharedTreeDiagnosticEvent.DroppedInvalidEdit;
786
+ default:
787
+ return SharedTreeDiagnosticEvent.DroppedMalformedEdit;
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Initializes a new `EditLog` and `CachingLogViewer` on this `SharedTree`, replacing and disposing of any previously existing ones.
793
+ * @returns the initialized values (this is mostly to keep the constructor happy)
794
+ */
795
+ private initializeNewEditLogFromSummary(
796
+ editHistory: EditLogSummary<ChangeInternal, EditHandle<ChangeInternal>>,
797
+ currentTree: ChangeNode | undefined,
798
+ idCompressor: IdCompressor,
799
+ editStatusCallback: EditStatusCallback,
800
+ sequencedEditResultCallback: SequencedEditResultCallback,
801
+ version: WriteFormat
802
+ ): { editLog: EditLog<ChangeInternal>; cachingLogViewer: CachingLogViewer } {
803
+ this.idCompressor = idCompressor;
804
+ // Dispose the current log viewer if it exists. This ensures that re-used EditAddedHandlers below don't retain references to old
805
+ // log viewers.
806
+ this.cachingLogViewer?.detachFromEditLog();
807
+ const indexOfFirstEditInSession =
808
+ version === WriteFormat.v0_0_2 || (editHistory?.editIds.length === 1 && version === WriteFormat.v0_1_1)
809
+ ? 0
810
+ : editHistory?.editIds.length;
811
+
812
+ // Use previously registered EditAddedHandlers if there is an existing EditLog.
813
+ const editLog = new EditLog(
814
+ editHistory,
815
+ this.logger,
816
+ this.editLog?.editAddedHandlers,
817
+ indexOfFirstEditInSession
818
+ );
819
+
820
+ editLog.on(SharedTreeDiagnosticEvent.UnexpectedHistoryChunk, () => {
821
+ this.emit(SharedTreeDiagnosticEvent.UnexpectedHistoryChunk);
822
+ });
823
+
824
+ let knownRevisions: [number, EditCacheEntry][] | undefined;
825
+ if (currentTree !== undefined) {
826
+ const currentView = RevisionView.fromTree(currentTree);
827
+ // TODO:#47830: Store multiple checkpoints in summary.
828
+ knownRevisions = [[editLog.length, { view: currentView }]];
829
+ }
830
+
831
+ const logViewer = new CachingLogViewer(
832
+ editLog,
833
+ RevisionView.fromTree(initialTree, this),
834
+ knownRevisions,
835
+ this.expensiveValidation,
836
+ editStatusCallback,
837
+ sequencedEditResultCallback,
838
+ this.transactionFactory,
839
+ 0
840
+ );
841
+
842
+ this.editLog = editLog;
843
+ this.cachingLogViewer = logViewer;
844
+ return { editLog, cachingLogViewer: logViewer };
845
+ }
846
+
847
+ /**
848
+ * Upload any full chunks that have yet to be uploaded.
849
+ */
850
+ private uploadCatchUpBlobs(): void {
851
+ if (this.writeFormat !== WriteFormat.v0_0_2 && this.uploadEditChunks) {
852
+ for (const [startRevision, chunk] of this.editLog.getEditChunksReadyForUpload()) {
853
+ this.uploadEditChunk(chunk, startRevision)
854
+ .then(() => {
855
+ this.emit(SharedTreeDiagnosticEvent.CatchUpBlobUploaded);
856
+ this.logger.sendTelemetryEvent({ eventName: 'CatchUpBlobUpload', chunkSize: chunk.length });
857
+ })
858
+ // It is safe to swallow errors from edit chunk upload because the next summary load will
859
+ // do another attempt to upload the edit chunks that couldn't previously be uploaded
860
+ .catch((error) => {});
861
+ }
862
+ }
863
+ }
864
+
865
+ /**
866
+ * Compares this shared tree to another for equality. Should only be used for correctness testing.
867
+ *
868
+ * Equality means that the histories as captured by the EditLogs are equivalent.
869
+ *
870
+ * Equality does not include:
871
+ * - if an edit is open
872
+ * - the shared tree's id
873
+ * - local vs sequenced status of edits
874
+ * - registered event listeners
875
+ * - state of caches
876
+ *
877
+ * @internal
878
+ * */
879
+ public equals(sharedTree: SharedTree): boolean {
880
+ if (!areRevisionViewsSemanticallyEqual(this.currentView, this, sharedTree.currentView, sharedTree)) {
881
+ return false;
882
+ }
883
+
884
+ return this.editLog.equals(sharedTree.editLog);
885
+ }
886
+
887
+ /**
888
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
889
+ */
890
+ protected async loadCore(storage: IChannelStorageService): Promise<void> {
891
+ const summaryLoadPerformanceEvent = PerformanceEvent.start(this.logger, { eventName: 'SummaryLoad' });
892
+
893
+ try {
894
+ const newBlob = await storage.readBlob(snapshotFileName);
895
+ const blobData = bufferToString(newBlob, 'utf8');
896
+
897
+ const stats = this.loadSerializedSummary(blobData);
898
+
899
+ summaryLoadPerformanceEvent.end(stats);
900
+ } catch (error) {
901
+ summaryLoadPerformanceEvent.cancel({ eventName: 'SummaryLoadFailure' }, error);
902
+ throw error;
903
+ }
904
+ }
905
+
906
+ /**
907
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.processCore}
908
+ */
909
+ protected processCore(message: unknown, local: boolean): void {
910
+ const typedMessage = message as Omit<ISequencedDocumentMessage, 'contents'> & {
911
+ contents: SharedTreeOp_0_0_2 | SharedTreeOp;
912
+ };
913
+ this.cachingLogViewer.setMinimumSequenceNumber(typedMessage.minimumSequenceNumber);
914
+ const op = typedMessage.contents;
915
+ const { type, version } = op;
916
+ const resolvedVersion = version ?? WriteFormat.v0_0_2;
917
+ const sameVersion = resolvedVersion === this.writeFormat;
918
+
919
+ // Edit and handle ops should only be processed if they're the same version as the tree write version.
920
+ // Update ops should only be processed if they're not the same version.
921
+ if (sameVersion) {
922
+ if (type === SharedTreeOpType.Handle) {
923
+ const { editHandle, startRevision } = op as SharedTreeHandleOp;
924
+ const baseHandle = this.deserializeHandle(editHandle);
925
+ const decodedHandle: EditHandle<ChangeInternal> = {
926
+ get: async () => {
927
+ const contents = await baseHandle.get();
928
+ const parsedContents: EditChunkContents = JSON.parse(IsoBuffer.from(contents).toString());
929
+ return this.encoder_0_1_1.decodeEditChunk(
930
+ parsedContents,
931
+ sequencedIdNormalizer(this.idNormalizer),
932
+ this.interner
933
+ );
934
+ },
935
+ baseHandle,
936
+ };
937
+ this.editLog.processEditChunkHandle(decodedHandle, startRevision);
938
+ } else if (type === SharedTreeOpType.Edit) {
939
+ if (op.version === WriteFormat.v0_1_1) {
940
+ // TODO: This cast can be removed on typescript 4.6
941
+ this.idCompressor.finalizeCreationRange((op as SharedTreeEditOp).idRange);
942
+ }
943
+ // TODO: This cast can be removed on typescript 4.6
944
+ const edit = this.parseSequencedEdit(op as SharedTreeEditOp | SharedTreeEditOp_0_0_2);
945
+ this.internStringsFromEdit(edit);
946
+ this.processSequencedEdit(edit, typedMessage);
947
+ }
948
+ } else if (type === SharedTreeOpType.Update) {
949
+ this.processVersionUpdate(op.version);
950
+ } else if (compareSummaryFormatVersions(resolvedVersion, this.writeFormat) === 1) {
951
+ // An op version newer than our current version should not be received. If this happens, either an
952
+ // incorrect op version has been written or an update op was skipped.
953
+ const error = 'Newer op version received by a client that has yet to be updated.';
954
+ this.logger.sendErrorEvent(
955
+ {
956
+ eventName: 'UnexpectedNewerOpVersion',
957
+ },
958
+ error
959
+ );
960
+ fail(error);
961
+ }
962
+ }
963
+
964
+ /**
965
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.registerCore}
966
+ */
967
+ protected registerCore(): void {
968
+ // Do nothing
969
+ }
970
+
971
+ /**
972
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.onDisconnect}
973
+ */
974
+ protected onDisconnect(): void {
975
+ // Do nothing
976
+ }
977
+
978
+ /**
979
+ * Parses a sequenced edit. This is only invoked for ops with version matching the current `writeFormat`.
980
+ */
981
+ private parseSequencedEdit(op: SharedTreeEditOp | SharedTreeEditOp_0_0_2): Edit<ChangeInternal> {
982
+ // TODO:Type Safety: Improve type safety around op sending/parsing (e.g. discriminated union over version field somehow)
983
+ switch (op.version) {
984
+ case WriteFormat.v0_0_2:
985
+ return this.encoder_0_0_2.decodeEditOp(op, this.encodeSemiSerializedEdit.bind(this), this);
986
+ case WriteFormat.v0_1_1:
987
+ return this.encoder_0_1_1.decodeEditOp(
988
+ op,
989
+ this.encodeSemiSerializedEdit.bind(this),
990
+ this.idNormalizer,
991
+ this.interner
992
+ );
993
+ default:
994
+ fail('Unknown op version');
995
+ }
996
+ }
997
+
998
+ private encodeSemiSerializedEdit<T>(semiSerializedEdit: Edit<T>): Edit<T> {
999
+ // semiSerializedEdit may have handles which have been replaced by `serializer.encode`.
1000
+ // Since there is no API to un-replace them except via parse, re-stringify the edit, then parse it.
1001
+ // Stringify using JSON, not IFluidSerializer since OPs use JSON directly.
1002
+ // TODO:Performance:#48025: Avoid this serialization round trip.
1003
+ const encodedEdit: Edit<T> = this.serializer.parse(JSON.stringify(semiSerializedEdit));
1004
+ return encodedEdit;
1005
+ }
1006
+
1007
+ private processSequencedEdit(edit: Edit<ChangeInternal>, message: ISequencedDocumentMessage): void {
1008
+ const { id: editId } = edit;
1009
+ const wasLocalEdit = this.editLog.isLocalEdit(editId);
1010
+
1011
+ // If the id of the supplied edit matches a non-local edit already present in the log, this would normally be indicative of an error.
1012
+ // However, the @fluidframework packages prior to 0.37.x have a bug which can cause data corruption by sequencing duplicate edits--
1013
+ // see discussion on the following github issue: https://github.com/microsoft/FluidFramework/issues/4399
1014
+ // To work around this issue, we currently tolerate duplicate ops in loaded documents.
1015
+ // This could be strengthened in the future to only apply to documents which may have been impacted.
1016
+ const shouldIgnoreEdit = this.editLog.tryGetIndexOfId(editId) !== undefined && !wasLocalEdit;
1017
+ if (shouldIgnoreEdit) {
1018
+ return;
1019
+ }
1020
+
1021
+ if (wasLocalEdit) {
1022
+ this.editLog.addSequencedEdit(edit, message);
1023
+ // If this client created the edit that filled up a chunk, it is responsible for uploading that chunk.
1024
+ if (compareSummaryFormatVersions(this.writeFormat, WriteFormat.v0_0_2) > 0 && this.uploadEditChunks) {
1025
+ const lastPair = this.editLog.getLastEditChunk();
1026
+ if (lastPair !== undefined) {
1027
+ const [startRevision, chunk] = lastPair;
1028
+ const edits = assertNotUndefined(chunk.edits);
1029
+ if (edits.length === this.editLog.editsPerChunk) {
1030
+ this.uploadEditChunk(edits, startRevision)
1031
+ .then(() => {
1032
+ this.logger.sendTelemetryEvent({
1033
+ eventName: 'EditChunkUpload',
1034
+ chunkSize: edits.length,
1035
+ });
1036
+ })
1037
+ // It is safe to swallow errors from edit chunk upload because the next summary load will
1038
+ // do another attempt to upload the edit chunks that couldn't previously be uploaded
1039
+ .catch((error) => {});
1040
+ }
1041
+ }
1042
+ }
1043
+ } else {
1044
+ this.applyEditLocally(edit, message);
1045
+ }
1046
+ }
1047
+
1048
+ /**
1049
+ * Updates SharedTree to the provided version if the version is a valid write version newer than the current version.
1050
+ * @param version - The version to update to.
1051
+ */
1052
+ private processVersionUpdate(version: WriteFormat) {
1053
+ if (isUpdateRequired(this.writeFormat, version)) {
1054
+ PerformanceEvent.timedExec(
1055
+ this.logger,
1056
+ { eventName: 'VersionUpdate', version },
1057
+ () => {
1058
+ if (compareSummaryFormatVersions(version, WriteFormat.v0_1_1) >= 0) {
1059
+ this.upgradeFrom_0_0_2_to_0_1_1();
1060
+ } else {
1061
+ throw new Error(`Updating to version ${version} is not supported.`);
1062
+ }
1063
+
1064
+ this.changeWriteFormat(version);
1065
+
1066
+ // The edit log may contain some local edits submitted after the version update op was submitted but
1067
+ // before we receive the message it has been sequenced. Since these edits must be sequenced after the version
1068
+ // update op, they will be discarded. These edits are then re-submitted using the new format.
1069
+ for (const edit of this.editLog.getLocalEdits()) {
1070
+ this.submitEditOp(edit);
1071
+ }
1072
+
1073
+ if (this.currentIsOldest) {
1074
+ this.uploadCatchUpBlobs();
1075
+ }
1076
+ },
1077
+ {
1078
+ end: true,
1079
+ cancel: 'error',
1080
+ }
1081
+ );
1082
+ }
1083
+ }
1084
+
1085
+ private upgradeFrom_0_0_2_to_0_1_1(): void {
1086
+ for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
1087
+ this.internStringsFromEdit(this.editsInternal.getEditInSessionAtIndex(i));
1088
+ }
1089
+ const oldIdCompressor = this.idCompressor;
1090
+ // Create the IdCompressor that will be used after the upgrade
1091
+ const newIdCompressor = new IdCompressor(createSessionId(), reservedIdCount); // TODO: attribution info
1092
+ const newContext = getNodeIdContext(newIdCompressor);
1093
+ // Generate all local IDs in the new compressor that were in the old compressor and preserve their UUIDs.
1094
+ // This will allow the client to continue to use local IDs that were allocated pre-upgrade
1095
+ for (const localId of oldIdCompressor.getAllIdsFromLocalSession()) {
1096
+ newIdCompressor.generateCompressedId(oldIdCompressor.decompress(localId));
1097
+ }
1098
+
1099
+ const unifyHistoricalIds = (context: NodeIdContext): void => {
1100
+ for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
1101
+ const edit = this.editsInternal.getEditInSessionAtIndex(i);
1102
+ convertEditIds(edit, (id) => context.generateNodeId(this.convertToStableNodeId(id)));
1103
+ }
1104
+ };
1105
+ // Construct a temporary "ghost" compressor which is used to generate final IDs that will be consistent across all upgrading clients
1106
+ const ghostIdCompressor = new IdCompressor(ghostSessionId, reservedIdCount); // TODO: attribution info
1107
+ const ghostContext = getNodeIdContext(ghostIdCompressor);
1108
+ if (this.summarizeHistory) {
1109
+ // All clients have the full history, and can therefore all "generate" the same final IDs for every ID in the history
1110
+ // via the ghost compressor.
1111
+ unifyHistoricalIds(ghostContext);
1112
+ } else {
1113
+ // Clients do not have the full history, but all share the same current view (sequenced). They can all finalize the same final
1114
+ // IDs for every ID in the view via the ghost compressor.
1115
+ for (const node of this.logViewer.getRevisionViewInSession(this.editLog.numberOfSequencedEdits)) {
1116
+ ghostContext.generateNodeId(this.convertToStableNodeId(node.identifier));
1117
+ }
1118
+ // Every node in this client's history can simply be generated in the new compressor as well, preserving the UUID
1119
+ unifyHistoricalIds(newContext);
1120
+ }
1121
+ // Finalize any IDs in the ghost compressor into the actual compressor. This simulates all clients reaching a consensus on those IDs
1122
+ newIdCompressor.finalizeCreationRange(ghostIdCompressor.takeNextCreationRange());
1123
+ this.idCompressor = newIdCompressor;
1124
+ }
1125
+
1126
+ /**
1127
+ * Add an `Edit` directly.
1128
+ * External users should use one of the more specialized functions, like applyEdit which handles constructing the actual `Edit` object.
1129
+ * This is exposed as it is useful for testing, particularly with invalid and malformed Edits.
1130
+ * @internal
1131
+ */
1132
+ public applyEdit(...changes: readonly Change[]): Edit<unknown> {
1133
+ const id = newEditId();
1134
+ const internalEdit: Edit<ChangeInternal> = {
1135
+ id,
1136
+ changes: changes.map((c) => this.internalizeChange(c)),
1137
+ };
1138
+ this.submitEditOp(internalEdit);
1139
+ this.applyEditLocally(internalEdit, undefined);
1140
+ return internalEdit;
1141
+ }
1142
+
1143
+ /**
1144
+ * @internal
1145
+ */
1146
+ public applyEditInternal(editOrChanges: Edit<ChangeInternal> | readonly ChangeInternal[]): Edit<ChangeInternal> {
1147
+ let edit: Edit<ChangeInternal>;
1148
+ if (Array.isArray(editOrChanges)) {
1149
+ const id = newEditId();
1150
+ edit = { id, changes: editOrChanges };
1151
+ } else {
1152
+ edit = editOrChanges as Edit<ChangeInternal>;
1153
+ }
1154
+ this.submitEditOp(edit);
1155
+ this.applyEditLocally(edit, undefined);
1156
+ return edit;
1157
+ }
1158
+
1159
+ /**
1160
+ * Given a newly constructed edit, add any necessary metadata
1161
+ * @internal
1162
+ */
1163
+ public internalizeChange(change: Change): ChangeInternal {
1164
+ switch (change.type) {
1165
+ case ChangeType.Insert:
1166
+ return {
1167
+ source: change.source as DetachedSequenceId,
1168
+ destination: deepCloneStablePlace(change.destination),
1169
+ type: ChangeTypeInternal.Insert,
1170
+ };
1171
+ case ChangeType.Detach: {
1172
+ const detach: DetachInternal = {
1173
+ source: deepCloneStableRange(change.source),
1174
+ type: ChangeTypeInternal.Detach,
1175
+ };
1176
+ copyPropertyIfDefined(change, detach, 'destination');
1177
+ return detach;
1178
+ }
1179
+ case ChangeType.Build: {
1180
+ if (isTreeNodeSequence(change.source)) {
1181
+ const source = change.source.map((buildNode) =>
1182
+ convertTreeNodes<BuildTreeNode, TreeNode<BuildNodeInternal, NodeId>, number>(
1183
+ buildNode,
1184
+ (nodeData) => internalizeBuildNode(nodeData, this),
1185
+ (x): x is number => typeof x === 'number'
1186
+ )
1187
+ ) as TreeNodeSequence<TreeNode<BuildNodeInternal, NodeId> | DetachedSequenceId>;
1188
+ return {
1189
+ source,
1190
+ destination: change.destination as DetachedSequenceId,
1191
+ type: ChangeTypeInternal.Build,
1192
+ };
1193
+ } else {
1194
+ const source = convertTreeNodes<BuildTreeNode, TreeNode<BuildNodeInternal, NodeId>, number>(
1195
+ change.source,
1196
+ (nodeData) => internalizeBuildNode(nodeData, this),
1197
+ (x): x is number => typeof x === 'number'
1198
+ ) as TreeNode<BuildNodeInternal, NodeId> | DetachedSequenceId;
1199
+ return {
1200
+ source: [source],
1201
+ destination: change.destination as DetachedSequenceId,
1202
+ type: ChangeTypeInternal.Build,
1203
+ };
1204
+ }
1205
+ }
1206
+ case ChangeType.SetValue:
1207
+ return {
1208
+ nodeToModify: change.nodeToModify,
1209
+ payload: change.payload,
1210
+ type: ChangeTypeInternal.SetValue,
1211
+ };
1212
+ case ChangeType.Constraint: {
1213
+ const constraint: ConstraintInternal = {
1214
+ effect: change.effect,
1215
+ toConstrain: change.toConstrain,
1216
+ type: ChangeTypeInternal.Constraint,
1217
+ };
1218
+ copyPropertyIfDefined(change, constraint, 'contentHash');
1219
+ copyPropertyIfDefined(change, constraint, 'identityHash');
1220
+ copyPropertyIfDefined(change, constraint, 'label');
1221
+ copyPropertyIfDefined(change, constraint, 'length');
1222
+ copyPropertyIfDefined(change, constraint, 'parentNode');
1223
+ return constraint;
1224
+ }
1225
+ default:
1226
+ fail('unexpected change type');
1227
+ }
1228
+ }
1229
+
1230
+ private applyEditLocally(edit: Edit<ChangeInternal>, message: ISequencedDocumentMessage | undefined): void {
1231
+ const isSequenced = message !== undefined;
1232
+ if (isSequenced) {
1233
+ // TODO: This cast can be removed on typescript 4.6
1234
+ this.editLog.addSequencedEdit(edit, message as ISequencedDocumentMessage);
1235
+ } else {
1236
+ this.editLog.addLocalEdit(edit);
1237
+ }
1238
+
1239
+ const eventArguments: EditCommittedEventArguments = {
1240
+ editId: edit.id,
1241
+ local: !isSequenced,
1242
+ tree: this,
1243
+ };
1244
+ this.emit(SharedTreeEvent.EditCommitted, eventArguments);
1245
+ }
1246
+
1247
+ /**
1248
+ * Reverts a previous edit by applying a new edit containing the inverse of the original edit's changes.
1249
+ * @param editId - the edit to revert
1250
+ * @returns the id of the new edit, or undefined if the original edit could not be inverted given the current tree state.
1251
+ * @public
1252
+ */
1253
+ public revert(editId: EditId): EditId | undefined {
1254
+ const index = this.edits.getIndexOfId(editId);
1255
+ const edit = this.editLog.getEditInSessionAtIndex(index);
1256
+ const before = this.logViewer.getRevisionViewInSession(index);
1257
+ const changes = this.revertChanges(edit.changes, before);
1258
+ if (changes === undefined) {
1259
+ return undefined;
1260
+ }
1261
+
1262
+ return this.applyEditInternal(changes).id;
1263
+ }
1264
+
1265
+ /**
1266
+ * Revert the given changes
1267
+ * @param changes - the changes to revert
1268
+ * @param before - the revision view before the changes were originally applied
1269
+ * @returns the inverse of `changes` or undefined if the changes could not be inverted for the given tree state.
1270
+ * @internal
1271
+ */
1272
+ public revertChanges(changes: readonly ChangeInternal[], before: RevisionView): ChangeInternal[] | undefined {
1273
+ return revert(changes, before);
1274
+ }
1275
+
1276
+ /**
1277
+ * Submits an edit by the local client to the runtime.
1278
+ */
1279
+ private submitEditOp(edit: Edit<ChangeInternal>): void {
1280
+ // Only submit ops if attached, since op submission can have stateful side effects (e.g. changing the IdCompressor)
1281
+ // Ops will be submitted again when attached (see loadSummary())
1282
+ if (this.isAttached()) {
1283
+ switch (this.writeFormat) {
1284
+ case WriteFormat.v0_0_2:
1285
+ this.submitOp(this.encoder_0_0_2.encodeEditOp(edit, this.serializeEdit.bind(this), this));
1286
+ break;
1287
+ case WriteFormat.v0_1_1:
1288
+ this.submitOp(
1289
+ this.encoder_0_1_1.encodeEditOp(
1290
+ edit,
1291
+ this.serializeEdit.bind(this),
1292
+ this.idCompressor.takeNextCreationRange(),
1293
+ this.idNormalizer,
1294
+ this.interner
1295
+ )
1296
+ );
1297
+ break;
1298
+ default:
1299
+ fail('Unknown version');
1300
+ }
1301
+ }
1302
+ }
1303
+
1304
+ private serializeEdit<TChange>(preparedEdit: Edit<TChange>): Edit<TChange> {
1305
+ return this.serializer.encode(preparedEdit, this.handle) as Edit<TChange>;
1306
+ }
1307
+
1308
+ /** A type-safe `submitLocalMessage` wrapper to enforce op format */
1309
+ private submitOp(content: SharedTreeOp | SharedTreeOp_0_0_2, localOpMetadata: unknown = undefined): void {
1310
+ assert(
1311
+ compareSummaryFormatVersions(content.version, this.writeFormat) === 0,
1312
+ 'Attempted to submit op of wrong version'
1313
+ );
1314
+ this.submitLocalMessage(content, localOpMetadata);
1315
+ }
1316
+
1317
+ public getRuntime(): IFluidDataStoreRuntime {
1318
+ return this.runtime;
1319
+ }
1320
+
1321
+ /**
1322
+ * "Pending local state" refers to ops submitted to the runtime that have not yet been acked.
1323
+ * When closing a container, hosts have the option to stash this pending local state somewhere to be reapplied
1324
+ * later (to avoid data loss).
1325
+ * If a host then loads a container using that stashed state, this function is called for each stashed op, and is expected to:
1326
+ * 1. Update this DDS to reflect that state locally.
1327
+ * 2. Return any `localOpMetadata` that would have been associated with this op.
1328
+ *
1329
+ * @param content - op to apply locally.
1330
+ */
1331
+ protected applyStashedOp(op: unknown): void {
1332
+ const sharedTreeOp = op as SharedTreeOp | SharedTreeOp_0_0_2;
1333
+ switch (sharedTreeOp.type) {
1334
+ case SharedTreeOpType.Edit: {
1335
+ switch (this.writeFormat) {
1336
+ case WriteFormat.v0_0_2:
1337
+ switch (sharedTreeOp.version) {
1338
+ case WriteFormat.v0_0_2: {
1339
+ const edit = this.parseSequencedEdit(sharedTreeOp);
1340
+ this.applyEditLocally(edit, undefined);
1341
+ break;
1342
+ }
1343
+ case WriteFormat.v0_1_1:
1344
+ // TODO:#74390: Implement
1345
+ fail('Received stashed op 0.1.1 before upgrade');
1346
+ default:
1347
+ fail('Unknown version');
1348
+ }
1349
+ break;
1350
+ case WriteFormat.v0_1_1:
1351
+ switch (sharedTreeOp.version) {
1352
+ case WriteFormat.v0_0_2:
1353
+ // TODO:#74390: Implement
1354
+ fail('v0.1.1 does not support stashed ops.');
1355
+ case WriteFormat.v0_1_1:
1356
+ // TODO:#74390: Implement
1357
+ fail('v0.1.1 does not support stashed ops.');
1358
+ default:
1359
+ fail('Unknown version');
1360
+ }
1361
+ default:
1362
+ fail('Unknown version');
1363
+ }
1364
+ break;
1365
+ }
1366
+ // Handle and update ops are only acknowledged by the client that generated them upon sequencing--no local changes necessary.
1367
+ case SharedTreeOpType.Handle:
1368
+ case SharedTreeOpType.Update:
1369
+ case SharedTreeOpType.NoOp:
1370
+ break;
1371
+ default:
1372
+ fail('Unrecognized op');
1373
+ }
1374
+ }
1375
+
1376
+ private changeWriteFormat(newFormat: WriteFormat): void {
1377
+ this.writeFormat = newFormat;
1378
+ this.emit(SharedTreeDiagnosticEvent.WriteVersionChanged, newFormat);
1379
+ }
1380
+
1381
+ /**
1382
+ * Interns all Definitions and TraitLabel_s referenced by the provided edit.
1383
+ *
1384
+ * Clients must have consensus on the interned values to guarantee the interned ID is valid.
1385
+ */
1386
+ private internStringsFromEdit(edit: Edit<ChangeInternal>): void {
1387
+ for (const change of edit.changes) {
1388
+ if (change.type === ChangeTypeInternal.Build) {
1389
+ for (const root of change.source) {
1390
+ walkTree<TreeNode<BuildNodeInternal, NodeId>, DetachedSequenceId>(
1391
+ root,
1392
+ (node) => {
1393
+ this.interner.getOrCreateInternedId(node.definition);
1394
+ for (const trait of Object.keys(node.traits)) {
1395
+ this.interner.getOrCreateInternedId(trait);
1396
+ }
1397
+ },
1398
+ isDetachedSequenceId
1399
+ );
1400
+ }
1401
+ } else if (change.type === ChangeTypeInternal.Insert) {
1402
+ const { referenceTrait } = change.destination;
1403
+ if (referenceTrait !== undefined) {
1404
+ this.interner.getOrCreateInternedId(referenceTrait.label);
1405
+ }
1406
+ }
1407
+ }
1408
+ }
1409
+ }
1410
+
1411
+ /**
1412
+ * @returns 1 if versionA is newer, -1 if versionB is newer, and 0 if the versions are the same.
1413
+ * @throws if either version isn't a valid WriteFormat version.
1414
+ */
1415
+ function compareSummaryFormatVersions(versionA: string, versionB: string): number {
1416
+ const versionAIndex = sortedWriteVersions.indexOf(versionA as WriteFormat);
1417
+ const versionBIndex = sortedWriteVersions.indexOf(versionB as WriteFormat);
1418
+
1419
+ if (versionAIndex === -1 || versionBIndex === -1) {
1420
+ fail('Summary version being compared cannot be read.');
1421
+ }
1422
+
1423
+ if (versionAIndex < versionBIndex) {
1424
+ return -1;
1425
+ } else if (versionAIndex > versionBIndex) {
1426
+ return 1;
1427
+ }
1428
+
1429
+ return 0;
1430
+ }
1431
+
1432
+ /**
1433
+ * Checks if the summary version needs to be updated.
1434
+ * @returns true if the old version is older than the new version.
1435
+ * @throws if the new version isn't a supported WriteFormat version.
1436
+ */
1437
+ function isUpdateRequired(oldVersion: string, newVersion: string): boolean {
1438
+ const newVersionIndex = sortedWriteVersions.indexOf(newVersion as WriteFormat);
1439
+ if (newVersionIndex === -1) {
1440
+ fail('New write version is invalid.');
1441
+ }
1442
+
1443
+ return compareSummaryFormatVersions(oldVersion, newVersion) === -1 ? true : false;
1444
+ }
1445
+
1446
+ function isTreeNodeSequence(source: TreeNodeSequence<BuildNode> | BuildNode): source is TreeNodeSequence<BuildNode> {
1447
+ return Array.isArray(source);
1448
+ }