@fluid-experimental/tree 0.58.2002 → 0.59.1000

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 (817) hide show
  1. package/README.md +159 -46
  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 -105
  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 +61 -32
  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 +129 -85
  59. package/dist/LogViewer.d.ts.map +1 -1
  60. package/dist/LogViewer.js +111 -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 +105 -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 +439 -0
  90. package/dist/SharedTree.d.ts.map +1 -0
  91. package/dist/SharedTree.js +1109 -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 +52 -0
  114. package/dist/Transaction.d.ts.map +1 -0
  115. package/dist/Transaction.js +72 -0
  116. package/dist/Transaction.js.map +1 -0
  117. package/dist/TransactionInternal.d.ts +540 -0
  118. package/dist/TransactionInternal.d.ts.map +1 -0
  119. package/dist/TransactionInternal.js +626 -0
  120. package/dist/TransactionInternal.js.map +1 -0
  121. package/dist/TreeCompressor.d.ts +36 -0
  122. package/dist/TreeCompressor.d.ts.map +1 -0
  123. package/dist/TreeCompressor.js +137 -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 +218 -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/docs/Write-Format.md +19 -0
  190. package/lib/ChangeCompression.d.ts +39 -0
  191. package/lib/ChangeCompression.d.ts.map +1 -0
  192. package/lib/ChangeCompression.js +111 -0
  193. package/lib/ChangeCompression.js.map +1 -0
  194. package/{dist/default-edits/PersistedTypes.d.ts → lib/ChangeTypes.d.ts} +58 -105
  195. package/lib/ChangeTypes.d.ts.map +1 -0
  196. package/lib/{default-edits/PersistedTypes.js → ChangeTypes.js} +15 -68
  197. package/lib/ChangeTypes.js.map +1 -0
  198. package/lib/Checkout.d.ts +39 -27
  199. package/lib/Checkout.d.ts.map +1 -1
  200. package/lib/Checkout.js +53 -24
  201. package/lib/Checkout.js.map +1 -1
  202. package/lib/Common.d.ts +175 -38
  203. package/lib/Common.d.ts.map +1 -1
  204. package/lib/Common.js +226 -101
  205. package/lib/Common.js.map +1 -1
  206. package/lib/EagerCheckout.d.ts +24 -0
  207. package/lib/EagerCheckout.d.ts.map +1 -0
  208. package/lib/{BasicCheckout.js → EagerCheckout.js} +7 -4
  209. package/lib/EagerCheckout.js.map +1 -0
  210. package/lib/EditLog.d.ts +77 -63
  211. package/lib/EditLog.d.ts.map +1 -1
  212. package/lib/EditLog.js +83 -47
  213. package/lib/EditLog.js.map +1 -1
  214. package/lib/EditUtilities.d.ts +168 -0
  215. package/lib/EditUtilities.d.ts.map +1 -0
  216. package/lib/EditUtilities.js +353 -0
  217. package/lib/EditUtilities.js.map +1 -0
  218. package/lib/EventTypes.d.ts +73 -0
  219. package/lib/EventTypes.d.ts.map +1 -0
  220. package/lib/EventTypes.js +75 -0
  221. package/lib/EventTypes.js.map +1 -0
  222. package/lib/Forest.d.ts +29 -7
  223. package/lib/Forest.d.ts.map +1 -1
  224. package/lib/Forest.js +58 -35
  225. package/lib/Forest.js.map +1 -1
  226. package/lib/HistoryEditFactory.d.ts +20 -0
  227. package/lib/HistoryEditFactory.d.ts.map +1 -0
  228. package/lib/{default-edits/HistoryEditFactory.js → HistoryEditFactory.js} +78 -39
  229. package/lib/HistoryEditFactory.js.map +1 -0
  230. package/lib/IdConversion.d.ts +12 -0
  231. package/lib/IdConversion.d.ts.map +1 -0
  232. package/lib/IdConversion.js +91 -0
  233. package/lib/IdConversion.js.map +1 -0
  234. package/lib/Identifiers.d.ts +89 -2
  235. package/lib/Identifiers.d.ts.map +1 -1
  236. package/lib/Identifiers.js +8 -1
  237. package/lib/Identifiers.js.map +1 -1
  238. package/lib/InitialTree.d.ts +2 -2
  239. package/lib/InitialTree.d.ts.map +1 -1
  240. package/lib/InitialTree.js +2 -1
  241. package/lib/InitialTree.js.map +1 -1
  242. package/lib/LazyCheckout.d.ts +28 -0
  243. package/lib/LazyCheckout.d.ts.map +1 -0
  244. package/lib/LazyCheckout.js +40 -0
  245. package/lib/LazyCheckout.js.map +1 -0
  246. package/lib/LogViewer.d.ts +129 -85
  247. package/lib/LogViewer.d.ts.map +1 -1
  248. package/lib/LogViewer.js +103 -77
  249. package/lib/LogViewer.js.map +1 -1
  250. package/lib/MergeHealth.d.ts +221 -0
  251. package/lib/MergeHealth.d.ts.map +1 -0
  252. package/lib/MergeHealth.js +258 -0
  253. package/lib/MergeHealth.js.map +1 -0
  254. package/lib/NodeIdUtilities.d.ts +105 -0
  255. package/lib/NodeIdUtilities.d.ts.map +1 -0
  256. package/lib/NodeIdUtilities.js +53 -0
  257. package/lib/NodeIdUtilities.js.map +1 -0
  258. package/lib/PayloadUtilities.d.ts +42 -0
  259. package/lib/PayloadUtilities.d.ts.map +1 -0
  260. package/lib/PayloadUtilities.js +110 -0
  261. package/lib/PayloadUtilities.js.map +1 -0
  262. package/lib/ReconciliationPath.d.ts +18 -13
  263. package/lib/ReconciliationPath.d.ts.map +1 -1
  264. package/lib/ReconciliationPath.js.map +1 -1
  265. package/lib/RevisionValueCache.d.ts +11 -2
  266. package/lib/RevisionValueCache.d.ts.map +1 -1
  267. package/lib/RevisionValueCache.js +2 -3
  268. package/lib/RevisionValueCache.js.map +1 -1
  269. package/lib/RevisionView.d.ts +83 -0
  270. package/lib/RevisionView.d.ts.map +1 -0
  271. package/lib/RevisionView.js +175 -0
  272. package/lib/RevisionView.js.map +1 -0
  273. package/lib/SerializationUtilities.d.ts +36 -0
  274. package/lib/SerializationUtilities.d.ts.map +1 -0
  275. package/lib/SerializationUtilities.js +95 -0
  276. package/lib/SerializationUtilities.js.map +1 -0
  277. package/lib/SharedTree.d.ts +439 -0
  278. package/lib/SharedTree.d.ts.map +1 -0
  279. package/lib/SharedTree.js +1104 -0
  280. package/lib/SharedTree.js.map +1 -0
  281. package/lib/SharedTreeEncoder.d.ts +102 -0
  282. package/lib/SharedTreeEncoder.d.ts.map +1 -0
  283. package/lib/SharedTreeEncoder.js +308 -0
  284. package/lib/SharedTreeEncoder.js.map +1 -0
  285. package/lib/StringInterner.d.ts +46 -0
  286. package/lib/StringInterner.d.ts.map +1 -0
  287. package/lib/StringInterner.js +57 -0
  288. package/lib/StringInterner.js.map +1 -0
  289. package/lib/Summary.d.ts +40 -0
  290. package/lib/Summary.d.ts.map +1 -0
  291. package/lib/Summary.js +19 -0
  292. package/lib/Summary.js.map +1 -0
  293. package/lib/SummaryBackCompatibility.d.ts +22 -22
  294. package/lib/SummaryBackCompatibility.d.ts.map +1 -1
  295. package/lib/SummaryBackCompatibility.js +29 -32
  296. package/lib/SummaryBackCompatibility.js.map +1 -1
  297. package/lib/SummaryTestUtilities.d.ts +31 -0
  298. package/lib/SummaryTestUtilities.d.ts.map +1 -0
  299. package/lib/SummaryTestUtilities.js +32 -0
  300. package/lib/SummaryTestUtilities.js.map +1 -0
  301. package/lib/Transaction.d.ts +52 -0
  302. package/lib/Transaction.d.ts.map +1 -0
  303. package/lib/Transaction.js +68 -0
  304. package/lib/Transaction.js.map +1 -0
  305. package/lib/TransactionInternal.d.ts +540 -0
  306. package/lib/TransactionInternal.d.ts.map +1 -0
  307. package/lib/TransactionInternal.js +622 -0
  308. package/lib/TransactionInternal.js.map +1 -0
  309. package/lib/TreeCompressor.d.ts +36 -0
  310. package/lib/TreeCompressor.d.ts.map +1 -0
  311. package/lib/TreeCompressor.js +133 -0
  312. package/lib/TreeCompressor.js.map +1 -0
  313. package/lib/TreeNodeHandle.d.ts +12 -18
  314. package/lib/TreeNodeHandle.d.ts.map +1 -1
  315. package/lib/TreeNodeHandle.js +14 -24
  316. package/lib/TreeNodeHandle.js.map +1 -1
  317. package/lib/TreeView.d.ts +166 -0
  318. package/lib/TreeView.d.ts.map +1 -0
  319. package/lib/TreeView.js +214 -0
  320. package/lib/TreeView.js.map +1 -0
  321. package/lib/TreeViewUtilities.d.ts +21 -0
  322. package/lib/TreeViewUtilities.d.ts.map +1 -0
  323. package/lib/TreeViewUtilities.js +71 -0
  324. package/lib/TreeViewUtilities.js.map +1 -0
  325. package/lib/{default-edits/UndoRedoHandler.d.ts → UndoRedoHandler.d.ts} +2 -2
  326. package/lib/UndoRedoHandler.d.ts.map +1 -0
  327. package/lib/{default-edits/UndoRedoHandler.js → UndoRedoHandler.js} +3 -7
  328. package/lib/UndoRedoHandler.js.map +1 -0
  329. package/lib/id-compressor/AppendOnlySortedMap.d.ts +127 -0
  330. package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -0
  331. package/lib/id-compressor/AppendOnlySortedMap.js +278 -0
  332. package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -0
  333. package/lib/id-compressor/IdCompressor.d.ts +389 -0
  334. package/lib/id-compressor/IdCompressor.d.ts.map +1 -0
  335. package/lib/id-compressor/IdCompressor.js +1343 -0
  336. package/lib/id-compressor/IdCompressor.js.map +1 -0
  337. package/lib/id-compressor/IdRange.d.ts +11 -0
  338. package/lib/id-compressor/IdRange.d.ts.map +1 -0
  339. package/lib/id-compressor/IdRange.js +25 -0
  340. package/lib/id-compressor/IdRange.js.map +1 -0
  341. package/lib/id-compressor/NumericUuid.d.ts +63 -0
  342. package/lib/id-compressor/NumericUuid.d.ts.map +1 -0
  343. package/lib/id-compressor/NumericUuid.js +365 -0
  344. package/lib/id-compressor/NumericUuid.js.map +1 -0
  345. package/lib/id-compressor/index.d.ts +12 -0
  346. package/lib/id-compressor/index.d.ts.map +1 -0
  347. package/lib/id-compressor/index.js +12 -0
  348. package/lib/id-compressor/index.js.map +1 -0
  349. package/lib/id-compressor/persisted-types/0.0.1.d.ts +156 -0
  350. package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -0
  351. package/lib/{test/Snapshot.tests.d.ts → id-compressor/persisted-types/0.0.1.js} +1 -1
  352. package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -0
  353. package/lib/id-compressor/persisted-types/index.d.ts +6 -0
  354. package/lib/id-compressor/persisted-types/index.d.ts.map +1 -0
  355. package/lib/id-compressor/persisted-types/index.js +6 -0
  356. package/lib/id-compressor/persisted-types/index.js.map +1 -0
  357. package/lib/index.d.ts +29 -9
  358. package/lib/index.d.ts.map +1 -1
  359. package/lib/index.js +23 -6
  360. package/lib/index.js.map +1 -1
  361. package/lib/persisted-types/0.0.2.d.ts +385 -0
  362. package/lib/persisted-types/0.0.2.d.ts.map +1 -0
  363. package/lib/persisted-types/0.0.2.js +110 -0
  364. package/lib/persisted-types/0.0.2.js.map +1 -0
  365. package/lib/persisted-types/0.1.1.d.ts +314 -0
  366. package/lib/persisted-types/0.1.1.d.ts.map +1 -0
  367. package/lib/persisted-types/0.1.1.js +150 -0
  368. package/lib/persisted-types/0.1.1.js.map +1 -0
  369. package/lib/persisted-types/index.d.ts +7 -0
  370. package/lib/persisted-types/index.d.ts.map +1 -0
  371. package/lib/persisted-types/index.js +8 -0
  372. package/lib/persisted-types/index.js.map +1 -0
  373. package/lib/test/AppendOnlySortedMap.tests.d.ts +6 -0
  374. package/lib/test/AppendOnlySortedMap.tests.d.ts.map +1 -0
  375. package/lib/test/AppendOnlySortedMap.tests.js +169 -0
  376. package/lib/test/AppendOnlySortedMap.tests.js.map +1 -0
  377. package/lib/test/{SnapshotUtilities.tests.d.ts → ChangeCompression.tests.d.ts} +1 -1
  378. package/lib/test/ChangeCompression.tests.d.ts.map +1 -0
  379. package/lib/test/ChangeCompression.tests.js +145 -0
  380. package/lib/test/ChangeCompression.tests.js.map +1 -0
  381. package/lib/test/Checkout.tests.d.ts +2 -3
  382. package/lib/test/Checkout.tests.d.ts.map +1 -1
  383. package/lib/test/Checkout.tests.js +126 -69
  384. package/lib/test/Checkout.tests.js.map +1 -1
  385. package/lib/test/Common.tests.js +60 -2
  386. package/lib/test/Common.tests.js.map +1 -1
  387. package/lib/test/{BasicCheckout.tests.d.ts → EagerCheckout.tests.d.ts} +1 -1
  388. package/lib/test/EagerCheckout.tests.d.ts.map +1 -0
  389. package/lib/test/EagerCheckout.tests.js +20 -0
  390. package/lib/test/EagerCheckout.tests.js.map +1 -0
  391. package/lib/test/Edit.tests.js +22 -14
  392. package/lib/test/Edit.tests.js.map +1 -1
  393. package/lib/test/{Anchors.glassBox.tests.d.ts → EditLog.perf.tests.d.ts} +1 -1
  394. package/lib/test/EditLog.perf.tests.d.ts.map +1 -0
  395. package/lib/test/EditLog.perf.tests.js +30 -0
  396. package/lib/test/EditLog.perf.tests.js.map +1 -0
  397. package/lib/test/EditLog.tests.js +10 -6
  398. package/lib/test/EditLog.tests.js.map +1 -1
  399. package/lib/test/EditUtilities.tests.d.ts +6 -0
  400. package/lib/test/EditUtilities.tests.d.ts.map +1 -0
  401. package/lib/test/EditUtilities.tests.js +503 -0
  402. package/lib/test/EditUtilities.tests.js.map +1 -0
  403. package/lib/test/Forest.perf.tests.d.ts +6 -0
  404. package/lib/test/Forest.perf.tests.d.ts.map +1 -0
  405. package/lib/test/Forest.perf.tests.js +133 -0
  406. package/lib/test/Forest.perf.tests.js.map +1 -0
  407. package/lib/test/Forest.tests.js +54 -27
  408. package/lib/test/Forest.tests.js.map +1 -1
  409. package/lib/test/GenericTransaction.tests.js +12 -3
  410. package/lib/test/GenericTransaction.tests.js.map +1 -1
  411. package/lib/test/HistoryEditFactory.tests.d.ts +6 -0
  412. package/lib/test/HistoryEditFactory.tests.d.ts.map +1 -0
  413. package/lib/test/HistoryEditFactory.tests.js +90 -0
  414. package/lib/test/HistoryEditFactory.tests.js.map +1 -0
  415. package/lib/test/IdCompressor.perf.tests.d.ts +6 -0
  416. package/lib/test/IdCompressor.perf.tests.d.ts.map +1 -0
  417. package/lib/test/IdCompressor.perf.tests.js +304 -0
  418. package/lib/test/IdCompressor.perf.tests.js.map +1 -0
  419. package/lib/test/IdCompressor.tests.d.ts +6 -0
  420. package/lib/test/IdCompressor.tests.d.ts.map +1 -0
  421. package/lib/test/IdCompressor.tests.js +1075 -0
  422. package/lib/test/IdCompressor.tests.js.map +1 -0
  423. package/lib/test/IdConversion.tests.d.ts +6 -0
  424. package/lib/test/IdConversion.tests.d.ts.map +1 -0
  425. package/lib/test/IdConversion.tests.js +36 -0
  426. package/lib/test/IdConversion.tests.js.map +1 -0
  427. package/lib/test/LazyCheckout.tests.d.ts +6 -0
  428. package/lib/test/LazyCheckout.tests.d.ts.map +1 -0
  429. package/lib/test/LazyCheckout.tests.js +22 -0
  430. package/lib/test/LazyCheckout.tests.js.map +1 -0
  431. package/lib/test/LogViewer.tests.js +276 -191
  432. package/lib/test/LogViewer.tests.js.map +1 -1
  433. package/lib/test/{SharedTreeWithAnchors.tests.d.ts → MergeHealthTelemetryHeartbeat.tests.d.ts} +1 -1
  434. package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts.map +1 -0
  435. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js +342 -0
  436. package/lib/test/MergeHealthTelemetryHeartbeat.tests.js.map +1 -0
  437. package/lib/test/NumericUuid.perf.tests.d.ts +6 -0
  438. package/lib/test/NumericUuid.perf.tests.d.ts.map +1 -0
  439. package/lib/test/NumericUuid.perf.tests.js +68 -0
  440. package/lib/test/NumericUuid.perf.tests.js.map +1 -0
  441. package/lib/test/NumericUuid.tests.d.ts +6 -0
  442. package/lib/test/NumericUuid.tests.d.ts.map +1 -0
  443. package/lib/test/NumericUuid.tests.js +191 -0
  444. package/lib/test/NumericUuid.tests.js.map +1 -0
  445. package/lib/test/RevisionView.tests.d.ts +6 -0
  446. package/lib/test/RevisionView.tests.d.ts.map +1 -0
  447. package/lib/test/RevisionView.tests.js +133 -0
  448. package/lib/test/RevisionView.tests.js.map +1 -0
  449. package/lib/test/SharedTree.perf.tests.d.ts +6 -0
  450. package/lib/test/SharedTree.perf.tests.d.ts.map +1 -0
  451. package/lib/test/SharedTree.perf.tests.js +39 -0
  452. package/lib/test/SharedTree.perf.tests.js.map +1 -0
  453. package/lib/test/SharedTree.tests.js +15 -3
  454. package/lib/test/SharedTree.tests.js.map +1 -1
  455. package/lib/test/StringInterner.tests.d.ts +6 -0
  456. package/lib/test/StringInterner.tests.d.ts.map +1 -0
  457. package/lib/test/StringInterner.tests.js +71 -0
  458. package/lib/test/StringInterner.tests.js.map +1 -0
  459. package/lib/test/Summary.tests.d.ts +8 -0
  460. package/lib/test/Summary.tests.d.ts.map +1 -0
  461. package/lib/test/Summary.tests.js +407 -0
  462. package/lib/test/Summary.tests.js.map +1 -0
  463. package/lib/test/Transaction.tests.js +76 -330
  464. package/lib/test/Transaction.tests.js.map +1 -1
  465. package/lib/test/TransactionInternal.tests.d.ts +6 -0
  466. package/lib/test/TransactionInternal.tests.d.ts.map +1 -0
  467. package/lib/test/TransactionInternal.tests.js +568 -0
  468. package/lib/test/TransactionInternal.tests.js.map +1 -0
  469. package/lib/test/TreeCompression.tests.d.ts +6 -0
  470. package/lib/test/TreeCompression.tests.d.ts.map +1 -0
  471. package/lib/test/TreeCompression.tests.js +292 -0
  472. package/lib/test/TreeCompression.tests.js.map +1 -0
  473. package/lib/test/TreeView.tests.d.ts +6 -0
  474. package/lib/test/TreeView.tests.d.ts.map +1 -0
  475. package/lib/test/TreeView.tests.js +176 -0
  476. package/lib/test/TreeView.tests.js.map +1 -0
  477. package/lib/test/UndoRedoHandler.tests.js +2 -2
  478. package/lib/test/UndoRedoHandler.tests.js.map +1 -1
  479. package/lib/test/Virtualization.tests.js +146 -62
  480. package/lib/test/Virtualization.tests.js.map +1 -1
  481. package/lib/test/fuzz/Generators.d.ts +19 -0
  482. package/lib/test/fuzz/Generators.d.ts.map +1 -0
  483. package/lib/test/fuzz/Generators.js +420 -0
  484. package/lib/test/fuzz/Generators.js.map +1 -0
  485. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +20 -0
  486. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -0
  487. package/lib/test/fuzz/SharedTreeFuzzTests.js +217 -0
  488. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -0
  489. package/lib/test/fuzz/Types.d.ts +133 -0
  490. package/lib/test/fuzz/Types.d.ts.map +1 -0
  491. package/lib/test/{GenericTransactionWithAnchors.tests.d.ts → fuzz/Types.js} +2 -2
  492. package/lib/test/fuzz/Types.js.map +1 -0
  493. package/lib/test/utilities/IdCompressorTestUtilities.d.ts +180 -0
  494. package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -0
  495. package/lib/test/utilities/IdCompressorTestUtilities.js +528 -0
  496. package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -0
  497. package/lib/test/utilities/MockTransaction.d.ts +26 -7
  498. package/lib/test/utilities/MockTransaction.d.ts.map +1 -1
  499. package/lib/test/utilities/MockTransaction.js +40 -11
  500. package/lib/test/utilities/MockTransaction.js.map +1 -1
  501. package/lib/test/utilities/PendingLocalStateTests.d.ts +12 -0
  502. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -0
  503. package/lib/test/utilities/PendingLocalStateTests.js +105 -0
  504. package/lib/test/utilities/PendingLocalStateTests.js.map +1 -0
  505. package/lib/test/utilities/SharedTreeTests.d.ts +3 -4
  506. package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
  507. package/lib/test/utilities/SharedTreeTests.js +696 -439
  508. package/lib/test/utilities/SharedTreeTests.js.map +1 -1
  509. package/lib/test/utilities/SharedTreeVersioningTests.d.ts +11 -0
  510. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -0
  511. package/lib/test/utilities/SharedTreeVersioningTests.js +370 -0
  512. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -0
  513. package/lib/test/utilities/SummaryLoadPerfTests.d.ts +10 -0
  514. package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -0
  515. package/lib/test/utilities/SummaryLoadPerfTests.js +102 -0
  516. package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -0
  517. package/lib/test/utilities/SummarySizeTests.d.ts +11 -0
  518. package/lib/test/utilities/SummarySizeTests.d.ts.map +1 -0
  519. package/lib/test/utilities/SummarySizeTests.js +158 -0
  520. package/lib/test/utilities/SummarySizeTests.js.map +1 -0
  521. package/lib/test/utilities/TestCommon.d.ts +9 -0
  522. package/lib/test/utilities/TestCommon.d.ts.map +1 -0
  523. package/lib/test/utilities/TestCommon.js +13 -0
  524. package/lib/test/utilities/TestCommon.js.map +1 -0
  525. package/lib/test/utilities/TestNode.d.ts +140 -0
  526. package/lib/test/utilities/TestNode.d.ts.map +1 -0
  527. package/lib/test/utilities/TestNode.js +292 -0
  528. package/lib/test/utilities/TestNode.js.map +1 -0
  529. package/lib/test/utilities/TestUtilities.d.ts +84 -70
  530. package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
  531. package/lib/test/utilities/TestUtilities.js +218 -143
  532. package/lib/test/utilities/TestUtilities.js.map +1 -1
  533. package/lib/test/utilities/UndoRedoTests.d.ts +4 -5
  534. package/lib/test/utilities/UndoRedoTests.d.ts.map +1 -1
  535. package/lib/test/utilities/UndoRedoTests.js +138 -149
  536. package/lib/test/utilities/UndoRedoTests.js.map +1 -1
  537. package/package.json +22 -17
  538. package/src/ChangeCompression.ts +159 -0
  539. package/src/{default-edits/PersistedTypes.ts → ChangeTypes.ts} +62 -125
  540. package/src/Checkout.ts +82 -53
  541. package/src/Common.ts +317 -117
  542. package/src/EagerCheckout.ts +38 -0
  543. package/src/EditLog.ts +153 -100
  544. package/src/EditUtilities.ts +559 -0
  545. package/src/EventTypes.ts +74 -0
  546. package/src/Forest.ts +81 -73
  547. package/src/{default-edits/HistoryEditFactory.ts → HistoryEditFactory.ts} +103 -53
  548. package/src/IdConversion.ts +125 -0
  549. package/src/Identifiers.ts +101 -1
  550. package/src/InitialTree.ts +5 -4
  551. package/src/LazyCheckout.ts +51 -0
  552. package/src/LogViewer.ts +242 -166
  553. package/src/MergeHealth.ts +447 -0
  554. package/src/NodeIdUtilities.ts +156 -0
  555. package/src/PayloadUtilities.ts +124 -0
  556. package/src/ReconciliationPath.ts +18 -13
  557. package/src/RevisionValueCache.ts +14 -5
  558. package/src/RevisionView.ts +252 -0
  559. package/src/SerializationUtilities.ts +130 -0
  560. package/src/SharedTree.ts +1501 -0
  561. package/src/SharedTreeEncoder.ts +493 -0
  562. package/src/StringInterner.ts +72 -0
  563. package/src/Summary.ts +48 -0
  564. package/src/SummaryBackCompatibility.ts +47 -57
  565. package/src/SummaryTestUtilities.ts +54 -0
  566. package/src/Transaction.ts +89 -0
  567. package/src/TransactionInternal.ts +1087 -0
  568. package/src/TreeCompressor.ts +213 -0
  569. package/src/TreeNodeHandle.ts +19 -32
  570. package/src/TreeView.ts +322 -0
  571. package/src/TreeViewUtilities.ts +77 -0
  572. package/src/{default-edits/UndoRedoHandler.ts → UndoRedoHandler.ts} +8 -13
  573. package/src/id-compressor/AppendOnlySortedMap.ts +325 -0
  574. package/src/id-compressor/IdCompressor.md +3 -0
  575. package/src/id-compressor/IdCompressor.ts +1848 -0
  576. package/src/id-compressor/IdRange.ts +33 -0
  577. package/src/id-compressor/NumericUuid.ts +414 -0
  578. package/src/id-compressor/index.ts +13 -0
  579. package/src/id-compressor/persisted-types/0.0.1.ts +179 -0
  580. package/src/id-compressor/persisted-types/README.md +3 -0
  581. package/src/id-compressor/persisted-types/index.ts +6 -0
  582. package/src/index.ts +118 -59
  583. package/src/persisted-types/0.0.2.ts +442 -0
  584. package/src/persisted-types/0.1.1.ts +476 -0
  585. package/src/persisted-types/README.md +22 -0
  586. package/src/persisted-types/index.ts +9 -0
  587. package/.mocharc.js +0 -41
  588. package/api/tree.api.md +0 -729
  589. package/dist/BasicCheckout.d.ts +0 -23
  590. package/dist/BasicCheckout.d.ts.map +0 -1
  591. package/dist/BasicCheckout.js.map +0 -1
  592. package/dist/Snapshot.d.ts +0 -198
  593. package/dist/Snapshot.d.ts.map +0 -1
  594. package/dist/Snapshot.js +0 -267
  595. package/dist/Snapshot.js.map +0 -1
  596. package/dist/SnapshotUtilities.d.ts +0 -29
  597. package/dist/SnapshotUtilities.d.ts.map +0 -1
  598. package/dist/SnapshotUtilities.js +0 -73
  599. package/dist/SnapshotUtilities.js.map +0 -1
  600. package/dist/anchored-edits/AnchorResolution.d.ts +0 -144
  601. package/dist/anchored-edits/AnchorResolution.d.ts.map +0 -1
  602. package/dist/anchored-edits/AnchorResolution.js +0 -162
  603. package/dist/anchored-edits/AnchorResolution.js.map +0 -1
  604. package/dist/anchored-edits/Factory.d.ts +0 -56
  605. package/dist/anchored-edits/Factory.d.ts.map +0 -1
  606. package/dist/anchored-edits/Factory.js +0 -79
  607. package/dist/anchored-edits/Factory.js.map +0 -1
  608. package/dist/anchored-edits/PersistedTypes.d.ts +0 -245
  609. package/dist/anchored-edits/PersistedTypes.d.ts.map +0 -1
  610. package/dist/anchored-edits/PersistedTypes.js +0 -131
  611. package/dist/anchored-edits/PersistedTypes.js.map +0 -1
  612. package/dist/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
  613. package/dist/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
  614. package/dist/anchored-edits/SharedTreeWithAnchors.js +0 -115
  615. package/dist/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
  616. package/dist/anchored-edits/TransactionWithAnchors.d.ts +0 -28
  617. package/dist/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
  618. package/dist/anchored-edits/TransactionWithAnchors.js +0 -36
  619. package/dist/anchored-edits/TransactionWithAnchors.js.map +0 -1
  620. package/dist/anchored-edits/index.d.ts +0 -10
  621. package/dist/anchored-edits/index.d.ts.map +0 -1
  622. package/dist/anchored-edits/index.js +0 -34
  623. package/dist/anchored-edits/index.js.map +0 -1
  624. package/dist/default-edits/EditUtilities.d.ts +0 -57
  625. package/dist/default-edits/EditUtilities.d.ts.map +0 -1
  626. package/dist/default-edits/EditUtilities.js +0 -192
  627. package/dist/default-edits/EditUtilities.js.map +0 -1
  628. package/dist/default-edits/Factory.d.ts +0 -56
  629. package/dist/default-edits/Factory.d.ts.map +0 -1
  630. package/dist/default-edits/Factory.js +0 -79
  631. package/dist/default-edits/Factory.js.map +0 -1
  632. package/dist/default-edits/HistoryEditFactory.d.ts +0 -19
  633. package/dist/default-edits/HistoryEditFactory.d.ts.map +0 -1
  634. package/dist/default-edits/HistoryEditFactory.js +0 -187
  635. package/dist/default-edits/HistoryEditFactory.js.map +0 -1
  636. package/dist/default-edits/PersistedTypes.d.ts.map +0 -1
  637. package/dist/default-edits/PersistedTypes.js.map +0 -1
  638. package/dist/default-edits/SharedTree.d.ts +0 -111
  639. package/dist/default-edits/SharedTree.d.ts.map +0 -1
  640. package/dist/default-edits/SharedTree.js +0 -124
  641. package/dist/default-edits/SharedTree.js.map +0 -1
  642. package/dist/default-edits/Summary.d.ts +0 -15
  643. package/dist/default-edits/Summary.d.ts.map +0 -1
  644. package/dist/default-edits/Summary.js +0 -35
  645. package/dist/default-edits/Summary.js.map +0 -1
  646. package/dist/default-edits/Transaction.d.ts +0 -41
  647. package/dist/default-edits/Transaction.d.ts.map +0 -1
  648. package/dist/default-edits/Transaction.js +0 -225
  649. package/dist/default-edits/Transaction.js.map +0 -1
  650. package/dist/default-edits/UndoRedoHandler.d.ts.map +0 -1
  651. package/dist/default-edits/UndoRedoHandler.js.map +0 -1
  652. package/dist/default-edits/index.d.ts +0 -13
  653. package/dist/default-edits/index.d.ts.map +0 -1
  654. package/dist/default-edits/index.js +0 -41
  655. package/dist/default-edits/index.js.map +0 -1
  656. package/dist/generic/GenericEditUtilities.d.ts +0 -26
  657. package/dist/generic/GenericEditUtilities.d.ts.map +0 -1
  658. package/dist/generic/GenericEditUtilities.js +0 -45
  659. package/dist/generic/GenericEditUtilities.js.map +0 -1
  660. package/dist/generic/GenericSharedTree.d.ts +0 -221
  661. package/dist/generic/GenericSharedTree.d.ts.map +0 -1
  662. package/dist/generic/GenericSharedTree.js +0 -447
  663. package/dist/generic/GenericSharedTree.js.map +0 -1
  664. package/dist/generic/GenericTransaction.d.ts +0 -87
  665. package/dist/generic/GenericTransaction.d.ts.map +0 -1
  666. package/dist/generic/GenericTransaction.js +0 -144
  667. package/dist/generic/GenericTransaction.js.map +0 -1
  668. package/dist/generic/PersistedTypes.d.ts +0 -194
  669. package/dist/generic/PersistedTypes.d.ts.map +0 -1
  670. package/dist/generic/PersistedTypes.js +0 -42
  671. package/dist/generic/PersistedTypes.js.map +0 -1
  672. package/dist/generic/Summary.d.ts +0 -63
  673. package/dist/generic/Summary.d.ts.map +0 -1
  674. package/dist/generic/Summary.js +0 -64
  675. package/dist/generic/Summary.js.map +0 -1
  676. package/dist/generic/index.d.ts +0 -10
  677. package/dist/generic/index.d.ts.map +0 -1
  678. package/dist/generic/index.js +0 -26
  679. package/dist/generic/index.js.map +0 -1
  680. package/docs/Future.md +0 -155
  681. package/lib/BasicCheckout.d.ts +0 -23
  682. package/lib/BasicCheckout.d.ts.map +0 -1
  683. package/lib/BasicCheckout.js.map +0 -1
  684. package/lib/Snapshot.d.ts +0 -198
  685. package/lib/Snapshot.d.ts.map +0 -1
  686. package/lib/Snapshot.js +0 -263
  687. package/lib/Snapshot.js.map +0 -1
  688. package/lib/SnapshotUtilities.d.ts +0 -29
  689. package/lib/SnapshotUtilities.d.ts.map +0 -1
  690. package/lib/SnapshotUtilities.js +0 -67
  691. package/lib/SnapshotUtilities.js.map +0 -1
  692. package/lib/anchored-edits/AnchorResolution.d.ts +0 -144
  693. package/lib/anchored-edits/AnchorResolution.d.ts.map +0 -1
  694. package/lib/anchored-edits/AnchorResolution.js +0 -152
  695. package/lib/anchored-edits/AnchorResolution.js.map +0 -1
  696. package/lib/anchored-edits/Factory.d.ts +0 -56
  697. package/lib/anchored-edits/Factory.d.ts.map +0 -1
  698. package/lib/anchored-edits/Factory.js +0 -74
  699. package/lib/anchored-edits/Factory.js.map +0 -1
  700. package/lib/anchored-edits/PersistedTypes.d.ts +0 -245
  701. package/lib/anchored-edits/PersistedTypes.d.ts.map +0 -1
  702. package/lib/anchored-edits/PersistedTypes.js +0 -128
  703. package/lib/anchored-edits/PersistedTypes.js.map +0 -1
  704. package/lib/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
  705. package/lib/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
  706. package/lib/anchored-edits/SharedTreeWithAnchors.js +0 -110
  707. package/lib/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
  708. package/lib/anchored-edits/TransactionWithAnchors.d.ts +0 -28
  709. package/lib/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
  710. package/lib/anchored-edits/TransactionWithAnchors.js +0 -32
  711. package/lib/anchored-edits/TransactionWithAnchors.js.map +0 -1
  712. package/lib/anchored-edits/index.d.ts +0 -10
  713. package/lib/anchored-edits/index.d.ts.map +0 -1
  714. package/lib/anchored-edits/index.js +0 -11
  715. package/lib/anchored-edits/index.js.map +0 -1
  716. package/lib/default-edits/EditUtilities.d.ts +0 -57
  717. package/lib/default-edits/EditUtilities.d.ts.map +0 -1
  718. package/lib/default-edits/EditUtilities.js +0 -181
  719. package/lib/default-edits/EditUtilities.js.map +0 -1
  720. package/lib/default-edits/Factory.d.ts +0 -56
  721. package/lib/default-edits/Factory.d.ts.map +0 -1
  722. package/lib/default-edits/Factory.js +0 -74
  723. package/lib/default-edits/Factory.js.map +0 -1
  724. package/lib/default-edits/HistoryEditFactory.d.ts +0 -19
  725. package/lib/default-edits/HistoryEditFactory.d.ts.map +0 -1
  726. package/lib/default-edits/HistoryEditFactory.js.map +0 -1
  727. package/lib/default-edits/PersistedTypes.d.ts.map +0 -1
  728. package/lib/default-edits/PersistedTypes.js.map +0 -1
  729. package/lib/default-edits/SharedTree.d.ts +0 -111
  730. package/lib/default-edits/SharedTree.d.ts.map +0 -1
  731. package/lib/default-edits/SharedTree.js +0 -100
  732. package/lib/default-edits/SharedTree.js.map +0 -1
  733. package/lib/default-edits/Summary.d.ts +0 -15
  734. package/lib/default-edits/Summary.d.ts.map +0 -1
  735. package/lib/default-edits/Summary.js +0 -31
  736. package/lib/default-edits/Summary.js.map +0 -1
  737. package/lib/default-edits/Transaction.d.ts +0 -41
  738. package/lib/default-edits/Transaction.d.ts.map +0 -1
  739. package/lib/default-edits/Transaction.js +0 -221
  740. package/lib/default-edits/Transaction.js.map +0 -1
  741. package/lib/default-edits/UndoRedoHandler.d.ts.map +0 -1
  742. package/lib/default-edits/UndoRedoHandler.js.map +0 -1
  743. package/lib/default-edits/index.d.ts +0 -13
  744. package/lib/default-edits/index.d.ts.map +0 -1
  745. package/lib/default-edits/index.js +0 -14
  746. package/lib/default-edits/index.js.map +0 -1
  747. package/lib/generic/GenericEditUtilities.d.ts +0 -26
  748. package/lib/generic/GenericEditUtilities.d.ts.map +0 -1
  749. package/lib/generic/GenericEditUtilities.js +0 -38
  750. package/lib/generic/GenericEditUtilities.js.map +0 -1
  751. package/lib/generic/GenericSharedTree.d.ts +0 -221
  752. package/lib/generic/GenericSharedTree.d.ts.map +0 -1
  753. package/lib/generic/GenericSharedTree.js +0 -443
  754. package/lib/generic/GenericSharedTree.js.map +0 -1
  755. package/lib/generic/GenericTransaction.d.ts +0 -87
  756. package/lib/generic/GenericTransaction.d.ts.map +0 -1
  757. package/lib/generic/GenericTransaction.js +0 -140
  758. package/lib/generic/GenericTransaction.js.map +0 -1
  759. package/lib/generic/PersistedTypes.d.ts +0 -194
  760. package/lib/generic/PersistedTypes.d.ts.map +0 -1
  761. package/lib/generic/PersistedTypes.js +0 -39
  762. package/lib/generic/PersistedTypes.js.map +0 -1
  763. package/lib/generic/Summary.d.ts +0 -63
  764. package/lib/generic/Summary.d.ts.map +0 -1
  765. package/lib/generic/Summary.js +0 -58
  766. package/lib/generic/Summary.js.map +0 -1
  767. package/lib/generic/index.d.ts +0 -10
  768. package/lib/generic/index.d.ts.map +0 -1
  769. package/lib/generic/index.js +0 -11
  770. package/lib/generic/index.js.map +0 -1
  771. package/lib/test/Anchors.glassBox.tests.d.ts.map +0 -1
  772. package/lib/test/Anchors.glassBox.tests.js +0 -410
  773. package/lib/test/Anchors.glassBox.tests.js.map +0 -1
  774. package/lib/test/BasicCheckout.tests.d.ts.map +0 -1
  775. package/lib/test/BasicCheckout.tests.js +0 -8
  776. package/lib/test/BasicCheckout.tests.js.map +0 -1
  777. package/lib/test/GenericTransactionWithAnchors.tests.d.ts.map +0 -1
  778. package/lib/test/GenericTransactionWithAnchors.tests.js +0 -25
  779. package/lib/test/GenericTransactionWithAnchors.tests.js.map +0 -1
  780. package/lib/test/SharedTreeWithAnchors.tests.d.ts.map +0 -1
  781. package/lib/test/SharedTreeWithAnchors.tests.js +0 -420
  782. package/lib/test/SharedTreeWithAnchors.tests.js.map +0 -1
  783. package/lib/test/Snapshot.tests.d.ts.map +0 -1
  784. package/lib/test/Snapshot.tests.js +0 -96
  785. package/lib/test/Snapshot.tests.js.map +0 -1
  786. package/lib/test/SnapshotUtilities.tests.d.ts.map +0 -1
  787. package/lib/test/SnapshotUtilities.tests.js +0 -168
  788. package/lib/test/SnapshotUtilities.tests.js.map +0 -1
  789. package/lib/test/undoRedoStackManager.d.ts +0 -26
  790. package/lib/test/undoRedoStackManager.d.ts.map +0 -1
  791. package/lib/test/undoRedoStackManager.js +0 -176
  792. package/lib/test/undoRedoStackManager.js.map +0 -1
  793. package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts +0 -13
  794. package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts.map +0 -1
  795. package/lib/test/utilities/SummaryFormatCompatibilityTests.js +0 -154
  796. package/lib/test/utilities/SummaryFormatCompatibilityTests.js.map +0 -1
  797. package/src/BasicCheckout.ts +0 -34
  798. package/src/Snapshot.ts +0 -363
  799. package/src/SnapshotUtilities.ts +0 -88
  800. package/src/anchored-edits/AnchorResolution.ts +0 -442
  801. package/src/anchored-edits/Factory.ts +0 -94
  802. package/src/anchored-edits/PersistedTypes.ts +0 -310
  803. package/src/anchored-edits/SharedTreeWithAnchors.ts +0 -200
  804. package/src/anchored-edits/TransactionWithAnchors.ts +0 -39
  805. package/src/anchored-edits/index.ts +0 -21
  806. package/src/default-edits/EditUtilities.ts +0 -220
  807. package/src/default-edits/Factory.ts +0 -94
  808. package/src/default-edits/SharedTree.ts +0 -174
  809. package/src/default-edits/Summary.ts +0 -44
  810. package/src/default-edits/Transaction.ts +0 -262
  811. package/src/default-edits/index.ts +0 -29
  812. package/src/generic/GenericEditUtilities.ts +0 -46
  813. package/src/generic/GenericSharedTree.ts +0 -593
  814. package/src/generic/GenericTransaction.ts +0 -194
  815. package/src/generic/PersistedTypes.ts +0 -221
  816. package/src/generic/Summary.ts +0 -113
  817. package/src/generic/index.ts +0 -41
