@fluidframework/tree 2.115.0 → 2.116.0

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 (325) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +5 -3
  3. package/api-report/tree.alpha.api.md +4 -2
  4. package/api-report/tree.beta.api.md +1 -1
  5. package/api-report/tree.legacy.beta.api.md +1 -1
  6. package/dist/codec/codec.d.ts +6 -6
  7. package/dist/codec/codec.d.ts.map +1 -1
  8. package/dist/codec/codec.js +3 -3
  9. package/dist/codec/codec.js.map +1 -1
  10. package/dist/codec/versioned/codec.d.ts +3 -3
  11. package/dist/codec/versioned/codec.d.ts.map +1 -1
  12. package/dist/codec/versioned/codec.js.map +1 -1
  13. package/dist/core/forest/editableForest.d.ts +2 -0
  14. package/dist/core/forest/editableForest.d.ts.map +1 -1
  15. package/dist/core/forest/editableForest.js.map +1 -1
  16. package/dist/core/index.d.ts +1 -1
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/index.js +2 -2
  19. package/dist/core/index.js.map +1 -1
  20. package/dist/core/tree/index.d.ts +1 -1
  21. package/dist/core/tree/index.d.ts.map +1 -1
  22. package/dist/core/tree/index.js +2 -2
  23. package/dist/core/tree/index.js.map +1 -1
  24. package/dist/core/tree/visitorUtils.d.ts +24 -3
  25. package/dist/core/tree/visitorUtils.d.ts.map +1 -1
  26. package/dist/core/tree/visitorUtils.js +24 -6
  27. package/dist/core/tree/visitorUtils.js.map +1 -1
  28. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts +30 -6
  29. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  30. package/dist/feature-libraries/chunked-forest/chunkedForest.js +146 -54
  31. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  32. package/dist/feature-libraries/comparison-forest/comparisonForest.d.ts +2 -1
  33. package/dist/feature-libraries/comparison-forest/comparisonForest.d.ts.map +1 -1
  34. package/dist/feature-libraries/comparison-forest/comparisonForest.js +87 -70
  35. package/dist/feature-libraries/comparison-forest/comparisonForest.js.map +1 -1
  36. package/dist/feature-libraries/detachedFieldIndexSummarizer.js.map +1 -1
  37. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts +5 -0
  38. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  39. package/dist/feature-libraries/forest-summary/forestSummarizer.js +127 -107
  40. package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  41. package/dist/feature-libraries/forest-summary/summaryTypes.d.ts +2 -2
  42. package/dist/feature-libraries/forest-summary/summaryTypes.d.ts.map +1 -1
  43. package/dist/feature-libraries/forest-summary/summaryTypes.js.map +1 -1
  44. package/dist/feature-libraries/indexing/anchorTreeIndex.js +1 -1
  45. package/dist/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  46. package/dist/feature-libraries/modular-schema/invert.js +1 -1
  47. package/dist/feature-libraries/modular-schema/invert.js.map +1 -1
  48. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  49. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +2 -11
  50. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  51. package/dist/feature-libraries/modular-schema/modularChangeUtils.d.ts +11 -16
  52. package/dist/feature-libraries/modular-schema/modularChangeUtils.d.ts.map +1 -1
  53. package/dist/feature-libraries/modular-schema/modularChangeUtils.js +10 -3
  54. package/dist/feature-libraries/modular-schema/modularChangeUtils.js.map +1 -1
  55. package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  56. package/dist/feature-libraries/object-forest/objectForest.js +34 -13
  57. package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
  58. package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts +2 -2
  59. package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
  60. package/dist/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  61. package/dist/feature-libraries/sequence-field/rebase.js +1 -1
  62. package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
  63. package/dist/feature-libraries/sequence-field/utils.js +1 -1
  64. package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
  65. package/dist/packageVersion.d.ts +1 -1
  66. package/dist/packageVersion.js +1 -1
  67. package/dist/packageVersion.js.map +1 -1
  68. package/dist/shared-tree/independentView.d.ts.map +1 -1
  69. package/dist/shared-tree/independentView.js +2 -4
  70. package/dist/shared-tree/independentView.js.map +1 -1
  71. package/dist/shared-tree/sharedTree.d.ts +30 -5
  72. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  73. package/dist/shared-tree/sharedTree.js +8 -8
  74. package/dist/shared-tree/sharedTree.js.map +1 -1
  75. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts +2 -2
  76. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  77. package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  78. package/dist/shared-tree/sharedTreeChangeEnricher.d.ts +25 -1
  79. package/dist/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  80. package/dist/shared-tree/sharedTreeChangeEnricher.js +24 -0
  81. package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  82. package/dist/shared-tree/treeCheckout.d.ts +21 -10
  83. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  84. package/dist/shared-tree/treeCheckout.js +48 -16
  85. package/dist/shared-tree/treeCheckout.js.map +1 -1
  86. package/dist/shared-tree-core/branchCommitEnricher.d.ts +2 -1
  87. package/dist/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
  88. package/dist/shared-tree-core/branchCommitEnricher.js +3 -2
  89. package/dist/shared-tree-core/branchCommitEnricher.js.map +1 -1
  90. package/dist/shared-tree-core/changeEnricher.d.ts +3 -2
  91. package/dist/shared-tree-core/changeEnricher.d.ts.map +1 -1
  92. package/dist/shared-tree-core/changeEnricher.js.map +1 -1
  93. package/dist/shared-tree-core/defaultResubmitMachine.d.ts +9 -1
  94. package/dist/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
  95. package/dist/shared-tree-core/defaultResubmitMachine.js +7 -2
  96. package/dist/shared-tree-core/defaultResubmitMachine.js.map +1 -1
  97. package/dist/shared-tree-core/editManager.d.ts.map +1 -1
  98. package/dist/shared-tree-core/editManager.js +2 -2
  99. package/dist/shared-tree-core/editManager.js.map +1 -1
  100. package/dist/shared-tree-core/editManagerCodecs.d.ts +2 -2
  101. package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  102. package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
  103. package/dist/shared-tree-core/editManagerSummarizer.d.ts +2 -2
  104. package/dist/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
  105. package/dist/shared-tree-core/editManagerSummarizer.js.map +1 -1
  106. package/dist/shared-tree-core/messageCodecs.d.ts +2 -2
  107. package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
  108. package/dist/shared-tree-core/messageCodecs.js.map +1 -1
  109. package/dist/shared-tree-core/sharedTreeCore.d.ts +11 -2
  110. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  111. package/dist/shared-tree-core/sharedTreeCore.js +11 -8
  112. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  113. package/dist/shared-tree-core/summaryTypes.d.ts +2 -2
  114. package/dist/shared-tree-core/summaryTypes.d.ts.map +1 -1
  115. package/dist/shared-tree-core/summaryTypes.js.map +1 -1
  116. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +1 -1
  117. package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  118. package/dist/simple-tree/api/storedSchema.d.ts +2 -2
  119. package/dist/simple-tree/api/storedSchema.d.ts.map +1 -1
  120. package/dist/simple-tree/api/storedSchema.js.map +1 -1
  121. package/dist/simple-tree/api/treeChangeEvents.d.ts +5 -1
  122. package/dist/simple-tree/api/treeChangeEvents.d.ts.map +1 -1
  123. package/dist/simple-tree/api/treeChangeEvents.js.map +1 -1
  124. package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  125. package/dist/simple-tree/node-kinds/array/arrayNode.js +10 -0
  126. package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  127. package/dist/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
  128. package/dist/simple-tree/node-kinds/map/mapNode.js +19 -2
  129. package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  130. package/dist/tableSchema.d.ts +1 -1
  131. package/dist/tableSchema.js +1 -1
  132. package/dist/tableSchema.js.map +1 -1
  133. package/dist/treeFactory.d.ts.map +1 -1
  134. package/dist/treeFactory.js +1 -1
  135. package/dist/treeFactory.js.map +1 -1
  136. package/dist/util/breakable.d.ts +14 -2
  137. package/dist/util/breakable.d.ts.map +1 -1
  138. package/dist/util/breakable.js +9 -1
  139. package/dist/util/breakable.js.map +1 -1
  140. package/dist/util/compressedIds.d.ts +2 -2
  141. package/dist/util/compressedIds.d.ts.map +1 -1
  142. package/dist/util/compressedIds.js +1 -6
  143. package/dist/util/compressedIds.js.map +1 -1
  144. package/lib/codec/codec.d.ts +6 -6
  145. package/lib/codec/codec.d.ts.map +1 -1
  146. package/lib/codec/codec.js +3 -3
  147. package/lib/codec/codec.js.map +1 -1
  148. package/lib/codec/versioned/codec.d.ts +3 -3
  149. package/lib/codec/versioned/codec.d.ts.map +1 -1
  150. package/lib/codec/versioned/codec.js.map +1 -1
  151. package/lib/core/forest/editableForest.d.ts +2 -0
  152. package/lib/core/forest/editableForest.d.ts.map +1 -1
  153. package/lib/core/forest/editableForest.js.map +1 -1
  154. package/lib/core/index.d.ts +1 -1
  155. package/lib/core/index.d.ts.map +1 -1
  156. package/lib/core/index.js +1 -1
  157. package/lib/core/index.js.map +1 -1
  158. package/lib/core/tree/index.d.ts +1 -1
  159. package/lib/core/tree/index.d.ts.map +1 -1
  160. package/lib/core/tree/index.js +1 -1
  161. package/lib/core/tree/index.js.map +1 -1
  162. package/lib/core/tree/visitorUtils.d.ts +24 -3
  163. package/lib/core/tree/visitorUtils.d.ts.map +1 -1
  164. package/lib/core/tree/visitorUtils.js +22 -4
  165. package/lib/core/tree/visitorUtils.js.map +1 -1
  166. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts +30 -6
  167. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  168. package/lib/feature-libraries/chunked-forest/chunkedForest.js +146 -56
  169. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  170. package/lib/feature-libraries/comparison-forest/comparisonForest.d.ts +2 -1
  171. package/lib/feature-libraries/comparison-forest/comparisonForest.d.ts.map +1 -1
  172. package/lib/feature-libraries/comparison-forest/comparisonForest.js +88 -70
  173. package/lib/feature-libraries/comparison-forest/comparisonForest.js.map +1 -1
  174. package/lib/feature-libraries/detachedFieldIndexSummarizer.js.map +1 -1
  175. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts +5 -0
  176. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  177. package/lib/feature-libraries/forest-summary/forestSummarizer.js +129 -108
  178. package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  179. package/lib/feature-libraries/forest-summary/summaryTypes.d.ts +2 -2
  180. package/lib/feature-libraries/forest-summary/summaryTypes.d.ts.map +1 -1
  181. package/lib/feature-libraries/forest-summary/summaryTypes.js.map +1 -1
  182. package/lib/feature-libraries/indexing/anchorTreeIndex.js +1 -1
  183. package/lib/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  184. package/lib/feature-libraries/modular-schema/invert.js +2 -2
  185. package/lib/feature-libraries/modular-schema/invert.js.map +1 -1
  186. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  187. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +2 -11
  188. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  189. package/lib/feature-libraries/modular-schema/modularChangeUtils.d.ts +11 -16
  190. package/lib/feature-libraries/modular-schema/modularChangeUtils.d.ts.map +1 -1
  191. package/lib/feature-libraries/modular-schema/modularChangeUtils.js +10 -3
  192. package/lib/feature-libraries/modular-schema/modularChangeUtils.js.map +1 -1
  193. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  194. package/lib/feature-libraries/object-forest/objectForest.js +35 -14
  195. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  196. package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts +2 -2
  197. package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
  198. package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  199. package/lib/feature-libraries/sequence-field/rebase.js +2 -2
  200. package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
  201. package/lib/feature-libraries/sequence-field/utils.js +1 -1
  202. package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
  203. package/lib/packageVersion.d.ts +1 -1
  204. package/lib/packageVersion.js +1 -1
  205. package/lib/packageVersion.js.map +1 -1
  206. package/lib/shared-tree/independentView.d.ts.map +1 -1
  207. package/lib/shared-tree/independentView.js +3 -5
  208. package/lib/shared-tree/independentView.js.map +1 -1
  209. package/lib/shared-tree/sharedTree.d.ts +30 -5
  210. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  211. package/lib/shared-tree/sharedTree.js +8 -8
  212. package/lib/shared-tree/sharedTree.js.map +1 -1
  213. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts +2 -2
  214. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  215. package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  216. package/lib/shared-tree/sharedTreeChangeEnricher.d.ts +25 -1
  217. package/lib/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  218. package/lib/shared-tree/sharedTreeChangeEnricher.js +24 -0
  219. package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  220. package/lib/shared-tree/treeCheckout.d.ts +21 -10
  221. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  222. package/lib/shared-tree/treeCheckout.js +48 -16
  223. package/lib/shared-tree/treeCheckout.js.map +1 -1
  224. package/lib/shared-tree-core/branchCommitEnricher.d.ts +2 -1
  225. package/lib/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
  226. package/lib/shared-tree-core/branchCommitEnricher.js +3 -2
  227. package/lib/shared-tree-core/branchCommitEnricher.js.map +1 -1
  228. package/lib/shared-tree-core/changeEnricher.d.ts +3 -2
  229. package/lib/shared-tree-core/changeEnricher.d.ts.map +1 -1
  230. package/lib/shared-tree-core/changeEnricher.js.map +1 -1
  231. package/lib/shared-tree-core/defaultResubmitMachine.d.ts +9 -1
  232. package/lib/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
  233. package/lib/shared-tree-core/defaultResubmitMachine.js +7 -2
  234. package/lib/shared-tree-core/defaultResubmitMachine.js.map +1 -1
  235. package/lib/shared-tree-core/editManager.d.ts.map +1 -1
  236. package/lib/shared-tree-core/editManager.js +2 -2
  237. package/lib/shared-tree-core/editManager.js.map +1 -1
  238. package/lib/shared-tree-core/editManagerCodecs.d.ts +2 -2
  239. package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  240. package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
  241. package/lib/shared-tree-core/editManagerSummarizer.d.ts +2 -2
  242. package/lib/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
  243. package/lib/shared-tree-core/editManagerSummarizer.js.map +1 -1
  244. package/lib/shared-tree-core/messageCodecs.d.ts +2 -2
  245. package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
  246. package/lib/shared-tree-core/messageCodecs.js.map +1 -1
  247. package/lib/shared-tree-core/sharedTreeCore.d.ts +11 -2
  248. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  249. package/lib/shared-tree-core/sharedTreeCore.js +11 -8
  250. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  251. package/lib/shared-tree-core/summaryTypes.d.ts +2 -2
  252. package/lib/shared-tree-core/summaryTypes.d.ts.map +1 -1
  253. package/lib/shared-tree-core/summaryTypes.js.map +1 -1
  254. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +1 -1
  255. package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  256. package/lib/simple-tree/api/storedSchema.d.ts +2 -2
  257. package/lib/simple-tree/api/storedSchema.d.ts.map +1 -1
  258. package/lib/simple-tree/api/storedSchema.js.map +1 -1
  259. package/lib/simple-tree/api/treeChangeEvents.d.ts +5 -1
  260. package/lib/simple-tree/api/treeChangeEvents.d.ts.map +1 -1
  261. package/lib/simple-tree/api/treeChangeEvents.js.map +1 -1
  262. package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  263. package/lib/simple-tree/node-kinds/array/arrayNode.js +10 -0
  264. package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  265. package/lib/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
  266. package/lib/simple-tree/node-kinds/map/mapNode.js +19 -2
  267. package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  268. package/lib/tableSchema.d.ts +1 -1
  269. package/lib/tableSchema.js +1 -1
  270. package/lib/tableSchema.js.map +1 -1
  271. package/lib/treeFactory.d.ts.map +1 -1
  272. package/lib/treeFactory.js +1 -1
  273. package/lib/treeFactory.js.map +1 -1
  274. package/lib/util/breakable.d.ts +14 -2
  275. package/lib/util/breakable.d.ts.map +1 -1
  276. package/lib/util/breakable.js +9 -1
  277. package/lib/util/breakable.js.map +1 -1
  278. package/lib/util/compressedIds.d.ts +2 -2
  279. package/lib/util/compressedIds.d.ts.map +1 -1
  280. package/lib/util/compressedIds.js +1 -6
  281. package/lib/util/compressedIds.js.map +1 -1
  282. package/package.json +27 -27
  283. package/src/codec/codec.ts +7 -7
  284. package/src/codec/versioned/codec.ts +5 -5
  285. package/src/core/forest/editableForest.ts +2 -0
  286. package/src/core/index.ts +1 -1
  287. package/src/core/tree/index.ts +1 -1
  288. package/src/core/tree/visitorUtils.ts +37 -12
  289. package/src/feature-libraries/chunked-forest/chunkedForest.ts +214 -59
  290. package/src/feature-libraries/comparison-forest/comparisonForest.ts +6 -1
  291. package/src/feature-libraries/detachedFieldIndexSummarizer.ts +2 -2
  292. package/src/feature-libraries/forest-summary/forestSummarizer.ts +10 -0
  293. package/src/feature-libraries/forest-summary/summaryTypes.ts +2 -2
  294. package/src/feature-libraries/indexing/anchorTreeIndex.ts +1 -1
  295. package/src/feature-libraries/modular-schema/invert.ts +2 -2
  296. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +2 -23
  297. package/src/feature-libraries/modular-schema/modularChangeUtils.ts +12 -21
  298. package/src/feature-libraries/object-forest/objectForest.ts +48 -14
  299. package/src/feature-libraries/schema-index/schemaSummarizer.ts +3 -3
  300. package/src/feature-libraries/sequence-field/rebase.ts +2 -2
  301. package/src/feature-libraries/sequence-field/utils.ts +1 -1
  302. package/src/packageVersion.ts +1 -1
  303. package/src/shared-tree/independentView.ts +5 -12
  304. package/src/shared-tree/sharedTree.ts +41 -10
  305. package/src/shared-tree/sharedTreeChangeCodecs.ts +2 -2
  306. package/src/shared-tree/sharedTreeChangeEnricher.ts +25 -1
  307. package/src/shared-tree/treeCheckout.ts +45 -16
  308. package/src/shared-tree-core/branchCommitEnricher.ts +6 -2
  309. package/src/shared-tree-core/changeEnricher.ts +7 -2
  310. package/src/shared-tree-core/defaultResubmitMachine.ts +5 -0
  311. package/src/shared-tree-core/editManager.ts +2 -4
  312. package/src/shared-tree-core/editManagerCodecs.ts +2 -2
  313. package/src/shared-tree-core/editManagerSummarizer.ts +3 -3
  314. package/src/shared-tree-core/messageCodecs.ts +2 -2
  315. package/src/shared-tree-core/sharedTreeCore.ts +29 -11
  316. package/src/shared-tree-core/summaryTypes.ts +2 -2
  317. package/src/simple-tree/api/snapshotCompatibilityChecker.ts +1 -1
  318. package/src/simple-tree/api/storedSchema.ts +2 -2
  319. package/src/simple-tree/api/treeChangeEvents.ts +5 -1
  320. package/src/simple-tree/node-kinds/array/arrayNode.ts +15 -0
  321. package/src/simple-tree/node-kinds/map/mapNode.ts +23 -2
  322. package/src/tableSchema.ts +1 -1
  323. package/src/treeFactory.ts +0 -1
  324. package/src/util/breakable.ts +9 -1
  325. package/src/util/compressedIds.ts +7 -13
