@fluidframework/tree 2.61.0 → 2.62.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 (388) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/api-report/tree.alpha.api.md +26 -21
  3. package/api-report/tree.beta.api.md +15 -0
  4. package/api-report/tree.legacy.beta.api.md +18 -0
  5. package/dist/alpha.d.ts +8 -8
  6. package/dist/api.d.ts +17 -0
  7. package/dist/api.d.ts.map +1 -0
  8. package/dist/api.js +24 -0
  9. package/dist/api.js.map +1 -0
  10. package/dist/beta.d.ts +5 -0
  11. package/dist/codec/codec.d.ts +3 -5
  12. package/dist/codec/codec.d.ts.map +1 -1
  13. package/dist/codec/codec.js +9 -2
  14. package/dist/codec/codec.js.map +1 -1
  15. package/dist/codec/index.d.ts +0 -1
  16. package/dist/codec/index.d.ts.map +1 -1
  17. package/dist/codec/index.js +1 -3
  18. package/dist/codec/index.js.map +1 -1
  19. package/dist/core/index.d.ts +1 -1
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/core/index.js +2 -1
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/rebase/index.d.ts +1 -1
  24. package/dist/core/rebase/index.d.ts.map +1 -1
  25. package/dist/core/rebase/index.js +2 -1
  26. package/dist/core/rebase/index.js.map +1 -1
  27. package/dist/core/rebase/utils.d.ts +10 -0
  28. package/dist/core/rebase/utils.d.ts.map +1 -1
  29. package/dist/core/rebase/utils.js +20 -1
  30. package/dist/core/rebase/utils.js.map +1 -1
  31. package/dist/core/tree/detachedFieldIndex.js +1 -1
  32. package/dist/core/tree/detachedFieldIndex.js.map +1 -1
  33. package/dist/external-utilities/index.d.ts +1 -1
  34. package/dist/external-utilities/index.d.ts.map +1 -1
  35. package/dist/external-utilities/index.js +1 -2
  36. package/dist/external-utilities/index.js.map +1 -1
  37. package/dist/external-utilities/typeboxValidator.d.ts +0 -13
  38. package/dist/external-utilities/typeboxValidator.d.ts.map +1 -1
  39. package/dist/external-utilities/typeboxValidator.js +3 -5
  40. package/dist/external-utilities/typeboxValidator.js.map +1 -1
  41. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +2 -0
  42. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  43. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  44. package/dist/feature-libraries/flex-tree/index.d.ts +1 -0
  45. package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
  46. package/dist/feature-libraries/flex-tree/index.js +4 -1
  47. package/dist/feature-libraries/flex-tree/index.js.map +1 -1
  48. package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  49. package/dist/feature-libraries/flex-tree/lazyNode.js +15 -8
  50. package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  51. package/dist/feature-libraries/flex-tree/observer.d.ts +32 -0
  52. package/dist/feature-libraries/flex-tree/observer.d.ts.map +1 -0
  53. package/dist/feature-libraries/flex-tree/observer.js +33 -0
  54. package/dist/feature-libraries/flex-tree/observer.js.map +1 -0
  55. package/dist/feature-libraries/index.d.ts +1 -1
  56. package/dist/feature-libraries/index.d.ts.map +1 -1
  57. package/dist/feature-libraries/index.js +3 -1
  58. package/dist/feature-libraries/index.js.map +1 -1
  59. package/dist/index.d.ts +5 -5
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +9 -8
  62. package/dist/index.js.map +1 -1
  63. package/dist/legacy.d.ts +7 -1
  64. package/dist/packageVersion.d.ts +1 -1
  65. package/dist/packageVersion.js +1 -1
  66. package/dist/packageVersion.js.map +1 -1
  67. package/dist/shared-tree/index.d.ts +2 -2
  68. package/dist/shared-tree/index.d.ts.map +1 -1
  69. package/dist/shared-tree/index.js.map +1 -1
  70. package/dist/shared-tree/schematizingTreeView.js +2 -2
  71. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  72. package/dist/shared-tree/sharedTree.d.ts +21 -6
  73. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  74. package/dist/shared-tree/sharedTree.js +76 -37
  75. package/dist/shared-tree/sharedTree.js.map +1 -1
  76. package/dist/shared-tree/treeAlpha.d.ts +114 -1
  77. package/dist/shared-tree/treeAlpha.d.ts.map +1 -1
  78. package/dist/shared-tree/treeAlpha.js +140 -1
  79. package/dist/shared-tree/treeAlpha.js.map +1 -1
  80. package/dist/shared-tree/treeCheckout.d.ts +13 -7
  81. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  82. package/dist/shared-tree/treeCheckout.js +115 -85
  83. package/dist/shared-tree/treeCheckout.js.map +1 -1
  84. package/dist/shared-tree-core/branch.d.ts +3 -0
  85. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  86. package/dist/shared-tree-core/branch.js.map +1 -1
  87. package/dist/shared-tree-core/branchIdCodec.d.ts +11 -0
  88. package/dist/shared-tree-core/branchIdCodec.d.ts.map +1 -0
  89. package/dist/shared-tree-core/branchIdCodec.js +18 -0
  90. package/dist/shared-tree-core/branchIdCodec.js.map +1 -0
  91. package/dist/shared-tree-core/editManager.d.ts +39 -64
  92. package/dist/shared-tree-core/editManager.d.ts.map +1 -1
  93. package/dist/shared-tree-core/editManager.js +455 -295
  94. package/dist/shared-tree-core/editManager.js.map +1 -1
  95. package/dist/shared-tree-core/editManagerCodecs.d.ts +1 -1
  96. package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  97. package/dist/shared-tree-core/editManagerCodecs.js +7 -96
  98. package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
  99. package/dist/shared-tree-core/editManagerCodecsCommons.d.ts +17 -0
  100. package/dist/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -0
  101. package/dist/shared-tree-core/editManagerCodecsCommons.js +139 -0
  102. package/dist/shared-tree-core/editManagerCodecsCommons.js.map +1 -0
  103. package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts +16 -0
  104. package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -0
  105. package/dist/shared-tree-core/editManagerCodecsV1toV4.js +39 -0
  106. package/dist/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -0
  107. package/dist/shared-tree-core/editManagerCodecsV5.d.ts +16 -0
  108. package/dist/shared-tree-core/editManagerCodecsV5.d.ts.map +1 -0
  109. package/dist/shared-tree-core/editManagerCodecsV5.js +58 -0
  110. package/dist/shared-tree-core/editManagerCodecsV5.js.map +1 -0
  111. package/dist/shared-tree-core/{editManagerFormat.d.ts → editManagerFormatCommons.d.ts} +31 -7
  112. package/dist/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -0
  113. package/dist/shared-tree-core/{editManagerFormat.js → editManagerFormatCommons.js} +13 -12
  114. package/dist/shared-tree-core/editManagerFormatCommons.js.map +1 -0
  115. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts +31 -0
  116. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -0
  117. package/dist/shared-tree-core/editManagerFormatV1toV4.js +24 -0
  118. package/dist/shared-tree-core/editManagerFormatV1toV4.js.map +1 -0
  119. package/dist/shared-tree-core/editManagerFormatV5.d.ts +62 -0
  120. package/dist/shared-tree-core/editManagerFormatV5.d.ts.map +1 -0
  121. package/dist/shared-tree-core/editManagerFormatV5.js +20 -0
  122. package/dist/shared-tree-core/editManagerFormatV5.js.map +1 -0
  123. package/dist/shared-tree-core/index.d.ts +3 -3
  124. package/dist/shared-tree-core/index.d.ts.map +1 -1
  125. package/dist/shared-tree-core/index.js.map +1 -1
  126. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts +11 -0
  127. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -0
  128. package/dist/shared-tree-core/messageCodecV1ToV4.js +59 -0
  129. package/dist/shared-tree-core/messageCodecV1ToV4.js.map +1 -0
  130. package/dist/shared-tree-core/messageCodecV5.d.ts +11 -0
  131. package/dist/shared-tree-core/messageCodecV5.d.ts.map +1 -0
  132. package/dist/shared-tree-core/messageCodecV5.js +78 -0
  133. package/dist/shared-tree-core/messageCodecV5.js.map +1 -0
  134. package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
  135. package/dist/shared-tree-core/messageCodecs.js +16 -47
  136. package/dist/shared-tree-core/messageCodecs.js.map +1 -1
  137. package/dist/shared-tree-core/{messageFormat.d.ts → messageFormatV1ToV4.d.ts} +1 -1
  138. package/dist/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -0
  139. package/dist/shared-tree-core/{messageFormat.js → messageFormatV1ToV4.js} +1 -1
  140. package/dist/shared-tree-core/messageFormatV1ToV4.js.map +1 -0
  141. package/dist/shared-tree-core/messageFormatV5.d.ts +42 -0
  142. package/dist/shared-tree-core/messageFormatV5.d.ts.map +1 -0
  143. package/dist/shared-tree-core/messageFormatV5.js +20 -0
  144. package/dist/shared-tree-core/messageFormatV5.js.map +1 -0
  145. package/dist/shared-tree-core/messageTypes.d.ts +12 -2
  146. package/dist/shared-tree-core/messageTypes.d.ts.map +1 -1
  147. package/dist/shared-tree-core/messageTypes.js.map +1 -1
  148. package/dist/shared-tree-core/sequenceIdUtils.d.ts +1 -1
  149. package/dist/shared-tree-core/sequenceIdUtils.d.ts.map +1 -1
  150. package/dist/shared-tree-core/sequenceIdUtils.js.map +1 -1
  151. package/dist/shared-tree-core/sharedTreeCore.d.ts +19 -5
  152. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  153. package/dist/shared-tree-core/sharedTreeCore.js +182 -58
  154. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  155. package/dist/simple-tree/api/tree.d.ts +17 -0
  156. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  157. package/dist/simple-tree/api/tree.js +2 -0
  158. package/dist/simple-tree/api/tree.js.map +1 -1
  159. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  160. package/dist/simple-tree/core/unhydratedFlexTree.js +7 -1
  161. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  162. package/dist/treeFactory.d.ts +38 -9
  163. package/dist/treeFactory.d.ts.map +1 -1
  164. package/dist/treeFactory.js +44 -9
  165. package/dist/treeFactory.js.map +1 -1
  166. package/lib/alpha.d.ts +8 -8
  167. package/lib/api.d.ts +17 -0
  168. package/lib/api.d.ts.map +1 -0
  169. package/lib/api.js +22 -0
  170. package/lib/api.js.map +1 -0
  171. package/lib/beta.d.ts +5 -0
  172. package/lib/codec/codec.d.ts +3 -5
  173. package/lib/codec/codec.d.ts.map +1 -1
  174. package/lib/codec/codec.js +8 -1
  175. package/lib/codec/codec.js.map +1 -1
  176. package/lib/codec/index.d.ts +0 -1
  177. package/lib/codec/index.d.ts.map +1 -1
  178. package/lib/codec/index.js +0 -1
  179. package/lib/codec/index.js.map +1 -1
  180. package/lib/core/index.d.ts +1 -1
  181. package/lib/core/index.d.ts.map +1 -1
  182. package/lib/core/index.js +1 -1
  183. package/lib/core/index.js.map +1 -1
  184. package/lib/core/rebase/index.d.ts +1 -1
  185. package/lib/core/rebase/index.d.ts.map +1 -1
  186. package/lib/core/rebase/index.js +1 -1
  187. package/lib/core/rebase/index.js.map +1 -1
  188. package/lib/core/rebase/utils.d.ts +10 -0
  189. package/lib/core/rebase/utils.d.ts.map +1 -1
  190. package/lib/core/rebase/utils.js +18 -0
  191. package/lib/core/rebase/utils.js.map +1 -1
  192. package/lib/core/tree/detachedFieldIndex.js +2 -2
  193. package/lib/core/tree/detachedFieldIndex.js.map +1 -1
  194. package/lib/external-utilities/index.d.ts +1 -1
  195. package/lib/external-utilities/index.d.ts.map +1 -1
  196. package/lib/external-utilities/index.js +1 -1
  197. package/lib/external-utilities/index.js.map +1 -1
  198. package/lib/external-utilities/typeboxValidator.d.ts +0 -13
  199. package/lib/external-utilities/typeboxValidator.d.ts.map +1 -1
  200. package/lib/external-utilities/typeboxValidator.js +1 -3
  201. package/lib/external-utilities/typeboxValidator.js.map +1 -1
  202. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +2 -0
  203. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  204. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  205. package/lib/feature-libraries/flex-tree/index.d.ts +1 -0
  206. package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
  207. package/lib/feature-libraries/flex-tree/index.js +1 -0
  208. package/lib/feature-libraries/flex-tree/index.js.map +1 -1
  209. package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  210. package/lib/feature-libraries/flex-tree/lazyNode.js +15 -8
  211. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  212. package/lib/feature-libraries/flex-tree/observer.d.ts +32 -0
  213. package/lib/feature-libraries/flex-tree/observer.d.ts.map +1 -0
  214. package/lib/feature-libraries/flex-tree/observer.js +40 -0
  215. package/lib/feature-libraries/flex-tree/observer.js.map +1 -0
  216. package/lib/feature-libraries/index.d.ts +1 -1
  217. package/lib/feature-libraries/index.d.ts.map +1 -1
  218. package/lib/feature-libraries/index.js +1 -1
  219. package/lib/feature-libraries/index.js.map +1 -1
  220. package/lib/index.d.ts +5 -5
  221. package/lib/index.d.ts.map +1 -1
  222. package/lib/index.js +3 -3
  223. package/lib/index.js.map +1 -1
  224. package/lib/legacy.d.ts +7 -1
  225. package/lib/packageVersion.d.ts +1 -1
  226. package/lib/packageVersion.js +1 -1
  227. package/lib/packageVersion.js.map +1 -1
  228. package/lib/shared-tree/index.d.ts +2 -2
  229. package/lib/shared-tree/index.d.ts.map +1 -1
  230. package/lib/shared-tree/index.js.map +1 -1
  231. package/lib/shared-tree/schematizingTreeView.js +2 -2
  232. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  233. package/lib/shared-tree/sharedTree.d.ts +21 -6
  234. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  235. package/lib/shared-tree/sharedTree.js +78 -39
  236. package/lib/shared-tree/sharedTree.js.map +1 -1
  237. package/lib/shared-tree/treeAlpha.d.ts +114 -1
  238. package/lib/shared-tree/treeAlpha.d.ts.map +1 -1
  239. package/lib/shared-tree/treeAlpha.js +143 -4
  240. package/lib/shared-tree/treeAlpha.js.map +1 -1
  241. package/lib/shared-tree/treeCheckout.d.ts +13 -7
  242. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  243. package/lib/shared-tree/treeCheckout.js +117 -87
  244. package/lib/shared-tree/treeCheckout.js.map +1 -1
  245. package/lib/shared-tree-core/branch.d.ts +3 -0
  246. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  247. package/lib/shared-tree-core/branch.js.map +1 -1
  248. package/lib/shared-tree-core/branchIdCodec.d.ts +11 -0
  249. package/lib/shared-tree-core/branchIdCodec.d.ts.map +1 -0
  250. package/lib/shared-tree-core/branchIdCodec.js +13 -0
  251. package/lib/shared-tree-core/branchIdCodec.js.map +1 -0
  252. package/lib/shared-tree-core/editManager.d.ts +39 -64
  253. package/lib/shared-tree-core/editManager.d.ts.map +1 -1
  254. package/lib/shared-tree-core/editManager.js +455 -295
  255. package/lib/shared-tree-core/editManager.js.map +1 -1
  256. package/lib/shared-tree-core/editManagerCodecs.d.ts +1 -1
  257. package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  258. package/lib/shared-tree-core/editManagerCodecs.js +4 -93
  259. package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
  260. package/lib/shared-tree-core/editManagerCodecsCommons.d.ts +17 -0
  261. package/lib/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -0
  262. package/lib/shared-tree-core/editManagerCodecsCommons.js +134 -0
  263. package/lib/shared-tree-core/editManagerCodecsCommons.js.map +1 -0
  264. package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts +16 -0
  265. package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -0
  266. package/lib/shared-tree-core/editManagerCodecsV1toV4.js +35 -0
  267. package/lib/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -0
  268. package/lib/shared-tree-core/editManagerCodecsV5.d.ts +16 -0
  269. package/lib/shared-tree-core/editManagerCodecsV5.d.ts.map +1 -0
  270. package/lib/shared-tree-core/editManagerCodecsV5.js +54 -0
  271. package/lib/shared-tree-core/editManagerCodecsV5.js.map +1 -0
  272. package/lib/shared-tree-core/{editManagerFormat.d.ts → editManagerFormatCommons.d.ts} +31 -7
  273. package/lib/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -0
  274. package/lib/shared-tree-core/{editManagerFormat.js → editManagerFormatCommons.js} +10 -11
  275. package/lib/shared-tree-core/editManagerFormatCommons.js.map +1 -0
  276. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts +31 -0
  277. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -0
  278. package/lib/shared-tree-core/editManagerFormatV1toV4.js +20 -0
  279. package/lib/shared-tree-core/editManagerFormatV1toV4.js.map +1 -0
  280. package/lib/shared-tree-core/editManagerFormatV5.d.ts +62 -0
  281. package/lib/shared-tree-core/editManagerFormatV5.d.ts.map +1 -0
  282. package/lib/shared-tree-core/editManagerFormatV5.js +16 -0
  283. package/lib/shared-tree-core/editManagerFormatV5.js.map +1 -0
  284. package/lib/shared-tree-core/index.d.ts +3 -3
  285. package/lib/shared-tree-core/index.d.ts.map +1 -1
  286. package/lib/shared-tree-core/index.js.map +1 -1
  287. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts +11 -0
  288. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -0
  289. package/lib/shared-tree-core/messageCodecV1ToV4.js +55 -0
  290. package/lib/shared-tree-core/messageCodecV1ToV4.js.map +1 -0
  291. package/lib/shared-tree-core/messageCodecV5.d.ts +11 -0
  292. package/lib/shared-tree-core/messageCodecV5.d.ts.map +1 -0
  293. package/lib/shared-tree-core/messageCodecV5.js +74 -0
  294. package/lib/shared-tree-core/messageCodecV5.js.map +1 -0
  295. package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
  296. package/lib/shared-tree-core/messageCodecs.js +17 -48
  297. package/lib/shared-tree-core/messageCodecs.js.map +1 -1
  298. package/lib/shared-tree-core/{messageFormat.d.ts → messageFormatV1ToV4.d.ts} +1 -1
  299. package/lib/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -0
  300. package/lib/shared-tree-core/{messageFormat.js → messageFormatV1ToV4.js} +1 -1
  301. package/lib/shared-tree-core/messageFormatV1ToV4.js.map +1 -0
  302. package/lib/shared-tree-core/messageFormatV5.d.ts +42 -0
  303. package/lib/shared-tree-core/messageFormatV5.d.ts.map +1 -0
  304. package/lib/shared-tree-core/messageFormatV5.js +16 -0
  305. package/lib/shared-tree-core/messageFormatV5.js.map +1 -0
  306. package/lib/shared-tree-core/messageTypes.d.ts +12 -2
  307. package/lib/shared-tree-core/messageTypes.d.ts.map +1 -1
  308. package/lib/shared-tree-core/messageTypes.js.map +1 -1
  309. package/lib/shared-tree-core/sequenceIdUtils.d.ts +1 -1
  310. package/lib/shared-tree-core/sequenceIdUtils.d.ts.map +1 -1
  311. package/lib/shared-tree-core/sequenceIdUtils.js.map +1 -1
  312. package/lib/shared-tree-core/sharedTreeCore.d.ts +19 -5
  313. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  314. package/lib/shared-tree-core/sharedTreeCore.js +183 -59
  315. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  316. package/lib/simple-tree/api/tree.d.ts +17 -0
  317. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  318. package/lib/simple-tree/api/tree.js +2 -0
  319. package/lib/simple-tree/api/tree.js.map +1 -1
  320. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  321. package/lib/simple-tree/core/unhydratedFlexTree.js +8 -2
  322. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  323. package/lib/treeFactory.d.ts +38 -9
  324. package/lib/treeFactory.d.ts.map +1 -1
  325. package/lib/treeFactory.js +41 -8
  326. package/lib/treeFactory.js.map +1 -1
  327. package/package.json +25 -25
  328. package/src/api.ts +30 -0
  329. package/src/codec/codec.ts +12 -6
  330. package/src/codec/index.ts +0 -1
  331. package/src/core/index.ts +1 -0
  332. package/src/core/rebase/index.ts +1 -0
  333. package/src/core/rebase/utils.ts +27 -0
  334. package/src/core/tree/detachedFieldIndex.ts +2 -2
  335. package/src/external-utilities/index.ts +1 -1
  336. package/src/external-utilities/typeboxValidator.ts +1 -3
  337. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +2 -0
  338. package/src/feature-libraries/flex-tree/index.ts +2 -0
  339. package/src/feature-libraries/flex-tree/lazyNode.ts +13 -3
  340. package/src/feature-libraries/flex-tree/observer.ts +64 -0
  341. package/src/feature-libraries/index.ts +3 -0
  342. package/src/index.ts +6 -4
  343. package/src/packageVersion.ts +1 -1
  344. package/src/shared-tree/index.ts +2 -0
  345. package/src/shared-tree/schematizingTreeView.ts +2 -2
  346. package/src/shared-tree/sharedTree.ts +116 -52
  347. package/src/shared-tree/treeAlpha.ts +309 -4
  348. package/src/shared-tree/treeCheckout.ts +152 -100
  349. package/src/shared-tree-core/branch.ts +7 -0
  350. package/src/shared-tree-core/branchIdCodec.ts +28 -0
  351. package/src/shared-tree-core/editManager.ts +729 -430
  352. package/src/shared-tree-core/editManagerCodecs.ts +4 -164
  353. package/src/shared-tree-core/editManagerCodecsCommons.ts +245 -0
  354. package/src/shared-tree-core/editManagerCodecsV1toV4.ts +108 -0
  355. package/src/shared-tree-core/editManagerCodecsV5.ts +156 -0
  356. package/src/shared-tree-core/{editManagerFormat.ts → editManagerFormatCommons.ts} +17 -13
  357. package/src/shared-tree-core/editManagerFormatV1toV4.ts +42 -0
  358. package/src/shared-tree-core/editManagerFormatV5.ts +35 -0
  359. package/src/shared-tree-core/index.ts +3 -1
  360. package/src/shared-tree-core/messageCodecV1ToV4.ts +104 -0
  361. package/src/shared-tree-core/messageCodecV5.ts +131 -0
  362. package/src/shared-tree-core/messageCodecs.ts +16 -85
  363. package/src/shared-tree-core/messageFormatV5.ts +50 -0
  364. package/src/shared-tree-core/messageTypes.ts +15 -2
  365. package/src/shared-tree-core/sequenceIdUtils.ts +1 -1
  366. package/src/shared-tree-core/sharedTreeCore.ts +281 -85
  367. package/src/simple-tree/api/tree.ts +23 -0
  368. package/src/simple-tree/core/unhydratedFlexTree.ts +11 -2
  369. package/src/treeFactory.ts +48 -8
  370. package/dist/codec/noopValidator.d.ts +0 -13
  371. package/dist/codec/noopValidator.d.ts.map +0 -1
  372. package/dist/codec/noopValidator.js +0 -17
  373. package/dist/codec/noopValidator.js.map +0 -1
  374. package/dist/shared-tree-core/editManagerFormat.d.ts.map +0 -1
  375. package/dist/shared-tree-core/editManagerFormat.js.map +0 -1
  376. package/dist/shared-tree-core/messageFormat.d.ts.map +0 -1
  377. package/dist/shared-tree-core/messageFormat.js.map +0 -1
  378. package/docs/user-facing/schema-evolution.md +0 -309
  379. package/lib/codec/noopValidator.d.ts +0 -13
  380. package/lib/codec/noopValidator.d.ts.map +0 -1
  381. package/lib/codec/noopValidator.js +0 -14
  382. package/lib/codec/noopValidator.js.map +0 -1
  383. package/lib/shared-tree-core/editManagerFormat.d.ts.map +0 -1
  384. package/lib/shared-tree-core/editManagerFormat.js.map +0 -1
  385. package/lib/shared-tree-core/messageFormat.d.ts.map +0 -1
  386. package/lib/shared-tree-core/messageFormat.js.map +0 -1
  387. package/src/codec/noopValidator.ts +0 -18
  388. /package/src/shared-tree-core/{messageFormat.ts → messageFormatV1ToV4.ts} +0 -0