@@ -3,34 +3,50 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { assert, expect } from 'chai';
6
- import { v4 as uuidv4 } from 'uuid';
7
- import { delay } from '@fluidframework/common-utils';
8
- import { MockFluidDataStoreRuntime } from '@fluidframework/test-runtime-utils';
9
- import { assertArrayOfOne, assertNotUndefined, isSharedTreeEvent } from '../../Common';
10
- import { SharedTreeOpType, SharedTreeEvent, serialize, newEdit, EditStatus, } from '../../generic';
11
- import { Change, ChangeType, Delete, Insert, StablePlace, StableRange } from '../../default-edits';
12
- import { Snapshot } from '../../Snapshot';
6
+ import { IsoBuffer } from '@fluidframework/common-utils';
7
+ import { LoaderHeader } from '@fluidframework/container-definitions';
8
+ import { MockFluidDataStoreRuntime, } from '@fluidframework/test-runtime-utils';
9
+ import { assertArrayOfOne, assertNotUndefined, fail, isSharedTreeEvent } from '../../Common';
10
+ import { EditLog } from '../../EditLog';
13
11
  import { initialTree } from '../../InitialTree';
14
12
  import { TreeNodeHandle } from '../../TreeNodeHandle';
15
13
  import { deserialize } from '../../SummaryBackCompatibility';