@@ -12,7 +12,6 @@ import {
12
12
  type RevisionInfo,
13
13
  type RevisionTag,
14
14
  type TaggedChange,
15
- type TreeChunk,
16
15
  } from "../../core/index.js";
17
16
  import { brand, type Mutable, type RangeQueryResult } from "../../util/index.js";
18
17
  import {
@@ -33,12 +32,9 @@ import type { FlexFieldKind } from "./fieldKind.js";
33
32
  import { genericFieldKind } from "./genericFieldKind.js";
34
33
  import {
35
34
  newCrossFieldKeyTable,
36
- type CrossFieldKeyTable,
37
35
  type FieldChange,
38
36
  type FieldChangeMap,
39
- type FieldId,
40
37
  type ModularChangeset,
41
- type NoChangeConstraint,
42
38
  type NodeChangeset,
43
39
  type NodeId,
44
40
  } from "./modularChangeTypes.js";
@@ -47,22 +43,17 @@ export function hasConflicts(change: ModularChangeset): boolean {
47
43
  return (change.constraintViolationCount ?? 0) > 0;
48
44
  }
49
45
 
50
- export function makeModularChangeset(props?: {
51
- fieldChanges?: FieldChangeMap;
52
- nodeChanges?: ChangeAtomIdBTree<NodeChangeset>;
53
- nodeToParent?: ChangeAtomIdBTree<FieldId>;
54
- nodeAliases?: ChangeAtomIdBTree<NodeId>;
55
- crossFieldKeys?: CrossFieldKeyTable;
56
- maxId: number;
57
- revisions?: readonly RevisionInfo[];
58
- constraintViolationCount?: number;
59
- noChangeConstraint?: NoChangeConstraint;
60
- noChangeConstraintOnRevert?: NoChangeConstraint;
61
- builds?: ChangeAtomIdBTree<TreeChunk>;
62
- destroys?: ChangeAtomIdBTree<number>;
63
- refreshers?: ChangeAtomIdBTree<TreeChunk>;
64
- }): ModularChangeset {
65
- const p = props ?? { maxId: -1 };
46
+ /**
47
+ * Creates a normalized changeset from the provided properties.
48
+ *
49
+ * Required changeset maps are initialized when omitted, and empty optional properties are not
50
+ * included in the returned changeset. `maxId` accepts an unbranded allocator ID; `undefined`
51
+ * indicates that the changeset contains no IDs.
52
+ */
53
+ export function makeModularChangeset(
54
+ props: Partial<Omit<ModularChangeset, "maxId">> & { maxId?: number } = {},
55
+ ): ModularChangeset {
56
+ const p = props;
66
57
  const changeset: Mutable<ModularChangeset> = {
67
58
  fieldChanges: p.fieldChanges ?? new Map<FieldKey, FieldChange>(),
68
59
  nodeChanges: p.nodeChanges ?? newChangeAtomIdBTree(),
@@ -74,7 +65,7 @@ export function makeModularChangeset(props?: {
74
65
  if (p.revisions !== undefined && p.revisions.length > 0) {
75
66
  changeset.revisions = p.revisions;
76
67
  }
77
- if (p.maxId >= 0) {
68
+ if (p.maxId !== undefined && p.maxId >= 0) {
78
69
  changeset.maxId = brand(p.maxId);
79
70
  }
80
71
  if (p.constraintViolationCount !== undefined && p.constraintViolationCount > 0) {
@@ -38,6 +38,7 @@ import {
38
38
  aboveRootPlaceholder,
39
39
  combineVisitors,
40
40
  deepCopyMapTree,
41
+ makeBreakingVisitor,
41
42
  rootFieldKey,
42
43
  } from "../../core/index.js";
43
44
  import {
@@ -90,6 +91,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
90
91
 
91
92
  readonly #roots: MutableMapTree;
92
93
  public get roots(): MapTree {
94
+ this.breaker.use();
93
95
  return this.#roots;
94
96
  }
95
97
 
@@ -117,10 +119,12 @@ export class ObjectForest implements IEditableForest, WithBreakable {
117
119
  }
118
120
 
119
121
  public get isEmpty(): boolean {
122
+ this.breaker.use();
120
123
  return this.roots.fields.size === 0;
121
124
  }
122
125
 
123
126
  public clone(schema: TreeStoredSchemaSubscription, breaker?: Breakable): ObjectForest {
127
+ this.breaker.use();
124
128
  return new ObjectForest(
125
129
  breaker ?? this.breaker,
126
130
  schema,
@@ -131,6 +135,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
131
135
  }
132
136
 
133
137
  public chunkField(cursor: ITreeCursorSynchronous): TreeChunk[] {
138
+ this.breaker.use();
134
139
  return chunkField(cursor, { idCompressor: undefined, policy: defaultChunkPolicy });
135
140
  }
136
141
 
@@ -174,6 +179,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
174
179
  }
175
180
 
176
181
  public acquireVisitor(): DeltaVisitor {
182
+ this.breaker.use();
177
183
  assert(
178
184
  this.activeVisitor === undefined,
179
185
  0x76c /* Must release existing visitor before acquiring another */,
@@ -228,10 +234,17 @@ export class ObjectForest implements IEditableForest, WithBreakable {
228
234
  }
229
235
  public destroy(detachedField: FieldKey, count: number): void {
230
236
  preEdit();
237
+ const field = this.forest.#roots.fields.get(detachedField);
238
+ assert(field !== undefined, 0xd28 /* Destroyed field must exist */);
239
+ assert(field.length === count, 0xd29 /* Destroy count must match field length */);
231
240
  this.forest.delete(detachedField);
232
241
  }
233
242
  public create(content: readonly ITreeCursorSynchronous[], destination: FieldKey): void {
234
243
  preEdit();
244
+ assert(
245
+ !this.forest.#roots.fields.has(destination),
246
+ 0xd2a /* Create destination must be a new empty field */,
247
+ );
235
248
  this.forest.add(content, destination);
236
249
  this.forest.#events.emit("afterRootFieldCreated", destination);
237
250
  }
@@ -253,22 +266,24 @@ export class ObjectForest implements IEditableForest, WithBreakable {
253
266
  */
254
267
  private attachEdit(source: FieldKey, count: number, destination: PlaceIndex): void {
255
268
  assertNonNegativeSafeInteger(count);
256
- if (count === 0) {
257
- return;
258
- }
259
269
  const [parent, key] = cursor.getParent();
260
270
  assert(
261
271
  parent !== this.forest.roots || key !== source,
262
- 0x7b6 /* Attach source field must be different from current field */,
272
+ 0xd2b /* Attach source field must be different from current field */,
263
273
  );
264
- const currentField = getOrCreateField(parent, key);
265
- assertValidIndex(destination, currentField, true);
266
- const sourceField = this.forest.#roots.fields.get(source) ?? [];
267
- assert(sourceField !== undefined, 0x7b7 /* Attach source field must exist */);
274
+ const sourceField = this.forest.#roots.fields.get(source);
275
+ assert(sourceField !== undefined, 0xd2c /* Attach source field must exist */);
268
276
  assert(
269
277
  sourceField.length === count,
270
- 0x7b8 /* Attach must consume all nodes in source field */,
278
+ 0xd2d /* Attach must consume all nodes in source field */,
279
+ );
280
+ const existingDestinationField = parent.fields.get(key) ?? [];
281
+ assert(
282
+ destination <= existingDestinationField.length,
283
+ 0xd2e /* Attach destination must not exceed field length */,
271
284
  );
285
+ assertValidIndex(destination, existingDestinationField, true);
286
+ const currentField = getOrCreateField(parent, key);
272
287
  // TODO: this will fail for very large insertions due to argument limits.
273
288
  currentField.splice(destination, 0, ...sourceField);
274
289
  this.forest.delete(source);
@@ -284,10 +299,21 @@ export class ObjectForest implements IEditableForest, WithBreakable {
284
299
  const [parent, key] = cursor.getParent();
285
300
  assert(
286
301
  destination === undefined || parent !== this.forest.roots || key !== destination,
287
- 0x7b9 /* Detach destination field must be different from current field */,
302
+ 0xd2f /* Detach destination field must be different from current field */,
303
+ );
304
+ const currentField = parent.fields.get(key) ?? [];
305
+ assert(source.start <= source.end, 0xd30 /* Detach range start must not exceed end */);
306
+ assert(
307
+ source.end <= currentField.length,
308
+ 0xd31 /* Detach range must not exceed field length */,
288
309
  );
289
- const currentField = getOrCreateField(parent, key);
290
310
  assertValidRange(source, currentField);
311
+ if (destination !== undefined) {
312
+ assert(
313
+ !this.forest.#roots.fields.has(destination),
314
+ 0xd32 /* Detach destination must be a new empty field */,
315
+ );
316
+ }
291
317
  const content = currentField.splice(source.start, source.end - source.start);
292
318
  if (destination !== undefined) {
293
319
  this.forest.addFieldAsDetached(content, destination);
@@ -317,9 +343,12 @@ export class ObjectForest implements IEditableForest, WithBreakable {
317
343
  for (const getVisitor of this.deltaVisitors) {
318
344
  announcedVisitors.push(getVisitor());
319
345
  }
320
- const combinedVisitor = combineVisitors([forestVisitor, ...announcedVisitors]);
321
- this.activeVisitor = combinedVisitor;
322
- return combinedVisitor;
346
+ const visitor = combineVisitors([
347
+ makeBreakingVisitor(forestVisitor, this.breaker),
348
+ ...announcedVisitors,
349
+ ]);
350
+ this.activeVisitor = visitor;
351
+ return visitor;
323
352
  }
324
353
 
325
354
  public registerAnnouncedVisitor(visitor: () => AnnouncedVisitor): void {
@@ -354,6 +383,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
354
383
  }
355
384
 
356
385
  public allocateCursor(source?: string): Cursor {
386
+ this.breaker.use();
357
387
  return new Cursor(this, source);
358
388
  }
359
389
 
@@ -361,6 +391,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
361
391
  destination: Anchor,
362
392
  cursorToMove: ITreeSubscriptionCursor,
363
393
  ): TreeNavigationResult {
394
+ this.breaker.use();
364
395
  const path = this.anchors.locate(destination);
365
396
  if (path === undefined) {
366
397
  return TreeNavigationResult.NotFound;
@@ -373,6 +404,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
373
404
  destination: FieldAnchor,
374
405
  cursorToMove: ITreeSubscriptionCursor,
375
406
  ): TreeNavigationResult {
407
+ this.breaker.use();
376
408
  if (destination.parent === undefined) {
377
409
  this.moveCursorToPath(undefined, cursorToMove);
378
410
  } else {
@@ -389,6 +421,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
389
421
  destination: UpPath | undefined,
390
422
  cursorToMove: ITreeSubscriptionCursor,
391
423
  ): void {
424
+ this.breaker.use();
392
425
  assert(
393
426
  cursorToMove instanceof Cursor,
394
427
  0x337 /* ObjectForest must only be given its own Cursor type */,
@@ -419,6 +452,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
419
452
  }
420
453
 
421
454
  public getCursorAboveDetachedFields(): ITreeCursorSynchronous {
455
+ this.breaker.use();
422
456
  return cursorForMapTreeNode(this.roots);
423
457
  }
424
458
  }
@@ -10,7 +10,7 @@ import { SummaryType } from "@fluidframework/driver-definitions";
10
10
  import type {
11
11
  IExperimentalIncrementalSummaryContext,
12
12
  ITelemetryContext,
13
- MinimumVersionForCollab,
13
+ OldestSupportedClientVersion,
14
14
  } from "@fluidframework/runtime-definitions/internal";
15
15
  import type { SummaryTreeBuilder } from "@fluidframework/runtime-utils/internal";
16
16
 
@@ -57,7 +57,7 @@ const supportedVersions = new Set<SchemaSummaryFormatVersion>([
57
57
  * Returns the summary version to use as per the given minimum version for collab.
58
58
  */
59
59
  function minVersionToSchemaSummaryFormatVersion(
60
- version: MinimumVersionForCollab,
60
+ version: OldestSupportedClientVersion,
61
61
  ): SchemaSummaryFormatVersion {
62
62
  // Currently, version 2 is written which adds metadata blob to the summary.
63
63
  return SchemaSummaryFormatVersion.v2;
@@ -76,7 +76,7 @@ export class SchemaSummarizer
76
76
  private readonly schema: MutableTreeStoredSchema,
77
77
  collabWindow: CollabWindow,
78
78
  private readonly codec: IJsonCodec<TreeStoredSchema>,
79
- minVersionForCollab: MinimumVersionForCollab,
79
+ minVersionForCollab: OldestSupportedClientVersion,
80
80
  ) {
81
81
  super(
82
82
  "Schema",
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
6
+ import { assert, fail, unreachableCase } from "@fluidframework/core-utils/internal";
7
7
 
8
8
  import type { ChangeAtomId, RevisionMetadataSource, RevisionTag } from "../../core/index.js";
9
9
  import { type IdAllocator, type Mutable, brand } from "../../util/index.js";
@@ -252,7 +252,7 @@ function addMovedMarkEffect(mark: Mark, effect: Detach): Mark {
252
252
  } else if (isTombstone(mark)) {
253
253
  return { ...mark, ...effect };
254
254
  }
255
- assert(false, 0x818 /* Unexpected combination of mark effects at source and destination */);
255
+ fail(0x818 /* Unexpected combination of mark effects at source and destination */);
256
256
  }
257
257
 
258
258
  /**
@@ -278,7 +278,7 @@ export function compareCellPositionsUsingTombstones(
278
278
  //
279
279
  // The same scenario can arise in the context of compose (just consider composing `old'` and `new'` from
280
280
  // the examples above) with the same resolution.
281
- assert(false, 0x8a2 /* Invalid cell ordering scenario */);
281
+ fail(0x8a2 /* Invalid cell ordering scenario */);
282
282
  }
283
283
 
284
284
  // The absence of metadata for a cell with a defined revision means that the cell is from a revision that
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.115.0";
9
+ export const pkgVersion = "2.116.0";
@@ -9,10 +9,7 @@ import {
9
9
  type IIdCompressor,
10
10
  createIdCompressor,
11
11
  } from "@fluidframework/id-compressor/internal";
12
- import {
13
- createChildLogger,
14
- extractTelemetryLoggerExt,
15
- } from "@fluidframework/telemetry-utils/internal";
12
+ import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
16
13
 
17
14
  import type { CodecWriteOptions, ICodecOptions } from "../codec/index.js";
18
15
  import {
@@ -224,12 +221,10 @@ export function createIndependentTreeBeta<const TSchema extends ImplicitFieldSch
224
221
  export function createIndependentTreeAlpha<const TSchema extends ImplicitFieldSchema>(
225
222
  options?: CreateIndependentTreeAlphaOptions,
226
223
  ): ViewableTree & Pick<ITreeAlpha, "exportVerbose" | "exportSimpleSchema"> {
227
- const logger = extractTelemetryLoggerExt(
228
- createChildLogger({
229
- logger: options?.logger,
230
- namespace: "independentView",
231
- }),
232
- );
224
+ const logger = createChildLogger({
225
+ logger: options?.logger,
226
+ namespace: "independentView",
227
+ });
233
228
  const breaker = new Breakable("independentView", logger);
234
229
  const idCompressor: IIdCompressor =
235
230
  options?.idCompressor ?? options?.content?.idCompressor ?? createIdCompressor();
@@ -250,8 +245,6 @@ export function createIndependentTreeAlpha<const TSchema extends ImplicitFieldSc
250
245
  const checkout = createTreeCheckout(idCompressor, mintRevisionTag, revisionTagCodec, {
251
246
  forest,
252
247
  schema: schemaRepository,
253
- breaker,
254
- logger,
255
248
  codecOptions: options,
256
249
  });
257
250
 
@@ -7,13 +7,13 @@ import type { ErasedType, IFluidLoadable } from "@fluidframework/core-interfaces
7
7
  import { assert, fail } from "@fluidframework/core-utils/internal";
8
8
  import type { IChannelStorageService } from "@fluidframework/datastore-definitions/internal";
9
9
  import type { IIdCompressor, StableId } from "@fluidframework/id-compressor";
10
- import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";
10
+ import type { OldestSupportedClientVersion } from "@fluidframework/runtime-definitions/internal";
11
11
  import type {
12
12
  IChannelView,
13
13
  IFluidSerializer,
14
14
  SharedKernel,
15
15
  } from "@fluidframework/shared-object-base/internal";
16
- import { type TelemetryLoggerExt, UsageError } from "@fluidframework/telemetry-utils/internal";
16
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
17
17
 
18
18
  import {
19
19
  type CodecTree,
@@ -206,7 +206,6 @@ export class SharedTreeKernel
206
206
  submitLocalMessage: (content: unknown, localOpMetadata?: unknown) => void,
207
207
  lastSequenceNumber: () => number | undefined,
208
208
  initialSequenceNumber: number,
209
- private readonly logger: TelemetryLoggerExt | undefined,
210
209
  idCompressor: IIdCompressor,
211
210
  optionsParam: SharedTreeOptionsInternal,
212
211
  ) {
@@ -252,7 +251,7 @@ export class SharedTreeKernel
252
251
  idCompressor,
253
252
  healing:
254
253
  options.healUnresolvableIdentifiersOnDecode === true
255
- ? { sharedObjectId: sharedObject.id, logger }
254
+ ? { sharedObjectId: sharedObject.id, logger: breaker.logger }
256
255
  : undefined,
257
256
  });
258
257
  const forestSummarizer = new ForestSummarizer(
@@ -305,7 +304,6 @@ export class SharedTreeKernel
305
304
  sharedObject,
306
305
  serializer,
307
306
  submitLocalMessage,
308
- logger,
309
307
  [schemaSummarizer, forestSummarizer, removedRootsSummarizer],
310
308
  changeFamily,
311
309
  options,
@@ -324,8 +322,6 @@ export class SharedTreeKernel
324
322
  fieldBatchCodec,
325
323
  removedRoots,
326
324
  chunkCompressionStrategy: options.treeEncodeType,
327
- logger,
328
- breaker: this.breaker,
329
325
  });
330
326
 
331
327
  this.registerSharedBranchForEditing("main", this.checkout);
@@ -546,14 +542,16 @@ export const changeFormatVersionForMessage = DependentFormatVersion.fromPairs<
546
542
  [MessageFormatVersion.v6, SharedTreeChangeFormatVersion.v5],
547
543
  ]);
548
544
 
549
- function getCodecTreeForEditManagerFormat(clientVersion: MinimumVersionForCollab): CodecTree {
545
+ function getCodecTreeForEditManagerFormat(
546
+ clientVersion: OldestSupportedClientVersion,
547
+ ): CodecTree {
550
548
  const editManagerVersion = makeEditManagerCodecBuilder().getCodecTree(clientVersion).version;
551
549
  const change = changeFormatVersionForEditManager.lookup(editManagerVersion);
552
550
  const changeCodecTree = getCodecTreeForChangeFormat(change, clientVersion);
553
551
  return getCodecTreeForEditManagerFormatWithChange(clientVersion, changeCodecTree);
554
552
  }
555
553
 
556
- function getCodecTreeForMessageFormat(clientVersion: MinimumVersionForCollab): CodecTree {
554
+ function getCodecTreeForMessageFormat(clientVersion: OldestSupportedClientVersion): CodecTree {
557
555
  const messageVersion = makeMessageCodecBuilder().getCodecTree(clientVersion).version;
558
556
  assert(
559
557
  messageVersion !== undefined,
@@ -565,7 +563,7 @@ function getCodecTreeForMessageFormat(clientVersion: MinimumVersionForCollab): C
565
563
  }
566
564
 
567
565
  export function getCodecTreeForSharedTreeFormat(
568
- clientVersion: MinimumVersionForCollab,
566
+ clientVersion: OldestSupportedClientVersion,
569
567
  ): CodecTree {
570
568
  const children: CodecTree[] = [];
571
569
  children.push(forestCodecBuilder.getCodecTree(clientVersion));
@@ -665,6 +663,35 @@ export interface SharedTreeOptions
665
663
  * lifetime of the client, which increases memory usage over time and should be used with care.
666
664
  */
667
665
  readonly retainHistory?: boolean;
666
+
667
+ /**
668
+ * When `true`, validates that commits being submitted for the first time can be applied without errors to a view.
669
+ * In the event that a commit cannot be applied, SharedTree will throw an error and will enter a "broken" state, preventing the offending commit (and any further commits) from being submitted.
670
+ *
671
+ * This can be enabled (at the cost of performance) to improve safety against document corruption in the event of a bug in the SharedTree code:
672
+ * when the additional validation is enabled, a client will error instead of potentially corrupting the document.
673
+ *
674
+ * @defaultValue `false`
675
+ *
676
+ * @remarks
677
+ * This validation is more expensive than {@link SharedTreeOptions.validateRebasedCommitsBeforeResubmission} because it is likely to be performed more often.
678
+ * We recommend {@link ForestOptions.forest|configuring SharedTree} with the {@link ForestTypeOptimized|optimized forest implementation} to reduce its performance impact.
679
+ */
680
+ readonly validateCommitsOnFirstSubmission?: boolean;
681
+
682
+ /**
683
+ * When `true`, validates that the commits being resubmitted can be applied without errors to a view.
684
+ * In the event that a commit cannot be applied, SharedTree will throw an error and will enter a "broken" state, preventing the offending commit (and any further commits) from being submitted.
685
+ *
686
+ * This can be enabled (at the cost of performance) to improve safety against document corruption in the event of a bug in the SharedTree code:
687
+ * when the additional validation is enabled, a client will error instead of potentially corrupting the document.
688
+ *
689
+ * @defaultValue `false`
690
+ *
691
+ * @remarks
692
+ * We recommend {@link ForestOptions.forest|configuring SharedTree} with the {@link ForestTypeOptimized|optimized forest implementation} to reduce the performance impact of this validation.
693
+ */
694
+ readonly validateRebasedCommitsBeforeResubmission?: boolean;
668
695
  }
669
696
 
670
697
  export interface SharedTreeOptionsInternal
@@ -741,6 +768,7 @@ export const ForestTypeOptimized = toForestType(
741
768
  makeTreeChunker(schema, defaultSchemaPolicy, shouldEncodeIncrementally),
742
769
  undefined,
743
770
  idCompressor,
771
+ breaker,
744
772
  ),
745
773
  );
746
774
 
@@ -767,6 +795,7 @@ export const ForestTypeExpensiveDebug = toForestType(
767
795
  makeTreeChunker(schema, defaultSchemaPolicy, shouldEncodeIncrementally),
768
796
  undefined,
769
797
  idCompressor,
798
+ breaker,
770
799
  ),
771
800
  buildForest(breaker, schema, undefined, true),
772
801
  ),
@@ -812,6 +841,8 @@ export const defaultSharedTreeOptions: Required<SharedTreeOptionsInternal> = {
812
841
  writeVersionOverrides: new Map(),
813
842
  allowPossiblyIncompatibleWriteVersionOverrides: false,
814
843
  retainHistory: false,
844
+ validateCommitsOnFirstSubmission: false,
845
+ validateRebasedCommitsBeforeResubmission: false,
815
846
  };
816
847
 
817
848
  /**
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { fail } from "@fluidframework/core-utils/internal";
7
- import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";
7
+ import type { OldestSupportedClientVersion } from "@fluidframework/runtime-definitions/internal";
8
8
 
9
9
  import {
10
10
  type CodecTree,
@@ -130,7 +130,7 @@ export const dependenciesForChangeFormat = new Map<
130
130
 
131
131
  export function getCodecTreeForChangeFormat(
132
132
  version: SharedTreeChangeFormatVersion,
133
- clientVersion: MinimumVersionForCollab,
133
+ clientVersion: OldestSupportedClientVersion,
134
134
  ): CodecTree {
135
135
  const { modularChange } =
136
136
  dependenciesForChangeFormat.get(version) ?? fail(0xc78 /* Unknown change format */);
@@ -40,6 +40,10 @@ interface OwnedState {
40
40
  readonly removedRoots: DetachedFieldIndex;
41
41
  }
42
42
 
43
+ /**
44
+ * A helper for enriching `SharedTreeChange`s.
45
+ * It lazily forks the borrowed state (which it does not mutate) and lazily applies {@link enqueueChange|changes} to the forked state when {@link enrich|commit enrichment} requires access to detached root data.
46
+ */
43
47
  export class SharedTreeChangeEnricher {
44
48
  /**
45
49
  * Queue of changes to be applied before querying for detached roots.
@@ -76,6 +80,13 @@ export class SharedTreeChangeEnricher {
76
80
  };
77
81
  }
78
82
 
83
+ /**
84
+ * Enriches a change.
85
+ * @param change - The change to enrich (not mutated)
86
+ * @returns An enriched copy of the given `change`.
87
+ * @remarks
88
+ * Invoking this method will flush {@link enqueueChange|queued} changes if the enrichment process requires reading detached roots from the forest.
89
+ */
79
90
  public enrich(change: SharedTreeChange): SharedTreeChange {
80
91
  this.onEnrichCommit?.();
81
92
  return updateRefreshers(
@@ -104,13 +115,23 @@ export class SharedTreeChangeEnricher {
104
115
  return undefined;
105
116
  }
106
117
 
118
+ /**
119
+ * Enqueues a change to be applied before reading detached root data during the next {@link enrich|enrichment}.
120
+ * @param change - The change to apply or a function that returns the change to apply.
121
+ * If a function is provided, it will be invoked during a future call to {@link enrich} (if at all).
122
+ */
107
123
  public enqueueChange(
108
124
  change: TaggedChange<SharedTreeChange> | (() => TaggedChange<SharedTreeChange>),
109
125
  ): void {
110
126
  this.changeQueue.push(typeof change === "function" ? change : () => change);
111
127
  }
112
128
 
113
- private purgeChangeQueue(): void {
129
+ /**
130
+ * Flushes all {@link enqueueChange|queued} changes by applying the data changes to a forked forest and detached field index.
131
+ * @remarks
132
+ * This validates that the data changes are valid.
133
+ */
134
+ public purgeChangeQueue(): void {
114
135
  if (this.changeQueue.length === 0) {
115
136
  return;
116
137
  }
@@ -150,6 +171,9 @@ export class SharedTreeChangeEnricher {
150
171
  this.changeQueue.length = 0;
151
172
  }
152
173
 
174
+ /**
175
+ * Dispose of this object and its resources.
176
+ */
153
177
  public [disposeSymbol](): void {
154
178
  // TODO: in the future, forest and/or its AnchorSet may require disposal.
155
179
  }
@@ -334,14 +334,11 @@ export function createTreeCheckout(
334
334
  fieldBatchCodec?: FieldBatchCodec;
335
335
  removedRoots?: DetachedFieldIndex;
336
336
  chunkCompressionStrategy?: TreeCompressionStrategy;
337
- logger?: TelemetryLoggerExt;
338
- breaker?: Breakable;
339
337
  codecOptions?: Partial<CodecWriteOptions>;
340
338
  },
341
339
  ): TreeCheckout {
342
- const breaker = args?.breaker ?? new Breakable("TreeCheckout", args?.logger);
343
340
  const schema = args?.schema ?? new TreeStoredSchemaRepository();
344
- const forest = args?.forest ?? buildForest(breaker, schema);
341
+ const forest = args?.forest ?? buildForest(new Breakable("TreeCheckout"), schema);
345
342
  const defaultCodecOptions: CodecWriteOptions = {
346
343
  jsonValidator: FormatValidatorNoOp,
347
344
  minVersionForCollab: FluidClientVersion.v2_0,
@@ -380,8 +377,6 @@ export function createTreeCheckout(
380
377
  revisionTagCodec,
381
378
  idCompressor,
382
379
  args?.removedRoots,
383
- args?.logger,
384
- breaker,
385
380
  );
386
381
  }
387
382
 
@@ -489,6 +484,31 @@ export function assertValidConstraint(
489
484
  export class TreeCheckout implements ITreeCheckout {
490
485
  public disposed = false;
491
486
 
487
+ /**
488
+ * Logger for telemetry events related to this checkout.
489
+ * @remarks
490
+ * This logger is provided by the {@link Breakable} passed to the constructor, and may be `undefined` if no logger was provided.
491
+ * See the explanation on {@link Breakable.logger} for why this pattern makes sense.
492
+ */
493
+ private get logger(): TelemetryLoggerExt | undefined {
494
+ return this.breaker.logger;
495
+ }
496
+
497
+ /**
498
+ * The breaker for this checkout.
499
+ * @remarks
500
+ * As the forest is part of the mutable state of the checkout,
501
+ * we want to be broken if it is broken, so we use the forest's breaker as our own.
502
+ *
503
+ * This does mean that if something goes wrong in the checkout, we break the forest,
504
+ * which is not strictly necessary, but may be desirable as if something is wrong with the checkout,
505
+ * while the forest might be internally consistent, it likely doesn't contain the correct content
506
+ * (since the checkout is keeping it up to date properly anymore).
507
+ */
508
+ public get breaker(): Breakable {
509
+ return this.forest.breaker;
510
+ }
511
+
492
512
  private editLock: EditLock;
493
513
 
494
514
  /**
@@ -566,9 +586,6 @@ export class TreeCheckout implements ITreeCheckout {
566
586
  private readonly revisionTagCodec: RevisionTagCodec,
567
587
  private readonly idCompressor: IIdCompressor,
568
588
  private readonly _removedRoots: DetachedFieldIndex = makeDetachedFieldIndex("repair"),
569
- /** Optional logger for telemetry. */
570
- private readonly logger?: TelemetryLoggerExt,
571
- public readonly breaker: Breakable = new Breakable("TreeCheckout", logger),
572
589
  public readonly disposeForksAfterTransaction = true,
573
590
  ) {
574
591
  this.#transaction = this.createTransactionStack(branch);
@@ -1119,6 +1136,7 @@ export class TreeCheckout implements ITreeCheckout {
1119
1136
  });
1120
1137
  };
1121
1138
 
1139
+ @breakingMethod
1122
1140
  private withCombinedVisitor(fn: (visitor: DeltaVisitor) => void): void {
1123
1141
  const anchorVisitor = this.forest.anchors.acquireVisitor();
1124
1142
  const combinedVisitor = combineVisitors([this.forest.acquireVisitor(), anchorVisitor]);
@@ -1132,7 +1150,7 @@ export class TreeCheckout implements ITreeCheckout {
1132
1150
  if (usageError !== undefined) {
1133
1151
  throw new UsageError(usageError);
1134
1152
  }
1135
- assert(false, 0x911 /* Invalid operation on a disposed TreeCheckout */);
1153
+ fail(0x911 /* Invalid operation on a disposed TreeCheckout */);
1136
1154
  }
1137
1155
  }
1138
1156
 
@@ -1304,8 +1322,6 @@ export class TreeCheckout implements ITreeCheckout {
1304
1322
  this.revisionTagCodec,
1305
1323
  this.idCompressor,
1306
1324
  this._removedRoots.clone(),
1307
- this.logger,
1308
- forkBreaker,
1309
1325
  );
1310
1326
  this.#events.emit("fork", checkout);
1311
1327
  return checkout;
@@ -1704,6 +1720,7 @@ export class TreeCheckout implements ITreeCheckout {
1704
1720
  public enrich(
1705
1721
  context: GraphCommit<SharedTreeChange>,
1706
1722
  changes: readonly TaggedChange<SharedTreeChange>[],
1723
+ forceValidation: boolean,
1707
1724
  ): SharedTreeChange[] {
1708
1725
  if (!hasSome(changes)) {
1709
1726
  return [];
@@ -1734,13 +1751,25 @@ export class TreeCheckout implements ITreeCheckout {
1734
1751
  return makeAnonChange(diff);
1735
1752
  });
1736
1753
  }
1737
- const enriched: SharedTreeChange[] = [];
1754
+ const enrichedChanges: SharedTreeChange[] = [];
1738
1755
  for (const change of changes) {
1739
- enriched.push(enricher.enrich(change.change));
1740
- enricher.enqueueChange(change);
1756
+ const enrichedChange = enricher.enrich(change.change);
1757
+ enrichedChanges.push(enrichedChange);
1758
+ // We could either apply the original or the enriched change: either should allow subsequent changes to be enriched correctly.
1759
+ // We choose to apply the enriched change in order to more closely replicate what a peer will experience (assuming no concurrent changes).
1760
+ // This makes it more likely that we'll detect a bug in the enrichment logic.
1761
+ // Note that enqueueing the change does not guarantee that it will be applied unless `forceValidation` is enabled.
1762
+ enricher.enqueueChange(tagChange(enrichedChange, change.revision));
1763
+ }
1764
+ if (forceValidation) {
1765
+ // Ensure that all changes can be applied successfully.
1766
+ // This is useful for catching bugs in the enrichment logic and in the rebasing logic that may have been involved in producing the changes.
1767
+ // Note that we cannot guarantee that the enriched change will be applied successfully even in the absence of concurrent changes,
1768
+ // because the checkout state we are applying this enriched change to was derived from the state of the checkout, which may be different from that of a peer.
1769
+ enricher.purgeChangeQueue();
1741
1770
  }
1742
1771
  enricher[disposeSymbol]();
1743
- return enriched;
1772
+ return enrichedChanges;
1744
1773
  }
1745
1774
 
1746
1775
  public get mainBranch(): SharedTreeBranch<