@@ -6,7 +6,7 @@
6
6
  import type { ErasedType, IFluidLoadable } from "@fluidframework/core-interfaces/internal";
7
7
  import { assert, fail } from "@fluidframework/core-utils/internal";
8
8
  import type { IChannelStorageService } from "@fluidframework/datastore-definitions/internal";
9
- import type { IIdCompressor } from "@fluidframework/id-compressor";
9
+ import type { IIdCompressor, StableId } from "@fluidframework/id-compressor";
10
10
  import type {
11
11
  IChannelView,
12
12
  IFluidSerializer,
@@ -20,8 +20,8 @@ import {
20
20
  import {
21
21
  type CodecWriteOptions,
22
22
  FluidClientVersion,
23
+ FormatValidatorNoOp,
23
24
  type ICodecOptions,
24
- noopValidator,
25
25
  } from "../codec/index.js";
26
26
  import {
27
27
  type FieldKey,
@@ -33,7 +33,6 @@ import {
33
33
  ObjectNodeStoredSchema,
34
34
  RevisionTagCodec,
35
35
  SchemaVersion,
36
- type TaggedChange,
37
36
  type TreeFieldStoredSchema,
38
37
  type TreeNodeSchemaIdentifier,
39
38
  type TreeNodeStoredSchema,
@@ -62,8 +61,8 @@ import {
62
61
  // eslint-disable-next-line import/no-internal-modules
63
62
  import type { FormatV1 } from "../feature-libraries/schema-index/index.js";
64
63
  import {
64
+ type BranchId,
65
65
  type ClonableSchemaAndPolicy,
66
- DefaultResubmitMachine,
67
66
  type ExplicitCoreCodecVersions,
68
67
  SharedTreeCore,
69
68
  } from "../shared-tree-core/index.js";
@@ -189,6 +188,10 @@ const formatVersionToTopLevelCodecVersions = new Map<number, ExplicitCodecVersio
189
188
  5,
190
189
  { forest: 1, schema: 2, detachedFieldIndex: 1, editManager: 4, message: 4, fieldBatch: 1 },
191
190
  ],
191
+ [
192
+ 100, // SharedTreeFormatVersion.vSharedBranches
193
+ { forest: 1, schema: 2, detachedFieldIndex: 1, editManager: 5, message: 5, fieldBatch: 1 },
194
+ ],
192
195
  ]);
193
196
 
194
197
  function getCodecVersions(formatVersion: number): ExplicitCodecVersions {
@@ -217,6 +220,8 @@ export class SharedTreeKernel
217
220
  return this.checkout.storedSchema;
218
221
  }
219
222
 
223
+ private readonly checkouts: Map<BranchId, TreeCheckout> = new Map();
224
+
220
225
  /**
221
226
  * The app-facing API for SharedTree implemented by this Kernel.
222
227
  * @remarks
@@ -232,7 +237,7 @@ export class SharedTreeKernel
232
237
  serializer: IFluidSerializer,
233
238
  submitLocalMessage: (content: unknown, localOpMetadata?: unknown) => void,
234
239
  lastSequenceNumber: () => number | undefined,
235
- logger: ITelemetryLoggerExt | undefined,
240
+ private readonly logger: ITelemetryLoggerExt | undefined,
236
241
  idCompressor: IIdCompressor,
237
242
  optionsParam: SharedTreeOptionsInternal,
238
243
  ) {
@@ -319,16 +324,12 @@ export class SharedTreeKernel
319
324
  idCompressor,
320
325
  schema,
321
326
  defaultSchemaPolicy,
322
- new DefaultResubmitMachine(
323
- (change: TaggedChange<SharedTreeChange>) =>
324
- changeFamily.rebaser.invert(change, true, this.mintRevisionTag()),
325
- changeEnricher,
326
- ),
327
+ undefined,
327
328
  changeEnricher,
328
329
  );
329
- const localBranch = this.getLocalBranch();
330
+
330
331
  this.checkout = createTreeCheckout(idCompressor, this.mintRevisionTag, revisionTagCodec, {
331
- branch: localBranch,
332
+ branch: this.getLocalBranch(),
332
333
  changeFamily,
333
334
  schema,
334
335
  forest,
@@ -340,39 +341,49 @@ export class SharedTreeKernel
340
341
  disposeForksAfterTransaction: options.disposeForksAfterTransaction,
341
342
  });
342
343
 
343
- this.checkout.transaction.events.on("started", () => {
344
- if (sharedObject.isAttached()) {
344
+ this.registerCheckout("main", this.checkout);
345
+
346
+ this.view = {
347
+ contentSnapshot: () => this.contentSnapshot(),
348
+ exportSimpleSchema: () => this.exportSimpleSchema(),
349
+ exportVerbose: () => this.exportVerbose(),
350
+ viewWith: this.viewWith.bind(this),
351
+ viewSharedBranchWith: this.viewBranchWith.bind(this),
352
+ createSharedBranch: this.createSharedBranch.bind(this),
353
+ getSharedBranchIds: this.getSharedBranchIds.bind(this),
354
+ kernel: this,
355
+ };
356
+ }
357
+
358
+ private registerCheckout(branchId: BranchId, checkout: TreeCheckout): void {
359
+ this.checkouts.set(branchId, checkout);
360
+ const enricher = this.getCommitEnricher(branchId);
361
+ checkout.transaction.events.on("started", () => {
362
+ if (this.sharedObject.isAttached()) {
345
363
  // It is currently forbidden to attach during a transaction, so transaction state changes can be ignored until after attaching.
346
- this.commitEnricher.startTransaction();
364
+ enricher.startTransaction();
347
365
  }
348
366
  });
349
- this.checkout.transaction.events.on("aborting", () => {
350
- if (sharedObject.isAttached()) {
367
+
368
+ checkout.transaction.events.on("aborting", () => {
369
+ if (this.sharedObject.isAttached()) {
351
370
  // It is currently forbidden to attach during a transaction, so transaction state changes can be ignored until after attaching.
352
- this.commitEnricher.abortTransaction();
371
+ enricher.abortTransaction();
353
372
  }
354
373
  });
355
- this.checkout.transaction.events.on("committing", () => {
356
- if (sharedObject.isAttached()) {
374
+ checkout.transaction.events.on("committing", () => {
375
+ if (this.sharedObject.isAttached()) {
357
376
  // It is currently forbidden to attach during a transaction, so transaction state changes can be ignored until after attaching.
358
- this.commitEnricher.commitTransaction();
377
+ enricher.commitTransaction();
359
378
  }
360
379
  });
361
- this.checkout.events.on("beforeBatch", (event) => {
362
- if (event.type === "append" && sharedObject.isAttached()) {
363
- if (this.checkout.transaction.isInProgress()) {
364
- this.commitEnricher.addTransactionCommits(event.newCommits);
380
+ checkout.events.on("beforeBatch", (event) => {
381
+ if (event.type === "append" && this.sharedObject.isAttached()) {
382
+ if (checkout.transaction.isInProgress()) {
383
+ enricher.addTransactionCommits(event.newCommits);
365
384
  }
366
385
  }
367
386
  });
368
-
369
- this.view = {
370
- contentSnapshot: () => this.contentSnapshot(),
371
- exportSimpleSchema: () => this.exportSimpleSchema(),
372
- exportVerbose: () => this.exportVerbose(),
373
- viewWith: this.viewWith.bind(this),
374
- kernel: this,
375
- };
376
387
  }
377
388
 
378
389
  public exportVerbose(): VerboseTree | undefined {
@@ -415,18 +426,56 @@ export class SharedTreeKernel
415
426
  TreeView<ReadSchema<TRoot>>;
416
427
  }
417
428
 
429
+ public viewBranchWith<TRoot extends ImplicitFieldSchema>(
430
+ branchId: string,
431
+ config: TreeViewConfiguration<TRoot>,
432
+ ): TreeView<TRoot>;
433
+
434
+ public viewBranchWith<TRoot extends ImplicitFieldSchema | UnsafeUnknownSchema>(
435
+ branchId: string,
436
+ config: TreeViewConfiguration<ReadSchema<TRoot>>,
437
+ ): SchematizingSimpleTreeView<TRoot> & TreeView<ReadSchema<TRoot>> {
438
+ const compressedId = this.idCompressor.tryRecompress(branchId as StableId);
439
+ if (compressedId === undefined) {
440
+ throw new UsageError(`No branch found with id: ${branchId}`);
441
+ }
442
+ return this.getCheckout(compressedId).viewWith(
443
+ config,
444
+ ) as SchematizingSimpleTreeView<TRoot> & TreeView<ReadSchema<TRoot>>;
445
+ }
446
+
447
+ private getCheckout(branchId: BranchId): TreeCheckout {
448
+ return this.checkouts.get(branchId) ?? this.checkoutBranch(branchId);
449
+ }
450
+
451
+ private checkoutBranch(branchId: BranchId): TreeCheckout {
452
+ const checkout = this.checkout.branch();
453
+ checkout.switchBranch(this.getSharedBranch(branchId));
454
+ const enricher = new SharedTreeReadonlyChangeEnricher(
455
+ checkout.forest,
456
+ checkout.storedSchema,
457
+ checkout.removedRoots,
458
+ );
459
+
460
+ this.registerSharedBranchForEditing(branchId, enricher);
461
+ this.registerCheckout(branchId, checkout);
462
+ return checkout;
463
+ }
464
+
418
465
  public override async loadCore(services: IChannelStorageService): Promise<void> {
419
466
  await super.loadCore(services);
420
467
  this.checkout.load();
421
468
  }
422
469
 
423
470
  public override didAttach(): void {
424
- if (this.checkout.transaction.isInProgress()) {
425
- // Attaching during a transaction is not currently supported.
426
- // At least part of of the system is known to not handle this case correctly - commit enrichment - and there may be others.
427
- throw new UsageError(
428
- "Cannot attach while a transaction is in progress. Commit or abort the transaction before attaching.",
429
- );
471
+ for (const checkout of this.checkouts.values()) {
472
+ if (checkout.transaction.isInProgress()) {
473
+ // Attaching during a transaction is not currently supported.
474
+ // At least part of of the system is known to not handle this case correctly - commit enrichment - and there may be others.
475
+ throw new UsageError(
476
+ "Cannot attach while a transaction is in progress. Commit or abort the transaction before attaching.",
477
+ );
478
+ }
430
479
  }
431
480
  super.didAttach();
432
481
  }
@@ -436,31 +485,35 @@ export class SharedTreeKernel
436
485
  SharedTreeCore<SharedTreeEditBuilder, SharedTreeChange>["applyStashedOp"]
437
486
  >
438
487
  ): void {
439
- assert(
440
- !this.checkout.transaction.isInProgress(),
441
- 0x674 /* Unexpected transaction is open while applying stashed ops */,
442
- );
488
+ for (const checkout of this.checkouts.values()) {
489
+ assert(
490
+ !checkout.transaction.isInProgress(),
491
+ 0x674 /* Unexpected transaction is open while applying stashed ops */,
492
+ );
493
+ }
443
494
  super.applyStashedOp(...args);
444
495
  }