16
- import { makeEmptyNode, testTrait, left, right, leftTraitLabel, leftTraitLocation, rightTraitLocation, simpleTestTree, areNodesEquivalent, rightTraitLabel, assertNoDelta, deepCompareNodes, initialSnapshot, } from './TestUtilities';
17
- import { runSharedTreeUndoRedoTestSuite } from './UndoRedoTests';
14
+ import { useFailedSequencedEditTelemetry } from '../../MergeHealth';
15
+ import { MutableStringInterner } from '../../StringInterner';
16
+ import { getChangeNodeFromView } from '../../SerializationUtilities';
17
+ import { ChangeInternal, ChangeTypeInternal, editsPerChunk, EditStatus, WriteFormat, } from '../../persisted-types';
18
+ import { SharedTreeDiagnosticEvent, SharedTreeEvent } from '../../EventTypes';
19
+ import { Change, ChangeType, StablePlace, StableRange } from '../../ChangeTypes';
20
+ import { convertTreeNodes, deepCompareNodes } from '../../EditUtilities';
21
+ import { serialize } from '../../Summary';
22
+ import { InterningTreeCompressor } from '../../TreeCompressor';
23
+ import { SharedTreeEncoder_0_0_2, SharedTreeEncoder_0_1_1 } from '../../SharedTreeEncoder';
24
+ import { sequencedIdNormalizer } from '../../NodeIdUtilities';
25
+ import { convertNodeDataIds } from '../../IdConversion';
26
+ import { buildLeaf, SimpleTestTree } from './TestNode';
18
27
  import { TestFluidHandle, TestFluidSerializer } from './TestSerializer';
19
- const revert = (tree, editId) => {
20
- const editIndex = tree.edits.getIndexOfId(editId);
21
- return tree.editor.revert(tree.edits.getEditInSessionAtIndex(editIndex), tree.logViewer.getSnapshotInSession(editIndex));
22
- };
28
+ import { runSharedTreeUndoRedoTestSuite } from './UndoRedoTests';
29
+ import { areNodesEquivalent, assertNoDelta, setUpTestTree, testTrait, testTraitLabel, translateId, spyOnSubmittedOps, normalizeIds, normalizeId, normalizeEdit, setUpLocalServerTestSharedTree, applyNoop, getIdNormalizerFromSharedTree, waitForSummary, } from './TestUtilities';
30
+ function revertEditInTree(tree, edit) {
31
+ return tree.revert(edit);
32
+ }
23
33
  // Options for the undo/redo test suite. The undo and redo functions are the same.
24
34
  const undoRedoOptions = {
25
35
  title: 'Revert',
26
- undo: revert,
27
- redo: revert,
36
+ undo: revertEditInTree,
37
+ redo: revertEditInTree,
28
38
  };
29
39
  /**
30
- * Runs a test suite for operations on `SharedTree`.
40
+ * Runs a test suite for operations on `SharedTree` writing ops at `writeFormat`.
31
41
  * This suite can be used to test other implementations that aim to fulfill `SharedTree`'s contract.
32
42
  */
33
- export function runSharedTreeOperationsTests(title, setUpTestSharedTree) {
43
+ export function runSharedTreeOperationsTests(title, writeFormat, setUpTestSharedTreeWithDefaultVersion) {
44
+ const setUpTestSharedTree = (options) => setUpTestSharedTreeWithDefaultVersion(Object.assign({ writeFormat }, options));
45
+ function createSimpleTestTree(options) {
46
+ const { tree: sharedTree, componentRuntime, containerRuntimeFactory } = setUpTestSharedTree(options);
47
+ const testTree = setUpTestTree(sharedTree);
48
+ return { sharedTree, testTree, componentRuntime, containerRuntimeFactory };
49
+ }
34
50
  describe(title, () => {
35
51
  const testSerializer = new TestFluidSerializer();
36
52
  describe('SharedTree before initialization', () => {
@@ -40,664 +56,905 @@ export function runSharedTreeOperationsTests(title, setUpTestSharedTree) {
40
56
  });
41
57
  it('valid without initial tree', () => {
42
58
  const { tree } = setUpTestSharedTree();
43
- expect(tree.currentView.getTrait(testTrait)).deep.equals([], 'Root should exist, and child traits should be valid but empty.');
59
+ expect(tree.currentView.getTrait(testTrait(tree.currentView))).deep.equals([], 'Root should exist, and child traits should be valid but empty.');
44
60
  });
45
61
  });
46
62
  describe('SharedTree in local state', () => {
47
63
  it('does not emit change events for each change in a batch of changes', () => {
48
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
64
+ const { sharedTree, testTree } = createSimpleTestTree();
49
65
  let changeCount = 0;
50
- tree.on(SharedTreeEvent.EditCommitted, () => {
51
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
52
- const rightTrait = tree.currentView.getTrait(rightTraitLocation);
66
+ sharedTree.on(SharedTreeEvent.EditCommitted, () => {
67
+ const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
68
+ const rightTrait = sharedTree.currentView.getTrait(testTree.right.traitLocation);
53
69
  expect(leftTrait.length).to.equal(0); // "left" child is deleted...
54
70
  expect(rightTrait.length).to.equal(2); // ...and added to "right" trait
55
71
  changeCount += 1;
56
72
  });
57
- tree.editor.move(left, StablePlace.after(right));
73
+ sharedTree.applyEdit(...Change.move(StableRange.only(testTree.left), StablePlace.after(testTree.right)));
58
74
  expect(changeCount).equals(1);
59
75
  });
60
76
  it('can insert a wrapped tree', () => {
61
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
62
- const childNode = makeEmptyNode();
77
+ const { sharedTree, testTree } = createSimpleTestTree();
78
+ const childNode = testTree.buildLeaf(testTree.generateNodeId());
63
79
  const childId = 0;
64
80
  const childrenTraitLabel = 'children';
65
81
  const parentNode = {
66
- identifier: uuidv4(),
82
+ identifier: testTree.generateNodeId(),
67
83
  definition: 'node',
68
84
  traits: {
69
- [childrenTraitLabel]: [childId],
85
+ [childrenTraitLabel]: childId,
70
86
  },
71
87
  };
72
88
  const parentId = 1;
73
- const buildChild = Change.build([childNode], childId);
74
- const buildParent = Change.build([parentNode], parentId);
75
- const insertParent = Change.insert(parentId, StablePlace.before(left));
76
- tree.applyEdit(buildChild, buildParent, insertParent);
77
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
89
+ const buildChild = Change.build(childNode, childId);
90
+ const buildParent = Change.build(parentNode, parentId);
91
+ const insertParent = Change.insert(parentId, StablePlace.before(testTree.left));
92
+ sharedTree.applyEdit(buildChild, buildParent, insertParent);
93
+ const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
78
94
  expect(leftTrait.length).to.equal(2);
79
95
  expect(leftTrait[0]).to.equal(parentNode.identifier);
80
- const childrenTrait = tree.currentView.getTrait({
96
+ const childrenTrait = sharedTree.currentView.getTrait({
81
97
  parent: parentNode.identifier,
82
98
  label: childrenTraitLabel,
83
99
  });
84
100
  expect(childrenTrait.length).to.equal(1);
85
101
  expect(childrenTrait[0]).to.equal(childNode.identifier);
86
102
  });
87
- it('prevents multiparenting detached trees', () => {
88
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowMalformed: true });
89
- const childNode = makeEmptyNode();
103
+ it('prevents multi-parenting detached trees', () => {
104
+ const { sharedTree, testTree } = createSimpleTestTree({ allowMalformed: true });
105
+ const childNode = testTree.buildLeaf();
90
106
  const childId = 0;
91
107
  const childrenTraitLabel = 'children';
92
108
  const parentNode = {
93
- identifier: uuidv4(),
109
+ identifier: testTree.generateNodeId(),
94
110
  definition: 'node',
95
111
  traits: {
96
- [childrenTraitLabel]: [childId],
112
+ [childrenTraitLabel]: childId,
97
113
  },
98
114
  };
99
- const parentId = 1;
100
115
  const parentNode2 = {
101
- identifier: uuidv4(),
116
+ identifier: testTree.generateNodeId(),
102
117
  definition: 'node',
103
118
  traits: {
104
- [childrenTraitLabel]: [childId],
119
+ [childrenTraitLabel]: childId,
105
120
  },
106
121
  };
107
- const parentId2 = 2;
108
- const buildChild = Change.build([childNode], childId);
109
- const buildParent = Change.build([parentNode], parentId);
110
- const buildParent2 = Change.build([parentNode2], parentId2);
111
- assertNoDelta(tree, () => {
122
+ const buildChild = Change.build(childNode, childId);
123
+ const buildParent = Change.build(parentNode, 1);
124
+ const buildParent2 = Change.build(parentNode2, 2);
125
+ assertNoDelta(sharedTree, () => {
112
126
  // we don't expect this edit application to change anything
113
- tree.applyEdit(buildChild, buildParent, buildParent2);
127
+ sharedTree.applyEdit(buildChild, buildParent, buildParent2);
114
128
  });
115
129
  });
116
130
  // TODO:#58052: Make this test pass.
117
131
  it.skip('prevents setting the value of a node in a detached subtree', () => {
118
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowInvalid: true });
119
- const detachedNode = makeEmptyNode();
132
+ const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
133
+ const detachedNode = testTree.buildLeaf(testTree.generateNodeId());
120
134
  const detachedSequenceId = 0;
121
- const editId = tree.applyEdit(Change.build([detachedNode], detachedSequenceId), Change.setPayload(detachedNode.identifier, 42), Change.insert(detachedSequenceId, StablePlace.before(left)));
122
- const logViewer = tree.logViewer;
123
- expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(editId)).status).equals(EditStatus.Invalid);
124
- tree.currentView.assertConsistent();
135
+ const { id } = sharedTree.applyEdit(Change.build(detachedNode, detachedSequenceId), Change.setPayload(detachedNode.identifier, 42), Change.insert(detachedSequenceId, StablePlace.before(testTree.left)));
136
+ const logViewer = sharedTree.logViewer;
137
+ expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(id)).status).equals(EditStatus.Invalid);
138
+ sharedTree.currentView.assertConsistent();
125
139
  });
126
140
  // TODO:#58052: Make this test pass.
127
141
  it.skip('prevents inserting a node in a detached subtree through a local edit', () => {
128
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowInvalid: true });
129
- const detachedNewNode = makeEmptyNode();
142
+ const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
143
+ const detachedNewNode = testTree.buildLeaf();
130
144
  const detachedNewNodeSequenceId = 0;
131
145
  const detachedRightNodeSequenceId = 1;
132
- const editId = tree.applyEdit(Change.build([detachedNewNode], detachedNewNodeSequenceId), Change.detach(StableRange.only(right), detachedRightNodeSequenceId),
146
+ const { id } = sharedTree.applyEdit(Change.build(detachedNewNode, detachedNewNodeSequenceId), Change.detach(StableRange.only(testTree.right), detachedRightNodeSequenceId),
133
147
  // This change attempts to insert a node under a detached node
134
- Change.insert(detachedNewNodeSequenceId, StablePlace.atStartOf({ parent: right.identifier, label: 'foo' })), Change.insert(detachedRightNodeSequenceId, StablePlace.before(left)));
135
- const logViewer = tree.logViewer;
136
- expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(editId)).status).equals(EditStatus.Invalid);
137
- tree.currentView.assertConsistent();
148
+ Change.insert(detachedNewNodeSequenceId, StablePlace.atStartOf({ parent: testTree.right.identifier, label: 'foo' })), Change.insert(detachedRightNodeSequenceId, StablePlace.before(testTree.left)));
149
+ const logViewer = sharedTree.logViewer;
150
+ expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(id)).status).equals(EditStatus.Invalid);
151
+ sharedTree.currentView.assertConsistent();
138
152
  });
139
153
  it('prevents deletion of the root', () => {
140
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowInvalid: true });
141
- expect(tree.currentView.hasNode(initialSnapshot.root));
142
- assertNoDelta(tree, () => {
154
+ const { sharedTree } = createSimpleTestTree({ allowInvalid: true });
155
+ const rootId = sharedTree.convertToNodeId(initialTree.identifier);
156
+ expect(sharedTree.currentView.hasNode(rootId));
157
+ assertNoDelta(sharedTree, () => {
143
158
  // Try to delete the root
144
- tree.processLocalEdit(newEdit([Delete.create(StableRange.only(initialSnapshot.root))]));
159
+ sharedTree.applyEdit(Change.delete(StableRange.only(rootId)));
145
160
  });
146
161
  });
147
162
  it('can apply multiple local edits without ack from server', () => {
148
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
149
- const newNode = makeEmptyNode();
150
- tree.editor.insert(newNode, StablePlace.after(left));
151
- tree.editor.move(newNode, StablePlace.before(left));
152
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
163
+ const { sharedTree, testTree } = createSimpleTestTree();
164
+ const newNode = testTree.buildLeaf(testTree.generateNodeId());
165
+ sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
166
+ sharedTree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
167
+ const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
153
168
  expect(leftTrait.length).equals(2);
154
169
  expect(leftTrait[0]).equals(newNode.identifier);
155
170
  });
156
- it('is not equal to a tree with different initial views', () => {
157
- const secondSimpleTestTree = Object.assign(Object.assign({}, simpleTestTree), { identifier: uuidv4() });
158
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
159
- const { tree: secondTree } = setUpTestSharedTree({ initialTree: secondSimpleTestTree });
160
- expect(tree.equals(secondTree)).to.be.false;
171
+ it('is not equal to a tree with different current views', () => {
172
+ const { sharedTree: sharedTree1 } = createSimpleTestTree();
173
+ const { tree: sharedTree2 } = setUpTestSharedTree();
174
+ expect(sharedTree1.equals(sharedTree2)).to.be.false;
161
175
  });
162
- it('is not equal to a tree with the same view but different edit lists', () => {
163
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
164
- const { tree: secondTree } = setUpTestSharedTree({ initialTree: simpleTestTree });
176
+ it('is not equal to a tree with the same current view but different edit logs', () => {
177
+ const { sharedTree: sharedTree1 } = createSimpleTestTree();
178
+ const { sharedTree: sharedTree2 } = createSimpleTestTree();
165
179
  // The edits that create the initial tree have different identities.
166
- expect(tree.equals(secondTree)).to.be.false;
180
+ expect(sharedTree1.equals(sharedTree2)).to.be.false;
167
181
  });
168
182
  it('tolerates invalid inserts', () => {
169
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowInvalid: true });
170
- const firstNode = makeEmptyNode();
171
- const secondNode = makeEmptyNode();
172
- tree.editor.insert(firstNode, StablePlace.after(left));
173
- tree.editor.delete(firstNode);
183
+ const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
184
+ const firstNode = testTree.buildLeaf(testTree.generateNodeId());
185
+ const secondNode = testTree.buildLeaf(testTree.generateNodeId());
186
+ sharedTree.applyEdit(...Change.insertTree(firstNode, StablePlace.after(testTree.left)));
187
+ sharedTree.applyEdit(Change.delete(StableRange.only(firstNode)));
174
188
  // Trying to insert next to the deleted node should drop, confirm that it doesn't
175
- // change the snapshot
176
- assertNoDelta(tree, () => {
177
- tree.processLocalEdit(newEdit(Insert.create([secondNode], StablePlace.after(firstNode))));
189
+ // change the view
190
+ assertNoDelta(sharedTree, () => {
191
+ sharedTree.applyEdit(...Change.insertTree(secondNode, StablePlace.after(firstNode)));
178
192
  });
179
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
193
+ const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
180
194
  expect(leftTrait.length).to.equal(1);
181
195
  });
182
196
  it('tolerates invalid detaches', () => {
183
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowInvalid: true });
184
- const firstNode = makeEmptyNode();
185
- const secondNode = makeEmptyNode();
186
- const thirdNode = makeEmptyNode();
187
- tree.editor.insert([firstNode, secondNode, thirdNode], StablePlace.after(left));
188
- tree.editor.delete(secondNode);
189
- assertNoDelta(tree, () => {
197
+ const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
198
+ const firstNode = testTree.buildLeaf(testTree.generateNodeId());
199
+ const secondNode = testTree.buildLeaf(testTree.generateNodeId());
200
+ const thirdNode = testTree.buildLeaf(testTree.generateNodeId());
201
+ sharedTree.applyEdit(...Change.insertTree([firstNode, secondNode, thirdNode], StablePlace.after(testTree.left)));
202
+ sharedTree.applyEdit(Change.delete(StableRange.only(secondNode)));
203
+ assertNoDelta(sharedTree, () => {
190
204
  // Trying to delete from before firstNode to after secondNode should drop
191
- tree.processLocalEdit(newEdit([
192
- Delete.create(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode))),
193
- ]));
205
+ sharedTree.applyEdit(Change.delete(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode))));
194
206
  // Trying to delete from after thirdNode to before firstNode should drop
195
- tree.processLocalEdit(newEdit([
196
- Delete.create(StableRange.from(StablePlace.after(thirdNode)).to(StablePlace.before(firstNode))),
197
- ]));
207
+ sharedTree.applyEdit(Change.delete(StableRange.from(StablePlace.after(thirdNode)).to(StablePlace.before(firstNode))));
198
208
  });
199
209
  // Expect that firstNode did not get deleted
200
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
210
+ const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
201
211
  expect(leftTrait.length).to.equal(3);
202
212
  });