445
496
 
446
497
  protected override submitCommit(
498
+ branchId: BranchId,
447
499
  commit: GraphCommit<SharedTreeChange>,
448
500
  schemaAndPolicy: ClonableSchemaAndPolicy,
449
501
  isResubmit: boolean,
450
502
  ): void {
503
+ const checkout = this.getCheckout(branchId);
451
504
  assert(
452
- !this.checkout.transaction.isInProgress(),
505
+ !checkout.transaction.isInProgress(),
453
506
  0xaa6 /* Cannot submit a commit while a transaction is in progress */,
454
507
  );
455
508
  if (isResubmit) {
456
- return super.submitCommit(commit, schemaAndPolicy, isResubmit);
509
+ return super.submitCommit(branchId, commit, schemaAndPolicy, isResubmit);
457
510
  }
458
511
 
459
512
  // Refrain from submitting new commits until they are validated by the checkout.
460
513
  // This is not a strict requirement for correctness in our system, but in the event that there is a bug when applying commits to the checkout
461
514
  // that causes a crash (e.g. in the forest), this will at least prevent this client from sending the problematic commit to any other clients.
462
- this.checkout.onCommitValid(commit, () =>
463
- super.submitCommit(commit, schemaAndPolicy, isResubmit),
515
+ checkout.onCommitValid(commit, () =>
516
+ super.submitCommit(branchId, commit, schemaAndPolicy, isResubmit),
464
517
  );
465
518
  }
466
519
 
@@ -561,6 +614,11 @@ export const SharedTreeFormatVersion = {
561
614
  * Requires \@fluidframework/tree \>= 2.0.0.
562
615
  */
563
616
  v5: 5,
617
+
618
+ /**
619
+ * For testing purposes only.
620
+ */
621
+ vSharedBranches: 100,
564
622
  } as const;
565
623
 
566
624
  /**
@@ -577,13 +635,19 @@ export const SharedTreeFormatVersion = {
577
635
  */
578
636
  export type SharedTreeFormatVersion = typeof SharedTreeFormatVersion;
579
637
 
638
+ /**
639
+ * Configuration options for SharedTree.
640
+ * @beta @input
641
+ */
642
+ export type SharedTreeOptionsBeta = ForestOptions;
643
+
580
644
  /**
581
645
  * Configuration options for SharedTree.
582
646
  * @alpha @input
583
647
  */
584
648
  export type SharedTreeOptions = Partial<CodecWriteOptions> &
585
649
  Partial<SharedTreeFormatOptions> &
586
- ForestOptions;
650
+ SharedTreeOptionsBeta;
587
651
 
588
652
  export interface SharedTreeOptionsInternal
589
653
  extends Omit<SharedTreeOptions, "treeEncodeType">,
@@ -661,7 +725,7 @@ export interface ForestType extends ErasedType<"ForestType"> {}
661
725
  * A simple implementation with minimal complexity and moderate debuggability, validation and performance.
662
726
  * @privateRemarks
663
727
  * The "ObjectForest" forest type.
664
- * @alpha
728
+ * @beta
665
729
  */
666
730
  export const ForestTypeReference = toForestType(
667
731
  (breaker: Breakable, schema: TreeStoredSchemaSubscription, idCompressor: IIdCompressor) =>
@@ -675,7 +739,7 @@ export const ForestTypeReference = toForestType(
675
739
  * Uses an internal representation optimized for size designed to scale to larger datasets with reduced overhead.
676
740
  * @privateRemarks
677
741
  * The "ChunkedForest" forest type.
678
- * @alpha
742
+ * @beta
679
743
  */
680
744
  export const ForestTypeOptimized = toForestType(
681
745
  (breaker: Breakable, schema: TreeStoredSchemaSubscription, idCompressor: IIdCompressor) =>
@@ -689,7 +753,7 @@ export const ForestTypeOptimized = toForestType(
689
753
  * May be asymptotically slower than {@link ForestTypeReference}, and may perform very badly with larger data sizes.
690
754
  * @privateRemarks
691
755
  * The "ObjectForest" forest type with expensive asserts for debugging.
692
- * @alpha
756
+ * @beta
693
757
  */
694
758
  export const ForestTypeExpensiveDebug = toForestType(
695
759
  (breaker: Breakable, schema: TreeStoredSchemaSubscription) =>
@@ -719,7 +783,7 @@ export function buildConfiguredForest(
719
783
  }
720
784
 
721
785
  export const defaultSharedTreeOptions: Required<SharedTreeOptionsInternal> = {
722
- jsonValidator: noopValidator,
786
+ jsonValidator: FormatValidatorNoOp,
723
787
  oldestCompatibleClient: FluidClientVersion.v2_0,
724
788
  forest: ForestTypeReference,
725
789
  treeEncodeType: TreeCompressionStrategy.Compressed,
@@ -17,7 +17,7 @@ import type { IIdCompressor } from "@fluidframework/id-compressor";
17
17
  import {
18
18
  asIndex,
19
19
  getKernel,
20
- type TreeNode,
20
+ TreeNode,
21
21
  type Unhydrated,
22
22
  TreeBeta,
23
23
  tryGetSchema,
@@ -55,15 +55,16 @@ import {
55
55
  convertField,
56
56
  toUnhydratedSchema,
57
57
  type TreeParsingOptions,
58
+ type NodeChangedData,
58
59
  } from "../simple-tree/index.js";
59
60
  import { brand, extractFromOpaque, type JsonCompatible } from "../util/index.js";
60
61
  import {
61
62
  FluidClientVersion,
62
- noopValidator,
63
63
  type ICodecOptions,
64
64
  type CodecWriteOptions,
65
+ FormatValidatorNoOp,
65
66
  } from "../codec/index.js";
66
- import { EmptyKey, type ITreeCursorSynchronous } from "../core/index.js";
67
+ import { EmptyKey, type FieldKey, type ITreeCursorSynchronous } from "../core/index.js";
67
68
  import {
68
69
  cursorForMapTreeField,
69
70
  defaultSchemaPolicy,
@@ -76,6 +77,9 @@ import {
76
77
  fluidVersionToFieldBatchCodecWriteVersion,
77
78
  type LocalNodeIdentifier,
78
79
  type FlexTreeSequenceField,
80
+ type FlexTreeNode,
81
+ type Observer,
82
+ withObservation,
79
83
  } from "../feature-libraries/index.js";
80
84
  import { independentInitializedView, type ViewContent } from "./independentView.js";
81
85
  import { SchematizingSimpleTreeView, ViewSlot } from "./schematizingTreeView.js";
@@ -419,6 +423,283 @@ export interface TreeAlpha {
419
423
  children(
420
424
  node: TreeNode,
421
425
  ): Iterable<[propertyKey: string | number, child: TreeNode | TreeLeafValue]>;
426
+
427
+ /**
428
+ * Track observations of any TreeNode content.
429
+ * @remarks
430
+ * This subscribes to changes to any nodes content observed during `trackDuring`.
431
+ *
432
+ * Currently this does not support tracking parentage (see {@link (TreeAlpha:interface).trackObservationsOnce} for a version which does):
433
+ * if accessing parentage during `trackDuring`, this will throw a usage error.
434
+ *
435
+ * This also does not track node status changes (e.g. whether a node is attached to a view or not).
436
+ * The current behavior of checking status is unspecified: future versions may track it, error, or ignore it.
437
+ *
438
+ * These subscriptions remain active until `unsubscribe` is called: `onInvalidation` may be called multiple times.
439
+ * See {@link (TreeAlpha:interface).trackObservationsOnce} for a version which automatically unsubscribes on the first invalidation.
440
+ * @privateRemarks
441
+ * This version, while more general than {@link (TreeAlpha:interface).trackObservationsOnce}, might be unnecessary.
442
+ * Maybe this should be removed and only `trackObservationsOnce` kept.
443
+ * Reevaluate this before stabilizing.
444
+ */
445
+ trackObservations<TResult>(
446
+ onInvalidation: () => void,
447
+ trackDuring: () => TResult,
448
+ ): ObservationResults<TResult>;
449
+
450
+ /**
451
+ * {@link (TreeAlpha:interface).trackObservations} except automatically unsubscribes when the first invalidation occurs.
452
+ * @remarks
453
+ * This also supports tracking parentage, unlike {@link (TreeAlpha:interface).trackObservations}, as long as the parent is not undefined.
454
+ *
455
+ * @example Simple cached value invalidation
456
+ * ```typescript
457
+ * // Compute and cache this "foo" value, and clear the cache when the fields read in the callback to compute it change.
458
+ * cachedFoo ??= TreeAlpha.trackObservationsOnce(
459
+ * () => {
460
+ * cachedFoo = undefined;
461
+ * },
462
+ * () => nodeA.someChild.bar + nodeB.someChild.baz,
463
+ * ).result;
464
+ * ```
465
+ *
466
+ * That is equivalent to doing the following:
467
+ * ```typescript
468
+ * if (cachedFoo === undefined) {
469
+ * cachedFoo = nodeA.someChild.bar + nodeB.someChild.baz;
470
+ * const invalidate = (): void => {
471
+ * cachedFoo = undefined;
472
+ * for (const u of unsubscribe) {
473
+ * u();
474
+ * }
475
+ * };
476
+ * const unsubscribe: (() => void)[] = [
477
+ * TreeBeta.on(nodeA, "nodeChanged", (data) => {
478
+ * if (data.changedProperties.has("someChild")) {
479
+ * invalidate();
480
+ * }
481
+ * }),
482
+ * TreeBeta.on(nodeB, "nodeChanged", (data) => {
483
+ * if (data.changedProperties.has("someChild")) {
484
+ * invalidate();
485
+ * }
486
+ * }),
487
+ * TreeBeta.on(nodeA.someChild, "nodeChanged", (data) => {
488
+ * if (data.changedProperties.has("bar")) {
489
+ * invalidate();
490
+ * }
491
+ * }),
492
+ * TreeBeta.on(nodeB.someChild, "nodeChanged", (data) => {
493
+ * if (data.changedProperties.has("baz")) {
494
+ * invalidate();
495
+ * }
496
+ * }),
497
+ * ];
498
+ * }
499
+ * ```
500
+ * @example Cached derived schema property
501
+ * ```typescript
502
+ * const factory = new SchemaFactory("com.example");
503
+ * class Vector extends factory.object("Vector", {
504
+ * x: SchemaFactory.number,
505
+ * y: SchemaFactory.number,
506
+ * }) {
507
+ * #length: number | undefined = undefined;
508
+ * public length(): number {
509
+ * if (this.#length === undefined) {
510
+ * const result = TreeAlpha.trackObservationsOnce(
511
+ * () => {
512
+ * this.#length = undefined;
513
+ * },
514
+ * () => Math.hypot(this.x, this.y),
515
+ * );
516
+ * this.#length = result.result;
517
+ * }
518
+ * return this.#length;
519
+ * }
520
+ * }
521
+ * const vec = new Vector({ x: 3, y: 4 });
522
+ * assert.equal(vec.length(), 5);
523
+ * vec.x = 0;
524
+ * assert.equal(vec.length(), 4);
525
+ * ```
526
+ */
527
+ trackObservationsOnce<TResult>(
528
+ onInvalidation: () => void,
529
+ trackDuring: () => TResult,
530
+ ): ObservationResults<TResult>;
531
+ }
532
+
533
+ /**
534
+ * Results from an operation with tracked observations.
535
+ * @remarks
536
+ * Results from {@link (TreeAlpha:interface).trackObservations} or {@link (TreeAlpha:interface).trackObservationsOnce}.
537
+ * @sealed @alpha
538
+ */
539
+ export interface ObservationResults<TResult> {
540
+ /**
541
+ * The result of the operation which had its observations tracked.
542
+ */
543
+ readonly result: TResult;
544
+
545
+ /**
546
+ * Call to unsubscribe from further invalidations.
547
+ */
548
+ readonly unsubscribe: () => void;
549
+ }
550
+
551
+ /**
552
+ * Subscription to changes on a single node.
553
+ * @remarks
554
+ * Either tracks some set of fields, or all fields and can be updated to track more fields.
555
+ */
556
+ class NodeSubscription {
557
+ /**
558
+ * If undefined, subscribes to all keys.
559
+ * Otherwise only subscribes to the keys in the set.
560
+ */
561
+ private keys: Set<FieldKey> | undefined;
562
+ private readonly unsubscribe: () => void;
563
+ private constructor(
564
+ private readonly onInvalidation: () => void,
565
+ flexNode: FlexTreeNode,
566
+ ) {
567
+ // TODO:Performance: It is possible to optimize this to not use the public TreeNode API.
568
+ const node = getOrCreateNodeFromInnerNode(flexNode);
569
+ assert(node instanceof TreeNode, 0xc54 /* Unexpected leaf value */);
570
+
571
+ const handler = (data: NodeChangedData): void => {
572
+ if (this.keys === undefined || data.changedProperties === undefined) {
573
+ this.onInvalidation();
574
+ } else {
575
+ let keyMap: ReadonlyMap<FieldKey, string> | undefined;
576
+ const schema = treeNodeApi.schema(node);
577
+ if (isObjectNodeSchema(schema)) {
578
+ keyMap = schema.storedKeyToPropertyKey;
579
+ }
580
+ // TODO:Performance: Ideally this would use Set.prototype.isDisjointFrom when available.
581
+ for (const flexKey of this.keys) {
582
+ // TODO:Performance: doing everything at the flex tree layer could avoid this translation
583
+ const key = keyMap?.get(flexKey) ?? flexKey;
584
+
585
+ if (data.changedProperties.has(key)) {
586
+ this.onInvalidation();
587
+ return;
588
+ }
589
+ }
590
+ }
591
+ };
592
+ this.unsubscribe = TreeBeta.on(node, "nodeChanged", handler);
593
+ }
594
+
595
+ /**
596
+ * Create an {@link Observer} which subscribes to what was observed in {@link NodeSubscription}s.
597
+ */
598
+ public static createObserver(
599
+ invalidate: () => void,
600
+ onlyOnce = false,
601
+ ): { observer: Observer; unsubscribe: () => void } {
602
+ const subscriptions = new Map<FlexTreeNode, NodeSubscription>();
603
+ const observer: Observer = {
604
+ observeNodeFields(flexNode: FlexTreeNode): void {
605
+ if (flexNode.value !== undefined) {
606
+ // Leaf value, nothing to observe.
607
+ return;
608
+ }
609
+ const subscription = subscriptions.get(flexNode);
610
+ if (subscription !== undefined) {
611
+ // Already subscribed to this node.
612
+ subscription.keys = undefined; // Now subscribed to all keys.
613
+ } else {
614
+ const newSubscription = new NodeSubscription(invalidate, flexNode);
615
+ subscriptions.set(flexNode, newSubscription);
616
+ }
617
+ },
618
+ observeNodeField(flexNode: FlexTreeNode, key: FieldKey): void {
619
+ if (flexNode.value !== undefined) {
620
+ // Leaf value, nothing to observe.
621
+ return;
622
+ }
623
+ const subscription = subscriptions.get(flexNode);
624
+ if (subscription !== undefined) {
625
+ // Already subscribed to this node: if not subscribed to all keys, subscribe to this one.
626
+ // TODO:Performance: due to how JavaScript set ordering works,
627
+ // it might be faster to check `has` and only add if not present in case the same field is viewed many times.
628
+ subscription.keys?.add(key);
629
+ } else {
630
+ const newSubscription = new NodeSubscription(invalidate, flexNode);
631
+ newSubscription.keys = new Set([key]);
632
+ subscriptions.set(flexNode, newSubscription);
633
+ }
634
+ },
635
+ observeParentOf(node: FlexTreeNode): void {
636
+ // Supporting parent tracking is more difficult that it might seem at first.
637
+ // There are two main complicating factors:
638
+ // 1. The parent may be undefined (the node is a root).
639
+ // 2. If tracking this by subscribing to the parent's changes, then which events are subscribed to needs to be updated after the parent changes.
640
+ //
641
+ // If not supporting the first case (undefined parents), the second case gets problematic: edits which un-parent a node could error due to being unable to update the event subscription.
642
+ // For now this is mitigated by only supporting one of tracking (non-undefined) parents or maintaining event subscriptions across edits.
643
+
644
+ if (!onlyOnce) {
645
+ // TODO: better APIS should be provided which make handling this case practical.
646
+ throw new UsageError("Observation tracking for parents is currently not supported.");
647
+ }
648
+
649
+ const parent = withObservation(undefined, () => node.parentField.parent);
650
+
651
+ if (parent.parent === undefined) {
652
+ // TODO: better APIS should be provided which make handling this case practical.
653
+ throw new UsageError(
654
+ "Observation tracking for parents is currently not supported when parent is undefined.",
655
+ );
656
+ }
657
+ observer.observeNodeField(parent.parent, parent.key);
658
+ },
659
+ };
660
+
661
+ let subscribed = true;
662
+
663
+ return {
664
+ observer,
665
+ unsubscribe: () => {
666
+ if (!subscribed) {
667
+ throw new UsageError("Already unsubscribed");
668
+ }
669
+ subscribed = false;
670
+ for (const subscription of subscriptions.values()) {
671
+ subscription.unsubscribe();
672
+ }
673
+ },
674
+ };
675
+ }
676
+ }
677
+
678
+ /**
679
+ * Handles both {@link (TreeAlpha:interface).trackObservations} and {@link (TreeAlpha:interface).trackObservationsOnce}.
680
+ */
681
+ function trackObservations<TResult>(
682
+ onInvalidation: () => void,
683
+ trackDuring: () => TResult,
684
+ onlyOnce = false,
685
+ ): ObservationResults<TResult> {
686
+ let observing = true;
687
+
688
+ const invalidate = (): void => {
689
+ if (observing) {
690
+ throw new UsageError("Cannot invalidate while tracking observations");
691
+ }
692
+ onInvalidation();
693
+ };
694
+
695
+ const { observer, unsubscribe } = NodeSubscription.createObserver(invalidate, onlyOnce);
696
+ const result = withObservation(observer, trackDuring);
697
+ observing = false;
698
+
699
+ return {
700
+ result,
701
+ unsubscribe,
702
+ };
422
703
  }
423
704
 
424
705
  /**
@@ -427,6 +708,30 @@ export interface TreeAlpha {
427
708
  * @alpha
428
709
  */
429
710
  export const TreeAlpha: TreeAlpha = {
711
+ trackObservations<TResult>(
712
+ onInvalidation: () => void,
713
+ trackDuring: () => TResult,
714
+ ): ObservationResults<TResult> {
715
+ return trackObservations(onInvalidation, trackDuring);
716
+ },
717
+
718
+ trackObservationsOnce<TResult>(
719
+ onInvalidation: () => void,
720
+ trackDuring: () => TResult,
721
+ ): ObservationResults<TResult> {
722
+ const result = trackObservations(
723
+ () => {
724
+ // trackObservations ensures no invalidation occurs while its running,
725
+ // so this callback can only run after trackObservations has returns and thus result is defined.
726
+ result.unsubscribe();
727
+ onInvalidation();
728
+ },
729
+ trackDuring,
730
+ true,
731
+ );
732
+ return result;
733
+ },
734
+
430
735
  branch(node: TreeNode): TreeBranch | undefined {
431
736
  const kernel = getKernel(node);
432
737
  if (!kernel.isHydrated()) {
@@ -523,7 +828,7 @@ export const TreeAlpha: TreeAlpha = {
523
828
  ): JsonCompatible<IFluidHandle> {
524
829
  const schema = tryGetSchema(node) ?? fail(0xacf /* invalid input */);
525
830
  const format = fluidVersionToFieldBatchCodecWriteVersion(options.oldestCompatibleClient);
526
- const codec = makeFieldBatchCodec({ jsonValidator: noopValidator }, format);
831
+ const codec = makeFieldBatchCodec({ jsonValidator: FormatValidatorNoOp }, format);
527
832
  const cursor = borrowFieldCursorFromTreeNodeOrValue(node);
528
833
  const batch: FieldBatch = [cursor];
529
834
  // If none provided, create a compressor which will not compress anything.