203
213
  it('tolerates malformed inserts', () => {
204
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree, allowMalformed: true });
205
- assertNoDelta(tree, () => {
206
- tree.processLocalEdit(newEdit([Change.build([], 0)]));
214
+ const { sharedTree } = createSimpleTestTree({ allowMalformed: true });
215
+ assertNoDelta(sharedTree, () => {
216
+ sharedTree.applyEdit(Change.build([], 0));
207
217
  });
208
218
  });
209
219
  runSharedTreeUndoRedoTestSuite(Object.assign({ localMode: true }, undoRedoOptions));
210
220
  });
211
221
  describe('SharedTree in connected state with a remote SharedTree', () => {
212
- const treeOptions = { initialTree: simpleTestTree, localMode: false };
213
- const secondTreeOptions = {
214
- id: 'secondTestSharedTree',
215
- localMode: false,
216
- };
222
+ /**
223
+ * Initial tree options for multi-tree tests below.
224
+ * Intended to be passed to {@link createSimpleTestTree}.
225
+ */
226
+ const tree1Options = { localMode: false };
227
+ /**
228
+ * Secondary tree options derived from some initial tree.
229
+ */
230
+ function createSecondTreeOptions(containerRuntimeFactory) {
231
+ return {
232
+ containerRuntimeFactory,
233
+ id: 'secondTestSharedTree',
234
+ localMode: false,
235
+ };
236
+ }
217
237
  it('should apply remote changes and converge', () => {
218
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
219
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
238
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
239
+ const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
240
+ const newNodeId1 = sharedTree1.generateNodeId();
241
+ sharedTree1.applyEdit(...Change.insertTree([buildLeaf(newNodeId1)], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
220
242
  // Sync initial tree
221
243
  containerRuntimeFactory.processAllMessages();
244
+ const newNodeId2 = translateId(newNodeId1, sharedTree1, sharedTree2);
222
245
  // Both trees should contain 'left'
223
- expect(tree.currentView.getSnapshotNode(left.identifier)).to.not.be.undefined;
224
- expect(secondTree.currentView.getSnapshotNode(left.identifier)).to.not.be.undefined;
225
- secondTree.editor.delete(left);
226
- containerRuntimeFactory.processAllMessages();
227
- const rootA = tree.currentView.getSnapshotNode(simpleTestTree.identifier);
228
- expect(rootA.traits.get(leftTraitLabel)).to.be.undefined;
229
- const rootB = secondTree.currentView.getSnapshotNode(simpleTestTree.identifier);
230
- expect(rootB.traits.get(leftTraitLabel)).to.be.undefined;
231
- });
232
- it('should apply local edits after all sequenced edits', () => {
233
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
234
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
235
- // Sync initial tree
236
- containerRuntimeFactory.processAllMessages();
237
- const newNode = makeEmptyNode();
238
- tree.editor.insert(newNode, StablePlace.after(left));
239
- containerRuntimeFactory.processAllMessages();
240
- // Concurrently perform edit that will be sequenced before the move below.
241
- // If local edits are sorted incorrectly (before sequenced edits), this will cause evaluation of a state in which the local
242
- // move is evaluated before all other edits. Since it is causally dependant on the initial insert edit, it would fail.
243
- secondTree.editor.delete(right);
244
- // Attempt to move the new node.
245
- tree.editor.move(newNode, StablePlace.before(left));
246
- // Deliver the remote edit. The move should be applied after all sequenced edits and succeed.
246
+ expect(sharedTree1.currentView.getViewNode(newNodeId1)).to.not.be.undefined;
247
+ expect(sharedTree2.currentView.getViewNode(newNodeId2)).to.not.be.undefined;
248
+ sharedTree2.applyEdit(Change.delete(StableRange.only(newNodeId2)));
247
249
  containerRuntimeFactory.processAllMessages();
250
+ const rootA = sharedTree1.currentView.getViewNode(sharedTree1.currentView.root);
251
+ expect(rootA.traits.get(testTraitLabel)).to.be.undefined;
252
+ const rootB = sharedTree2.currentView.getViewNode(sharedTree2.currentView.root);
253
+ expect(rootB.traits.get(testTraitLabel)).to.be.undefined;
248
254
  });
249
255
  it('converges in the face of concurrent changes', () => {
250
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
251
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
252
- // Sync initial tree
256
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
257
+ const { sharedTree: sharedTree2 } = createSimpleTestTree(createSecondTreeOptions(containerRuntimeFactory));
258
+ const newNodeId1 = sharedTree1.generateNodeId();
259
+ sharedTree1.applyEdit(...Change.insertTree([buildLeaf(newNodeId1)], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
253
260
  containerRuntimeFactory.processAllMessages();
254
261
  // First client deletes a trait containing a node in the initial tree
255
- tree.editor.delete(left);
262
+ sharedTree1.applyEdit(Change.delete(StableRange.all(testTrait(sharedTree1.currentView))));
256
263
  // Second client concurrently adds a new node to that trait
257
- const newNode = makeEmptyNode();
258
- secondTree.editor.insert(newNode, StablePlace.atEndOf(leftTraitLocation));
264
+ const newNodeId2 = sharedTree2.generateNodeId();
265
+ sharedTree2.applyEdit(...Change.insertTree([buildLeaf(newNodeId2)], StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
259
266
  containerRuntimeFactory.processAllMessages();
260
267
  // Second client's change gets sequenced after the deletion, so the trait
261
- // should exist and contain the new node on both clients after messages are delivered.
262
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
263
- const secondLeftTrait = secondTree.currentView.getTrait(leftTraitLocation);
268
+ // should exist and contain the second new node on both clients after messages are delivered.
269
+ const leftTrait = normalizeIds(sharedTree1, ...sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView)));
270
+ const secondLeftTrait = normalizeIds(sharedTree2, ...sharedTree2.currentView.getTrait(testTrait(sharedTree2.currentView)));
264
271
  expect(leftTrait.length).equals(1);
265
- expect(leftTrait[0]).equals(newNode.identifier);
272
+ expect(leftTrait[0]).equals(normalizeId(sharedTree2, newNodeId2));
266
273
  expect(leftTrait).deep.equals(secondLeftTrait);
267
274
  });
268
275
  it('is equal to a tree with the same state', () => {
269
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
270
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
276
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
277
+ const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
278
+ const newNodeId1 = sharedTree1.generateNodeId();
279
+ sharedTree1.applyEdit(...Change.insertTree([
280
+ {
281
+ identifier: newNodeId1,
282
+ definition: 'foo',
283
+ traits: { left: buildLeaf(), right: buildLeaf() },
284
+ },
285
+ ], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
271
286
  containerRuntimeFactory.processAllMessages();
272
- expect(tree.equals(secondTree)).to.be.true;
273
- secondTree.editor.delete(left);
287
+ expect(sharedTree1.equals(sharedTree2)).to.be.true;
288
+ sharedTree2.applyEdit(Change.delete(StableRange.only(translateId(newNodeId1, sharedTree1, sharedTree2))));
274
289
  containerRuntimeFactory.processAllMessages();
275
- expect(tree.equals(secondTree)).to.be.true;
290
+ expect(sharedTree1.equals(sharedTree2)).to.be.true;
276
291
  });
277
292
  // TODO:#58052: Make this test pass.
278
293
  it.skip('prevents inserting a node in a detached subtree as the result of merged edits', () => {
279
- const rootId = 'root';
280
- const parent1Id = 'parent1';
281
- const parent2Id = 'parent2';
282
- const childId = 'child';
283
- const childTraitUnderParent2 = { parent: parent2Id, label: 'child' };
284
- const badTraitUnderChild = { parent: childId, label: 'whatever' };
285
- const initialTree = Object.assign(Object.assign({}, makeEmptyNode(rootId)), { traits: {
294
+ const { testTree } = createSimpleTestTree();
295
+ const rootNode = testTree.buildLeaf(testTree.generateNodeId());
296
+ const parent1Node = testTree.buildLeaf(testTree.generateNodeId());
297
+ const parent2Node = testTree.buildLeaf(testTree.generateNodeId());
298
+ const parent2Id = parent2Node.identifier;
299
+ const childNode = testTree.buildLeaf(testTree.generateNodeId());
300
+ const childId = childNode.identifier;
301
+ const initialTree = Object.assign(Object.assign({}, rootNode), { traits: {
286
302
  parents: [
287
- Object.assign(Object.assign({}, makeEmptyNode(parent1Id)), { traits: { child: [makeEmptyNode(childId)] } }),
288
- makeEmptyNode(parent2Id),
303
+ Object.assign(Object.assign({}, parent1Node), { traits: { child: childNode } }),
304
+ parent2Node,
289
305
  ],
290
306
  } });
291
- const { tree: tree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, treeOptions), { initialTree, allowInvalid: true }));
292
- const { tree: tree2 } = setUpTestSharedTree(Object.assign(Object.assign({ containerRuntimeFactory }, secondTreeOptions), { allowInvalid: true }));
307
+ const childTraitUnderParent2 = { parent: parent2Id, label: 'child' };
308
+ const badTraitUnderChild = { parent: childId, label: 'whatever' };
309
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { initialTree, allowInvalid: true }));
310
+ const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
293
311
  containerRuntimeFactory.processAllMessages();
294
312
  // Move the child under parent2
295
313
  // This first edit should succeed locally and globally
296
- const edit1Id = tree1.editor.move(childId, StablePlace.atStartOf(childTraitUnderParent2));
314
+ const edit1 = sharedTree1.applyEdit(...Change.move(StableRange.only(childId), StablePlace.atStartOf(childTraitUnderParent2)));
297
315
  // Concurrently move parent2 under child
298
316
  // This first edit should succeed locally but fail globally
299
- const edit2Id = tree2.editor.move(parent2Id, StablePlace.atStartOf(badTraitUnderChild));
317
+ const edit2 = sharedTree2.applyEdit(...Change.move(StableRange.only(parent2Id), StablePlace.atStartOf(badTraitUnderChild)));
300
318
  containerRuntimeFactory.processAllMessages();
301
- const logViewer = tree1.logViewer;
302
- expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit1Id)).status).equals(EditStatus.Applied);
303
- expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit2Id)).status).equals(EditStatus.Invalid);
304
- tree1.currentView.assertConsistent();
319
+ const logViewer = sharedTree1.logViewer;
320
+ expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit1.id)).status).equals(EditStatus.Applied);
321
+ expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit2.id)).status).equals(EditStatus.Invalid);
322
+ sharedTree1.currentView.assertConsistent();
305
323
  });
306
324
  it('tolerates invalid inserts', () => {
307
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, treeOptions), { allowInvalid: true }));
308
- const { tree: secondTree } = setUpTestSharedTree(Object.assign(Object.assign({ containerRuntimeFactory }, secondTreeOptions), { allowInvalid: true }));
325
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
326
+ const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
309
327
  containerRuntimeFactory.processAllMessages();
310
- const firstNode = makeEmptyNode();
311
- const firstEditId = secondTree.editor.insert(firstNode, StablePlace.after(left));
328
+ const firstNode2 = buildLeaf(sharedTree2.generateNodeId());
329
+ const firstEdit = sharedTree2.applyEdit(...Change.insertTree(firstNode2, StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
312
330
  containerRuntimeFactory.processAllMessages();
313
331
  // Concurrently edit, creating invalid insert.
314
332
  // Create delete. This will apply.
315
- const secondEditId = tree.editor.delete(firstNode);
316
- let thirdEditId;
317
- assertNoDelta(tree, () => {
333
+ const secondEdit = sharedTree1.applyEdit(Change.delete(StableRange.only(translateId(firstNode2, sharedTree2, sharedTree1))));
334
+ let thirdEdit;
335
+ assertNoDelta(sharedTree1, () => {
318
336
  // concurrently insert next to the deleted node: this will become invalid.
319
- const secondNode = makeEmptyNode();
320
- thirdEditId = secondTree.editor.insert(secondNode, StablePlace.after(firstNode));
337
+ const secondNode2 = buildLeaf();
338
+ thirdEdit = sharedTree2.applyEdit(...Change.insertTree(secondNode2, StablePlace.after(firstNode2)));
321
339
  containerRuntimeFactory.processAllMessages();
322
340
  });
323
- const leftTrait = secondTree.currentView.getTrait(leftTraitLocation);
324
- expect(leftTrait.length).to.equal(1);
325
- const editIds = tree.edits.editIds;
326
- // Edit 0 creates initial tree
327
- expect(editIds[1]).is.equal(firstEditId);
328
- expect(editIds[2]).is.equal(secondEditId);
329
- expect(editIds[3]).is.equal(thirdEditId);
341
+ const leftTrait = sharedTree2.currentView.getTrait(testTrait(sharedTree2.currentView));
342
+ expect(leftTrait.length).to.equal(0);
343
+ const editIds = sharedTree1.edits.editIds;
344
+ expect(editIds[0]).is.equal(firstEdit.id);
345
+ expect(editIds[1]).is.equal(secondEdit.id);
346
+ expect(editIds[2]).is.equal(thirdEdit.id);
330
347
  });
331
348
  it('tolerates invalid detaches', () => {
332
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, treeOptions), { allowInvalid: true }));
333
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
349
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
350
+ const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
334
351
  containerRuntimeFactory.processAllMessages();
335
- const firstNode = makeEmptyNode();
336
- const secondNode = makeEmptyNode();
337
- const thirdNode = makeEmptyNode();
338
- const firstEditId = secondTree.editor.insert([firstNode, secondNode, thirdNode], StablePlace.after(left));
352
+ const firstNode2 = buildLeaf(sharedTree2.generateNodeId());
353
+ const secondNode2 = buildLeaf(sharedTree2.generateNodeId());
354
+ const thirdNode = buildLeaf();
355
+ const firstEdit = sharedTree2.applyEdit(...Change.insertTree([firstNode2, secondNode2, thirdNode], StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
339
356
  containerRuntimeFactory.processAllMessages();
340
- // Concurrently edit, creating invalid insert.
341
357
  // Create delete. This will apply.
342
- const secondEditId = tree.editor.delete(secondNode);
343
- let thirdEditId;
344
- assertNoDelta(tree, () => {
345
- // concurrently delete from before firstNode to after secondNode: this should become invalid
346
- thirdEditId = secondTree.editor.delete(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode)));
347
- });
358
+ const secondEdit = sharedTree1.applyEdit(Change.delete(StableRange.only(translateId(secondNode2, sharedTree2, sharedTree1))));
359
+ // concurrently delete from before firstNode to after secondNode: this should become invalid
360
+ const thirdEdit = sharedTree2.applyEdit(Change.delete(StableRange.from(StablePlace.before(firstNode2)).to(StablePlace.after(secondNode2))));
348
361
  containerRuntimeFactory.processAllMessages();
349
362
  // Expect that firstNode did not get deleted
350
- const leftTrait = tree.currentView.getTrait(leftTraitLocation);
351
- expect(leftTrait.length).to.equal(3);
352
- const editIds = tree.edits.editIds;
353
- // Edit 0 creates initial tree
354
- expect(editIds[1]).to.equal(firstEditId);
355
- expect(editIds[2]).to.equal(secondEditId);
356
- expect(editIds[3]).to.equal(thirdEditId);
363
+ const leftTrait = sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView));
364
+ expect(leftTrait.length).to.equal(2);
365
+ const editIds = sharedTree1.edits.editIds;
366
+ expect(editIds[0]).to.equal(firstEdit.id);
367
+ expect(editIds[1]).to.equal(secondEdit.id);
368
+ expect(editIds[2]).to.equal(thirdEdit.id);
357
369
  });
358
370
  it('tolerates malformed inserts', () => {
359
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, treeOptions), { allowMalformed: true }));
360
- const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
371
+ const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree(Object.assign(Object.assign({}, tree1Options), { allowMalformed: true }));
372
+ const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
361
373
  containerRuntimeFactory.processAllMessages();
362
- let edit;
363
- assertNoDelta(tree, () => {
374
+ let editId;
375
+ assertNoDelta(sharedTree1, () => {
364
376
  const build = Change.build([], 0);
365
- edit = newEdit([build]);
366
- secondTree.processLocalEdit(edit);
377
+ editId = sharedTree2.applyEdit(build).id;
367
378
  containerRuntimeFactory.processAllMessages();
368
379
  });
369
380
  // Edit 0 creates initial tree
370
- expect(tree.edits.getIdAtIndex(1)).to.equal(edit.id);
381
+ expect(sharedTree1.edits.getIdAtIndex(1)).to.equal(editId);
371
382
  });
372
383
  runSharedTreeUndoRedoTestSuite(Object.assign({ localMode: false }, undoRedoOptions));
373
384
  // This is a regression test for documents corrupted by the following github issue:
374
385
  // https://github.com/microsoft/FluidFramework/issues/4399
375
386
  it('tolerates duplicate edits in trailing operations', () => {
376
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign({}, treeOptions));
387
+ const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree(tree1Options);
377
388
  const remoteRuntime = containerRuntimeFactory.createContainerRuntime(new MockFluidDataStoreRuntime());
378
- const defaultEdits = tree.edits.length;
379
- const edit = newEdit([]);
380
- for (let submissions = 0; submissions < 2; submissions++) {
381
- remoteRuntime.submit({ type: SharedTreeOpType.Edit, edit }, /* localOpMetadata */ undefined);
382
- }
389
+ const ops = spyOnSubmittedOps(containerRuntimeFactory);
390
+ const initialEditCount = sharedTree1.edits.length;
391
+ sharedTree1.applyEdit(Change.setPayload(sharedTree1.currentView.root, 42));
392
+ remoteRuntime.submit(ops[0], /* localOpMetadata */ undefined);
383
393
  containerRuntimeFactory.processAllMessages();
384
- expect(tree.edits.length).to.equal(defaultEdits + 1);
394
+ expect(sharedTree1.edits.length).to.equal(initialEditCount + 1);
385
395
  });
396
+ it('detects concurrent duplicate IDs', () => {
397
+ const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
398
+ const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
399
+ containerRuntimeFactory.processAllMessages();
400
+ const duplicateId = 'duplicate';
401
+ sharedTree1.applyEdit(...Change.insertTree([buildLeaf(sharedTree1.generateNodeId(duplicateId))], StablePlace.atEndOf(testTrait(sharedTree1.currentView))));
402
+ sharedTree2.applyEdit(...Change.insertTree([buildLeaf(sharedTree2.generateNodeId(duplicateId))], StablePlace.atEndOf(testTrait(sharedTree2.currentView))));
403
+ containerRuntimeFactory.processAllMessages();
404
+ expect(sharedTree1.currentView.size).to.equal(2);
405
+ const trait1 = sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView));
406
+ expect(trait1.length).to.equal(1);
407
+ expect(sharedTree1.convertToStableNodeId(trait1[0])).to.equal(duplicateId);
408
+ expect(sharedTree1.equals(sharedTree2));
409
+ });
410
+ if (writeFormat !== WriteFormat.v0_0_2) {
411
+ // This is a regression test for an issue where edits containing Fluid handles weren't properly
412
+ // serialized by chunk uploading code: rather than use an IFluidSerializer, we previously just
413
+ // JSON.stringify'd.
414
+ it('can round-trip edits containing handles through chunking', async () => {
415
+ const blobbedPayload = 'blobbed-string-payload';
416
+ const { tree, testObjectProvider } = await setUpLocalServerTestSharedTree({
417
+ writeFormat,
418
+ });
419
+ const testTree = setUpTestTree(tree);
420
+ const buffer = IsoBuffer.from(blobbedPayload, 'utf8');
421
+ const blob = await tree.getRuntime().uploadBlob(buffer);
422
+ const nodeWithPayload = testTree.buildLeaf(testTree.generateNodeId());
423
+ tree.applyEdit(...Change.insertTree(nodeWithPayload, StablePlace.after(testTree.left)), Change.setPayload(nodeWithPayload.identifier, { blob }));
424
+ // Apply enough edits for the upload of an edit chunk
425
+ for (let i = 0; i < tree.edits.editsPerChunk; i++) {
426
+ applyNoop(tree);
427
+ }
428
+ // `ensureSynchronized` does not guarantee blob upload
429
+ await new Promise((resolve) => setImmediate(resolve));
430
+ // Wait for the ops to to be submitted and processed across the containers
431
+ await testObjectProvider.ensureSynchronized();
432
+ const summary = tree.saveSummary();
433
+ const { editHistory } = summary;
434
+ const { editChunks } = assertNotUndefined(editHistory);
435
+ expect(editChunks.length).to.equal(2);
436
+ const chunkHandle = editChunks[0].chunk;
437
+ expect(typeof chunkHandle.get).to.equal('function');
438
+ const { tree: secondTree } = await setUpLocalServerTestSharedTree({
439
+ writeFormat,
440
+ testObjectProvider,
441
+ });
442
+ secondTree.loadSummary(summary);
443
+ expect(tree.equals(secondTree)).to.be.true;
444
+ const { blob: blobHandle } = new TreeNodeHandle(secondTree.currentView, translateId(nodeWithPayload.identifier, tree, secondTree)).payload;
445
+ expect(blobHandle).to.not.be.undefined;
446
+ const blobContents = await blobHandle.get();
447
+ expect(IsoBuffer.from(blobContents, 'utf8').toString()).to.equal(blobbedPayload);
448
+ });
449
+ }
386
450
  });
387
451
  describe('SharedTree summarizing', () => {
388
- const treeOptions = { initialTree: simpleTestTree, localMode: false };
389
- const newNode = makeEmptyNode();
390
452
  const testHandle = new TestFluidHandle();
391
- it('returns false when given bad json input', () => {
392
- assert.typeOf(deserialize('', testSerializer), 'string');
393
- assert.typeOf(deserialize('~ malformed JSON ~', testSerializer), 'string');
394
- assert.typeOf(deserialize('{ unrecognizedKey: 42 }', testSerializer), 'string');
453
+ it('throws error when given bad json input', () => {
454
+ assert.throws(() => deserialize('', testSerializer));
455
+ assert.throws(() => deserialize('~ malformed JSON ~', testSerializer));
456
+ assert.throws(() => deserialize('{ unrecognizedKey: 42 }', testSerializer));
395
457
  });
396
458
  it('correctly handles snapshots of default trees', () => {
397
459
  const { tree: uninitializedTree } = setUpTestSharedTree();
398
460
  // Serialize the state of one uninitialized tree into a second tree
399
461
  const serialized = serialize(uninitializedTree.saveSummary(), testSerializer, testHandle);
400
462
  const parsedTree = deserialize(serialized, testSerializer);
401
- expect(parsedTree.sequencedEdits).deep.equal([]);
402
- expect(deepCompareNodes(parsedTree.currentTree, initialTree)).to.be.true;
463
+ if (writeFormat === WriteFormat.v0_0_2) {
464
+ const summary = parsedTree;
465
+ expect(summary.sequencedEdits).to.deep.equal([]);
466
+ expect(deepCompareNodes(summary.currentTree, initialTree)).to.be.true;
467
+ }
468
+ else {
469
+ const summary = parsedTree;
470
+ expect(summary.editHistory).to.deep.equal({ editChunks: [], editIds: [] });
471
+ expect(summary.currentTree).to.be.instanceOf(Array);
472
+ expect(summary.internedStrings).to.have.length(1);
473
+ }
403
474
  });
404
475
  [true, false].forEach((hasLocalEdits) => {
405
- it(`produces correct snapshot for a tree with ${hasLocalEdits ? 'local' : 'acked'} edits`, () => {
476
+ it(`produces correct snapshot for a tree with ${hasLocalEdits ? 'local' : 'acked'} edits`, async () => {
477
+ var _a;
406
478
  // The initial tree results in an edit.
407
- const { tree, containerRuntimeFactory } = setUpTestSharedTree({
408
- initialTree: treeOptions.initialTree,
479
+ const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
409
480
  localMode: hasLocalEdits,
410
481
  });
411
- tree.editor.insert(newNode, StablePlace.before(left));
482
+ const newNode = testTree.buildLeaf();
483
+ sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree.left)));
412
484
  if (!hasLocalEdits) {
413
485
  containerRuntimeFactory.processAllMessages();
414
486
  }
415
- const serialized = serialize(tree.saveSummary(), testSerializer, testHandle);
487
+ const serialized = serialize(sharedTree.saveSummary(), testSerializer, testHandle);
416
488
  const treeContent = JSON.parse(serialized);
417
- const parsedTree = treeContent;
489
+ let parsedTree;
490
+ if (writeFormat === WriteFormat.v0_1_1) {
491
+ parsedTree = new SharedTreeEncoder_0_1_1(true).decodeSummary(treeContent);
492
+ }
493
+ else {
494
+ parsedTree = new SharedTreeEncoder_0_0_2(true).decodeSummary(treeContent);
495
+ }
418
496
  expect(parsedTree.currentTree).to.not.be.undefined;
419
- const testRoot = assertArrayOfOne(parsedTree.currentTree.traits[testTrait.label]);
497
+ const testRoot = assertArrayOfOne(assertNotUndefined((_a = parsedTree.currentTree) === null || _a === void 0 ? void 0 : _a.traits[testTree.traitLabel]));
420
498
  expect(testRoot).to.not.be.undefined;
421
499
  expect(testRoot.traits.left).to.not.be.undefined;
422
500
  expect(testRoot.traits.right).to.not.be.undefined;
423
501
  expect(testRoot.traits.left.length).to.equal(2);
424
- expect(parsedTree.sequencedEdits).to.not.be.undefined;
425
- const sequencedEdits = assertNotUndefined(parsedTree.sequencedEdits);
502
+ const editLog = new EditLog(parsedTree.editHistory);
426
503
  // Expect there to be a change in the edit history in addition to the one from setUpTestSharedTree
427
- expect(sequencedEdits.length).to.equal(2);
504
+ expect(editLog.length).to.equal(2);
428
505
  // The first operation to be sequenced is the tree init
429
- expect(sequencedEdits[1].changes.length).to.equal(2);
430
- expect(sequencedEdits[1].changes[0].type).to.equal(ChangeType.Build);
431
- expect(sequencedEdits[1].changes[1].type).to.equal(ChangeType.Insert);
506
+ const treeInitEdit = await editLog.getEditAtIndex(1);
507
+ expect(treeInitEdit.changes.length).to.equal(2);
508
+ expect(treeInitEdit.changes[0].type).to.equal(ChangeType.Build);
509
+ expect(treeInitEdit.changes[1].type).to.equal(ChangeType.Insert);
432
510
  });
433
511
  });
434
512
  it('can be used to initialize a tree', () => {
435
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
436
- const { tree: secondTree } = setUpTestSharedTree();
437
- tree.editor.insert(newNode, StablePlace.before(left));
513
+ const { sharedTree: sharedTree1, testTree: testTree1, containerRuntimeFactory, } = createSimpleTestTree({ localMode: false });
514
+ const { tree: sharedTree2 } = setUpTestSharedTree();
515
+ const newNode = testTree1.buildLeaf();
516
+ sharedTree1.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree1.left)));
438
517
  containerRuntimeFactory.processAllMessages();
439
- secondTree.loadSummary(tree.saveSummary());
518
+ sharedTree2.loadSummary(sharedTree1.saveSummary());
440
519
  // Trees should have equal state since we deserialized the first tree's state into the second tree
441
- expect(tree.equals(secondTree)).to.be.true;
520
+ expect(sharedTree1.equals(sharedTree2)).to.be.true;
442
521
  });
443
522
  it('can be used to initialize a tree with an empty edit list', () => {
444
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
445
- const { tree: secondTree } = setUpTestSharedTree();
523
+ const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree({ localMode: false });
524
+ const { tree: sharedTree2 } = setUpTestSharedTree();
446
525
  containerRuntimeFactory.processAllMessages();
447
526
  // The second tree is not caught up to the first tree yet
448
- expect(tree.equals(secondTree)).to.be.false;
449
- secondTree.loadSummary(tree.saveSummary());
527
+ expect(sharedTree1.equals(sharedTree2)).to.be.false;
528
+ sharedTree2.loadSummary(sharedTree1.saveSummary());
450
529
  // Trees should have equal state since we deserialized the first tree's state into the second tree
451
- expect(tree.equals(secondTree)).to.be.true;
530
+ expect(sharedTree1.equals(sharedTree2)).to.be.true;
452
531
  });
453
532
  it('asserts when loading a summary with duplicated edits', () => {
454
- const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, treeOptions), { summarizeHistory: true }));
455
- const { tree: secondTree } = setUpTestSharedTree();
456
- tree.editor.insert(newNode, StablePlace.before(left));
533
+ const { sharedTree: sharedTree1, testTree: testTree1, containerRuntimeFactory, } = createSimpleTestTree({
534
+ localMode: false,
535
+ summarizeHistory: true,
536
+ writeFormat: WriteFormat.v0_0_2,
537
+ });
538
+ const { tree: sharedTree2 } = setUpTestSharedTree();
539
+ const newNode = testTree1.buildLeaf();
540
+ sharedTree1.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree1.left)));
457
541
  containerRuntimeFactory.processAllMessages();
458
- const summary = tree.saveSummary();
542
+ const summary = sharedTree1.saveSummary();
459
543
  const sequencedEdits = assertNotUndefined(summary.sequencedEdits).slice();
460
544
  sequencedEdits.push(sequencedEdits[0]);
461
545
  const corruptedSummary = Object.assign(Object.assign({}, summary), { sequencedEdits });
462
- expect(() => secondTree.loadSummary(corruptedSummary))
546
+ expect(() => sharedTree2.loadSummary(corruptedSummary))
463
547
  .to.throw(Error)
464
548
  .that.has.property('message')
465
549
  .which.matches(/Duplicate/);
466
550
  });
467
551
  it('can be used without history preservation', async () => {
468
- const { tree } = setUpTestSharedTree({
469
- initialTree: simpleTestTree,
552
+ const { sharedTree, testTree } = createSimpleTestTree({
470
553
  localMode: true,
471
554
  summarizeHistory: false,
472
555
  });
473
- const editID = tree.editor.insert(newNode, StablePlace.before(left));
474
- const view = tree.currentView;
475
- const summary = tree.saveSummary();
476
- tree.loadSummary(summary);
556
+ const newNode = testTree.buildLeaf();
557
+ const { id } = sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree.left)));
558
+ const treeBefore = convertTreeNodes(getChangeNodeFromView(sharedTree.currentView), (node) => convertNodeDataIds(node, (id) => sharedTree.convertToStableNodeId(id)));
559
+ const summary = sharedTree.saveSummary();
560
+ const { tree: sharedTree2 } = setUpTestSharedTree();
561
+ sharedTree2.loadSummary(summary);
562
+ const treeAfter = convertTreeNodes(getChangeNodeFromView(sharedTree2.currentView), (node) => convertNodeDataIds(node, (id) => sharedTree2.convertToStableNodeId(id)));
477
563
  // The current state of the tree should be identical to the one contained in the old summary.
478
- expect(tree.currentView.equals(view)).to.be.true;
564
+ expect(deepCompareNodes(treeBefore, treeAfter)).to.be.true;
479
565
  // The history should have been dropped by the default handling behavior.
480
566
  // It will contain a single entry setting the tree to equal the head revision.
481
- expect(tree.edits.length).to.equal(1);
482
- expect(await tree.edits.tryGetEdit(editID)).to.be.undefined;
567
+ expect(sharedTree2.edits.length).to.equal(1);
568
+ expect(await sharedTree2.edits.tryGetEdit(id)).to.be.undefined;
569
+ });
570
+ it('correctly handles payloads at the root', () => {
571
+ var _a;
572
+ const payload = 'foo';
573
+ const { tree, containerRuntimeFactory } = setUpTestSharedTree({ summarizeHistory: false });
574
+ tree.applyEdit(Change.setPayload(tree.currentView.root, payload));
575
+ containerRuntimeFactory.processAllMessages();
576
+ const summary = tree.saveSummary();
577
+ const { tree: tree2 } = setUpTestSharedTree({ summarizeHistory: false });
578
+ tree2.loadSummary(summary);
579
+ expect((_a = tree2.currentView.tryGetViewNode(tree2.currentView.root)) === null || _a === void 0 ? void 0 : _a.payload).to.equal(payload);
483
580
  });
484
581
  // TODO:#49901: Enable these tests once we write edit chunk handles to summaries
485
582
  it.skip('does not swallow errors in asynchronous blob uploading', async () => {
486
583
  const errorMessage = 'Simulated exception in uploadBlob';
487
- const { tree, componentRuntime, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
584
+ const { sharedTree, testTree, componentRuntime, containerRuntimeFactory } = createSimpleTestTree({
585
+ localMode: false,
586
+ });
488
587
  componentRuntime.uploadBlob = async () => {
489
588
  throw new Error(errorMessage);
490
589
  };
491
590
  let treeErrorEventWasInvoked = false;
492
- tree.on('error', (error) => {
591
+ sharedTree.on('error', (error) => {
493
592
  treeErrorEventWasInvoked = true;
494
593
  expect(error).to.have.property('message').which.equals(errorMessage);
495
594
  });
496
595
  // Generate enough edits to cause a chunk upload.
497
- for (let i = 0; i < tree.edits.editsPerChunk / 2 + 1; i++) {
498
- const insertee = makeEmptyNode();
499
- tree.editor.insert(insertee, StablePlace.before(left));
500
- tree.editor.delete(StableRange.only(insertee));
596
+ for (let i = 0; i < sharedTree.edits.editsPerChunk / 2 + 1; i++) {
597
+ const insertee = testTree.buildLeaf(testTree.generateNodeId());
598
+ sharedTree.applyEdit(...Change.insertTree(insertee, StablePlace.before(testTree.left)));
599
+ sharedTree.applyEdit(Change.delete(StableRange.only(insertee)));
501
600
  }
502
601
  containerRuntimeFactory.processAllMessages();
503
- tree.saveSummary();
602
+ sharedTree.saveSummary();
504
603
  // Just waiting for the ChunksEmitted event here isn't sufficient, as the SharedTree error
505
604
  // will propagate in a separate promise chain.
506
- await delay(0);
605
+ await new Promise((resolve) => setTimeout(resolve, 0));
507
606
  expect(treeErrorEventWasInvoked).to.equal(true, 'SharedTree error was never raised');
508
607
  });
509
608
  });
510
- describe('correctly diffs snapshots', () => {
511
- it('that are the same object', () => {
512
- const id = uuidv4();
513
- const snapshot = Snapshot.fromTree(makeEmptyNode(id));
514
- expect(snapshot.delta(snapshot)).deep.equals({
515
- changed: [],
516
- added: [],
517
- removed: [],
518
- });
519
- });
520
- it('that have the same tree', () => {
521
- const node = makeEmptyNode();
522
- const snapshotA = Snapshot.fromTree(node);
523
- const snapshotB = Snapshot.fromTree(node);
524
- expect(snapshotA.delta(snapshotB)).deep.equals({
525
- changed: [],
526
- added: [],
527
- removed: [],
528
- });
529
- });
530
- it('with different root ids', () => {
531
- const snapshotA = Snapshot.fromTree(makeEmptyNode());
532
- const snapshotB = Snapshot.fromTree(makeEmptyNode());
533
- expect(() => snapshotA.delta(snapshotB)).to.throw('Delta can only be calculated between snapshots that share a root');
534
- });
535
- it('with different subtrees', () => {
536
- const rootId = uuidv4();
537
- const subtreeA = uuidv4();
538
- const subtreeB = uuidv4();
539
- const leafA = makeEmptyNode();
540
- const leafB = makeEmptyNode();
541
- const rootA = {
542
- identifier: rootId,
543
- definition: 'node',
544
- traits: {
545
- children: [
546
- {
547
- identifier: subtreeA,
548
- definition: 'node',
549
- traits: { children: [leafA] },
550
- },
551
- ],
552
- },
553
- };
554
- const rootB = {
555
- identifier: rootId,
556
- definition: 'node',
557
- traits: {
558
- children: [
559
- {
560
- identifier: subtreeB,
561
- definition: 'node',
562
- traits: { children: [leafB] },
563
- },
564
- ],
565
- },
566
- };
567
- const snapshotA = Snapshot.fromTree(rootA);
568
- const snapshotB = Snapshot.fromTree(rootB);
569
- const delta = snapshotA.delta(snapshotB);
570
- expect(delta.changed).deep.equals([rootId]);
571
- expect(delta.removed.length).equals(2);
572
- expect(delta.added.length).equals(2);
573
- expect(delta.removed).contains(subtreeA);
574
- expect(delta.removed).contains(leafA.identifier);
575
- expect(delta.added).contains(subtreeB);
576
- expect(delta.added).contains(leafB.identifier);
577
- });
578
- it('with different payloads', () => {
579
- const rootId = uuidv4();
580
- const nodeA = {
581
- identifier: rootId,
582
- definition: 'node',
583
- payload: 'test1',
584
- traits: {},
585
- };
586
- const nodeB = {
587
- identifier: rootId,
588
- definition: 'node',
589
- payload: 'test2',
590
- traits: {},
591
- };
592
- const snapshotA = Snapshot.fromTree(nodeA);
593
- const snapshotB = Snapshot.fromTree(nodeB);
594
- const delta = snapshotA.delta(snapshotB);
595
- expect(delta.changed).deep.equals([rootId]);
596
- expect(delta.removed).deep.equals([]);
597
- expect(delta.added).deep.equals([]);
598
- });
599
- it('after an insert', () => {
600
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
601
- const snapshotA = tree.currentView;
602
- const insertedNode = makeEmptyNode();
603
- tree.editor.insert(insertedNode, StablePlace.before(left));
604
- const snapshotB = tree.currentView;
605
- const delta = snapshotA.delta(snapshotB);
606
- assert(delta);
607
- expect(delta.changed).deep.equals([simpleTestTree.identifier]);
608
- expect(delta.removed).deep.equals([]);
609
- expect(delta.added).deep.equals([insertedNode.identifier]);
610
- });
611
- it('after a delete', () => {
612
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
613
- const snapshotA = tree.currentView;
614
- tree.editor.delete(left);
615
- const snapshotB = tree.currentView;
616
- const delta = snapshotA.delta(snapshotB);
617
- assert(delta);
618
- expect(delta.changed).deep.equals([simpleTestTree.identifier]);
619
- expect(delta.removed).deep.equals([left.identifier]);
620
- expect(delta.added).deep.equals([]);
621
- });
622
- it('after a move', () => {
623
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
624
- const snapshotA = tree.currentView;
625
- tree.editor.move(left, StablePlace.after(right));
626
- const snapshotB = tree.currentView;
627
- const delta = snapshotA.delta(snapshotB);
628
- assert(delta);
629
- expect(delta.changed).deep.equals([simpleTestTree.identifier]);
630
- expect(delta.removed).deep.equals([]);
631
- expect(delta.added).deep.equals([]);
632
- });
633
- });
634
609
  describe('handles', () => {
635
610
  it('can reference a node', () => {
636
611
  // Test that a handle can wrap a node and retrieve that node's properties
637
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
638
- const leftHandle = new TreeNodeHandle(tree.currentView, left.identifier);
639
- expect(areNodesEquivalent(left, leftHandle)).to.be.true;
640
- expect(areNodesEquivalent(right, leftHandle)).to.be.false;
612
+ const { sharedTree, testTree } = createSimpleTestTree();
613
+ const leftHandle = new TreeNodeHandle(sharedTree.currentView, testTree.left.identifier);
614
+ expect(areNodesEquivalent(testTree.left, leftHandle)).to.be.true;
615
+ expect(areNodesEquivalent(testTree.right, leftHandle)).to.be.false;
641
616
  });
642
617
  it('can create handles from children', () => {
643
618
  // Test that when retrieving children via the "traits" property of a handle, the
644
619
  // children are also wrapped in handles
645
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
646
- const rootHandle = new TreeNodeHandle(tree.currentView, simpleTestTree.identifier);
647
- expect(areNodesEquivalent(simpleTestTree, rootHandle)).to.be.true;
620
+ const { sharedTree, testTree } = createSimpleTestTree();
621
+ const rootHandle = new TreeNodeHandle(sharedTree.currentView, testTree.identifier);
622
+ expect(areNodesEquivalent(testTree, rootHandle)).to.be.true;
648
623
  const leftHandle = rootHandle.traits.left[0];
649
- expect(areNodesEquivalent(left, leftHandle)).to.be.true;
624
+ expect(areNodesEquivalent(testTree.left, leftHandle)).to.be.true;
650
625
  expect(leftHandle instanceof TreeNodeHandle).to.be.true;
651
626
  });
652
627
  it('do not update when the current view of the tree changes', () => {
653
- // Unlike CurrentTreeNodeHandles, SnapshotTreeNodeHandles should never change
654
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
655
- const leftHandle = new TreeNodeHandle(tree.currentView, left.identifier);
628
+ const { sharedTree, testTree } = createSimpleTestTree();
629
+ const leftHandle = new TreeNodeHandle(sharedTree.currentView, testTree.left.identifier);
656
630
  expect(leftHandle.traits.right).to.be.undefined;
657
631
  // Move "right" under "left"
658
- tree.editor.move(right, StablePlace.atStartOf({ parent: left.identifier, label: rightTraitLabel }));
632
+ sharedTree.applyEdit(...Change.move(StableRange.only(testTree.right), StablePlace.atStartOf({ parent: testTree.left.identifier, label: testTree.right.traitLabel })));
659
633
  expect(leftHandle.traits.right).to.be.undefined;
660
634
  });
661
- it('can be fully demanded', () => {
662
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
663
- const rootHandle = new TreeNodeHandle(tree.currentView, simpleTestTree.identifier);
664
- const rootNode = rootHandle.demandTree();
665
- expect(areNodesEquivalent(simpleTestTree, rootNode)).to.be.true;
666
- const printBeforeDemand = JSON.stringify(rootNode);
667
- // Demand the tree by walking into its traits. If they were lazy, this would change the `rootNode` object.
668
- expect(areNodesEquivalent(left, rootNode.traits.left[0])).to.be.true;
669
- expect(areNodesEquivalent(right, rootNode.traits.right[0])).to.be.true;
670
- // Ensure that they were _not_ lazy by comparing with the initial print of the tree
671
- expect(JSON.stringify(rootNode)).equals(printBeforeDemand);
672
- });
673
- it('implement toString', () => {
674
- const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
675
- const rootHandle = new TreeNodeHandle(tree.currentView, simpleTestTree.identifier);
676
- const print = `${rootHandle}`;
677
- // Shouldn't print the default toString for objects
678
- expect(print.startsWith('[object')).to.be.false;
679
- });
680
635
  });
681
636
  describe('telemetry', () => {
682
- it('decorates events with the correct properties', async () => {
683
- // Test that a handle can wrap a node and retrieve that node's properties
684
- const events = [];
685
- const { tree, containerRuntimeFactory } = setUpTestSharedTree({
686
- initialTree: simpleTestTree,
687
- logger: { send: (event) => events.push(event) },
637
+ describe('useFailedSequencedEditTelemetry', () => {
638
+ it('decorates events with the correct properties', async () => {
639
+ // Test that a handle can wrap a node and retrieve that node's properties
640
+ const events = [];
641
+ const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
642
+ logger: { send: (event) => events.push(event) },
643
+ allowInvalid: true,
644
+ });
645
+ useFailedSequencedEditTelemetry(sharedTree);
646
+ // Invalid edit
647
+ sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
648
+ containerRuntimeFactory.processAllMessages();
649
+ // Force demand, which will cause a telemetry event for the invalid edit to be emitted
650
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
651
+ expect(events.length).is.greaterThan(0);
652
+ events.forEach((event) => {
653
+ expect(isSharedTreeEvent(event)).is.true;
654
+ });
655
+ });
656
+ it('is logged for invalid locally generated edits when those edits are sequenced', async () => {
657
+ const events = [];
658
+ const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
659
+ logger: { send: (event) => events.push(event) },
660
+ allowInvalid: true,
661
+ });
662
+ useFailedSequencedEditTelemetry(sharedTree);
663
+ // Invalid edit
664
+ sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
665
+ expect(events.length).equals(0);
666
+ containerRuntimeFactory.processAllMessages();
667
+ // Force demand, which will cause a telemetry event for the invalid edit to be emitted
668
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
669
+ expect(events.length).equals(1);
670
+ expect(events[0].category).equals('generic');
671
+ expect(events[0].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
672
+ });
673
+ it('can be disabled and re-enabled', async () => {
674
+ const events = [];
675
+ const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
676
+ logger: { send: (event) => events.push(event) },
677
+ allowInvalid: true,
678
+ });
679
+ const { disable } = useFailedSequencedEditTelemetry(sharedTree);
680
+ sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
681
+ expect(events.length).equals(0);
682
+ containerRuntimeFactory.processAllMessages();
683
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
684
+ expect(events.length).equals(1);
685
+ expect(events[0].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
686
+ disable();
687
+ sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
688
+ containerRuntimeFactory.processAllMessages();
689
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
690
+ expect(events.length).equals(1);
691
+ useFailedSequencedEditTelemetry(sharedTree);
692
+ sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
693
+ containerRuntimeFactory.processAllMessages();
694
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
695
+ expect(events.length).equals(2);
696
+ expect(events[1].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
697
+ });
698
+ it('is not logged for valid edits', async () => {
699
+ const events = [];
700
+ const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
701
+ logger: { send: (event) => events.push(event) },
702
+ });
703
+ useFailedSequencedEditTelemetry(sharedTree);
704
+ sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
705
+ containerRuntimeFactory.processAllMessages();
706
+ await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
707
+ expect(events.length).equals(0);
708
+ });
709
+ it('is not logged for remote edits', async () => {
710
+ const events = [];
711
+ const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree({
712
+ logger: { send: (event) => events.push(event) },
713
+ allowInvalid: true,
714
+ localMode: false,
715
+ });
716
+ const { sharedTree: sharedTree2, testTree: testTree2 } = createSimpleTestTree({
717
+ containerRuntimeFactory,
718
+ id: 'secondTestSharedTree',
719
+ localMode: false,
720
+ });
721
+ useFailedSequencedEditTelemetry(sharedTree1);
722
+ sharedTree2.applyEdit(...Change.insertTree([testTree2.buildLeaf()], StablePlace.after(testTree2.buildLeaf(testTree2.generateNodeId()))));
723
+ containerRuntimeFactory.processAllMessages();
724
+ await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
725
+ expect(events.length).equals(0);
726
+ });
727
+ });
728
+ });
729
+ describe('Events', () => {
730
+ it('fires an event when an edit is committed', () => {
731
+ const { sharedTree, testTree } = createSimpleTestTree();
732
+ let eventCount = 0;
733
+ let editIdFromEvent;
734
+ sharedTree.on(SharedTreeEvent.EditCommitted, (args) => {
735
+ expect(args.local).true;
736
+ expect(args.tree).equals(sharedTree);
737
+ editIdFromEvent = args.editId;
738
+ eventCount += 1;
739
+ });
740
+ // Invalid change
741
+ const invalidEdit = sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
742
+ expect(editIdFromEvent).equals(invalidEdit.id);
743
+ expect(eventCount).equals(1);
744
+ // Valid change
745
+ const { id } = sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
746
+ expect(editIdFromEvent).equals(id);
747
+ expect(eventCount).equals(2);
748
+ });
749
+ it('fires an event when a sequenced edit is applied', async () => {
750
+ const { sharedTree: sharedTree1, testTree, containerRuntimeFactory, } = createSimpleTestTree({
688
751
  allowInvalid: true,
752
+ localMode: false,
689
753
  });
690
- // Invalid edit
691
- tree.editor.insert(makeEmptyNode(), StablePlace.after(makeEmptyNode()));
692
- containerRuntimeFactory.processAllMessages();
693
- // Force demand, which will cause a telemetry event for the invalid edit to be emitted
694
- await tree.logViewer.getSnapshot(Number.POSITIVE_INFINITY);
695
- expect(events.length).is.greaterThan(0);
696
- events.forEach((event) => {
697
- expect(isSharedTreeEvent(event)).is.true;
754
+ const { tree: sharedTree2 } = setUpTestSharedTree({
755
+ containerRuntimeFactory,
756
+ id: 'secondTestSharedTree',
757
+ localMode: false,
698
758
  });
759
+ containerRuntimeFactory.processAllMessages();
760
+ await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
761
+ const eventArgs = [];
762
+ sharedTree1.on(SharedTreeEvent.SequencedEditApplied, (args) => eventArgs.push(args));
763
+ // Invalid change
764
+ const change = Change.setPayload(testTree.generateNodeId(), 42);
765
+ const invalidEdit = sharedTree1.applyEdit(change);
766
+ containerRuntimeFactory.processAllMessages();
767
+ await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
768
+ expect(eventArgs.length).equals(1);
769
+ expect(eventArgs[0].edit.id).equals(invalidEdit.id);
770
+ expect(eventArgs[0].wasLocal).equals(true);
771
+ expect(eventArgs[0].reconciliationPath.length).equals(0);
772
+ expect(eventArgs[0].outcome.status).equals(EditStatus.Invalid);
773
+ // Valid change
774
+ const validEdit1 = sharedTree2.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.left.translateId(sharedTree2))));
775
+ // Valid change
776
+ const validEdit2 = sharedTree1.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
777
+ containerRuntimeFactory.processAllMessages();
778
+ await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
779
+ expect(eventArgs.length).equals(3);
780
+ expect(eventArgs[1].edit.id).equals(validEdit1.id);
781
+ expect(eventArgs[1].wasLocal).equals(false);
782
+ expect(eventArgs[1].reconciliationPath.length).equals(0);
783
+ expect(eventArgs[1].outcome.status).equals(EditStatus.Applied);
784
+ expect(eventArgs[2].edit.id).equals(validEdit2.id);
785
+ expect(eventArgs[2].wasLocal).equals(true);
786
+ expect(eventArgs[2].reconciliationPath.length).equals(1);
787
+ expect(eventArgs[2].outcome.status).equals(EditStatus.Applied);
699
788
  });
700
789
  });
790
+ /**
791
+ * This test is a slightly minified regression test for an issue discovered by fuzz testing.
792
+ * It demonstrates issues with clients using writeFormat v0.1.1 and mixed `summarizeHistory` values.
793
+ * The problem is illustrated by the following scenario:
794
+ * 1. Client A and client B join a session. A does not summarize history, but B does.
795
+ * 2. A is elected to be the summarizer.
796
+ * 3. Client A and B make 50 edits (half a chunks' worth), then idle.
797
+ * 4. Client A summarizes. Since it does not summarize history, the summary it produces has a single edit.
798
+ * 5. Client C joins, configured to write history.
799
+ * 6. The three clients collaborate further for another 50/51 edits.
800
+ *
801
+ * At this point in time, client B thinks the first edit chunk is full, but client C thinks it's only half-full.
802
+ * The entire edit compression scheme is built upon assuming clients agree where the chunk boundaries are, so this
803
+ * generally leads to correctness issues. The fuzz test reproed a similar scenario, and what ultimately caused
804
+ * failure is a newly-loaded client being shocked at a chunk with `startRevision: 400` uploaded (when it thinks
805
+ * there has only been one edit).
806
+ *
807
+ * To fix this, we need to incorporate a scheme where all clients agree on chunk boundaries (e.g., by including the
808
+ * total number of edits even in no-history summaries).
809
+ *
810
+ * In the meantime, we are forbidding collaboration of no-history clients and history clients.
811
+ */
812
+ it('can be initialized on multiple clients with different `summarizeHistory` values', async () => {
813
+ const { tree, testObjectProvider, container } = await setUpLocalServerTestSharedTree({
814
+ writeFormat,
815
+ summarizeHistory: false,
816
+ });
817
+ testObjectProvider.logger.registerExpectedEvent({ eventName: 'fluid:telemetry:Batching:LengthTooBig' }, { eventName: 'fluid:telemetry:Batching:LengthTooBig' }, { eventName: 'fluid:telemetry:Batching:LengthTooBig' });
818
+ applyNoop(tree);
819
+ await testObjectProvider.ensureSynchronized();
820
+ const firstSummaryVersion = await waitForSummary(container);
821
+ const { tree: tree2 } = await setUpLocalServerTestSharedTree({
822
+ writeFormat,
823
+ testObjectProvider,
824
+ summarizeHistory: true,
825
+ headers: { [LoaderHeader.version]: firstSummaryVersion },
826
+ });
827
+ // Apply enough edits for the upload of a few edit chunks, and some extra so future chunks are misaligned
828
+ for (let i = 0; i < (5 * editsPerChunk) / 2; i++) {
829
+ applyNoop(tree);
830
+ }
831
+ const secondSummaryVersion = await waitForSummary(container);
832
+ const { tree: tree3 } = await setUpLocalServerTestSharedTree({
833
+ writeFormat,
834
+ testObjectProvider,
835
+ summarizeHistory: true,
836
+ headers: { [LoaderHeader.version]: secondSummaryVersion },
837
+ });
838
+ // Verify we loaded a no-history summary.
839
+ expect(tree3.editsInternal.length).to.equal(1);
840
+ let unexpectedHistoryChunkCount = 0;
841
+ tree3.on(SharedTreeDiagnosticEvent.UnexpectedHistoryChunk, () => unexpectedHistoryChunkCount++);
842
+ await testObjectProvider.ensureSynchronized();
843
+ // Apply enough edits to guarantee another chunk upload occurs.
844
+ for (let i = 0; i < editsPerChunk; i++) {
845
+ applyNoop(tree2);
846
+ }
847
+ await testObjectProvider.ensureSynchronized();
848
+ // If tree 2 didn't change its write format, it would attempt to upload the above chunk with start revision 200, which is past
849
+ // how many sequenced edits tree 3 thinks there are.
850
+ expect(unexpectedHistoryChunkCount).to.equal(0);
851
+ }).timeout(/* double summarization can take some time */ 20000);
852
+ // This functionality was only implemented in format 0.1.1.
853
+ if (writeFormat !== WriteFormat.v0_0_2) {
854
+ describe('String interning and tree compression', () => {
855
+ function getMutableStringInterner(tree) {
856
+ const summary = tree.saveSummary();
857
+ switch (summary.version) {
858
+ case WriteFormat.v0_0_2:
859
+ return new MutableStringInterner();
860
+ case WriteFormat.v0_1_1:
861
+ return new MutableStringInterner(summary.internedStrings);
862
+ default:
863
+ fail(`Invalid summary format: ${summary.version}`);
864
+ }
865
+ }
866
+ it('compress ops via interning and tree compression and decompress when processing edits', () => {
867
+ const { sharedTree: tree, testTree, containerRuntimeFactory, } = createSimpleTestTree({ writeFormat });
868
+ const { tree: secondTree } = setUpTestSharedTree({ containerRuntimeFactory, writeFormat });
869
+ const remoteRuntime = containerRuntimeFactory.createContainerRuntime(new MockFluidDataStoreRuntime());
870
+ const newNode = testTree.buildLeaf(testTree.generateNodeId());
871
+ tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
872
+ tree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
873
+ const factory = remoteRuntime.factory;
874
+ const messages = factory.messages;
875
+ expect(messages.length).to.equal(3);
876
+ for (const message of messages.slice(1)) {
877
+ // After the initial setup edit, common definitions should be interned
878
+ for (const change of message.contents.edit.changes) {
879
+ if (change.type === ChangeTypeInternal.CompressedBuild) {
880
+ const stringifiedContents = JSON.stringify(message.contents);
881
+ expect(stringifiedContents).to.not.include(SimpleTestTree.leftTraitLabel);
882
+ }
883
+ }
884
+ }
885
+ expect(tree.equals(secondTree)).to.be.false;
886
+ containerRuntimeFactory.processAllMessages();
887
+ const { internedStrings } = tree.saveSummary();
888
+ const insertEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(1));
889
+ const moveEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(2));
890
+ const insertEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(1));
891
+ const moveEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(2));
892
+ expect(insertEdit).to.deep.equal(insertEdit2);
893
+ expect(moveEdit).to.deep.equal(moveEdit2);
894
+ expect(tree.equals(secondTree)).to.be.true;
895
+ expect(internedStrings).to.include(SimpleTestTree.leftTraitLabel);
896
+ expect(internedStrings).to.include(newNode.definition);
897
+ });
898
+ it('compress summaries via interning and tree compression on save and decompress on load', () => {
899
+ const { sharedTree: tree, testTree: testTree, containerRuntimeFactory, } = createSimpleTestTree({ writeFormat });
900
+ const newNode = testTree.buildLeaf(testTree.generateNodeId());
901
+ tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
902
+ tree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
903
+ containerRuntimeFactory.processAllMessages();
904
+ const summary = tree.saveSummary();
905
+ expect(summary.internedStrings).to.not.be.undefined;
906
+ expect(summary.internedStrings.length).to.equal(5);
907
+ const interner = new MutableStringInterner(summary.internedStrings);
908
+ const treeCompressor = new InterningTreeCompressor();
909
+ const expectedCompressedTree = treeCompressor.compress(getChangeNodeFromView(tree.currentView), interner, sequencedIdNormalizer(getIdNormalizerFromSharedTree(tree)));
910
+ expect(summary.currentTree).deep.equal(expectedCompressedTree);
911
+ const { tree: secondTree } = setUpTestSharedTree({ writeFormat });
912
+ expect(tree.equals(secondTree)).to.be.false;
913
+ secondTree.loadSummary(summary);
914
+ expect(tree.equals(secondTree)).to.be.true;
915
+ });
916
+ it('compress and decompress edit chunks via interning and tree compression', async () => {
917
+ const { tree, testObjectProvider } = await setUpLocalServerTestSharedTree({
918
+ writeFormat,
919
+ });
920
+ const testTree = setUpTestTree(tree);
921
+ const uncompressedEdits = [
922
+ {
923
+ changes: tree.edits.getEditInSessionAtIndex(0).changes,
924
+ },
925
+ ];
926
+ // Apply enough edits for the upload of an edit chunk
927
+ for (let i = 0; i < tree.edits.editsPerChunk - 1; i++) {
928
+ const newNode = testTree.buildLeaf(testTree.generateNodeId());
929
+ const edit = tree.applyEditInternal(ChangeInternal.insertTree([newNode], StablePlace.after(testTree.left)));
930
+ uncompressedEdits.push({ changes: edit.changes });
931
+ }
932
+ await testObjectProvider.ensureSynchronized();
933
+ const interner = getMutableStringInterner(tree);
934
+ const expectedCompressedEdits = new SharedTreeEncoder_0_1_1(true).encodeEditChunk(uncompressedEdits, sequencedIdNormalizer(testTree), interner).edits;
935
+ // Apply one more edit so that an edit chunk gets uploaded
936
+ const newNode = testTree.buildLeaf(testTree.generateNodeId());
937
+ tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
938
+ // `ensureSynchronized` does not guarantee blob upload
939
+ await new Promise((resolve) => setImmediate(resolve));
940
+ // Wait for the ops to to be submitted and processed across the containers
941
+ await testObjectProvider.ensureSynchronized();
942
+ const summary = tree.saveSummary();
943
+ const { editHistory } = summary;
944
+ const { editChunks } = assertNotUndefined(editHistory);
945
+ expect(editChunks.length).to.equal(2);
946
+ const handle = editChunks[0].chunk;
947
+ expect(typeof handle.get).to.equal('function');
948
+ const chunkContents = JSON.parse(IsoBuffer.from(await handle.get()).toString());
949
+ expect(chunkContents.edits).to.deep.equal(expectedCompressedEdits);
950
+ const { tree: secondTree } = setUpTestSharedTree({ writeFormat });
951
+ expect(tree.equals(secondTree)).to.be.false;
952
+ secondTree.loadSummary(summary);
953
+ expect(tree.equals(secondTree)).to.be.true;
954
+ expect((await tree.edits.getEditAtIndex(2)).id).to.equal((await secondTree.edits.getEditAtIndex(2)).id);
955
+ });
956
+ });
957
+ }
701
958
  });
702
959
  }
703
960
  //# sourceMappingURL=SharedTreeTests.js.map