@fluidframework/tree 2.32.0 → 2.33.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 (339) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/api-report/tree.alpha.api.md +169 -192
  3. package/api-report/tree.beta.api.md +135 -164
  4. package/api-report/tree.legacy.alpha.api.md +138 -167
  5. package/api-report/tree.legacy.public.api.md +135 -164
  6. package/api-report/tree.public.api.md +135 -164
  7. package/dist/alpha.d.ts +3 -11
  8. package/dist/beta.d.ts +2 -11
  9. package/dist/core/index.d.ts +1 -1
  10. package/dist/core/index.d.ts.map +1 -1
  11. package/dist/core/index.js +1 -2
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/schema-stored/index.d.ts +1 -1
  14. package/dist/core/schema-stored/index.d.ts.map +1 -1
  15. package/dist/core/schema-stored/index.js +1 -2
  16. package/dist/core/schema-stored/index.js.map +1 -1
  17. package/dist/core/schema-stored/schema.d.ts +4 -11
  18. package/dist/core/schema-stored/schema.d.ts.map +1 -1
  19. package/dist/core/schema-stored/schema.js +7 -14
  20. package/dist/core/schema-stored/schema.js.map +1 -1
  21. package/dist/core/tree/anchorSet.d.ts.map +1 -1
  22. package/dist/core/tree/anchorSet.js +31 -24
  23. package/dist/core/tree/anchorSet.js.map +1 -1
  24. package/dist/core/tree/deltaUtil.d.ts +1 -4
  25. package/dist/core/tree/deltaUtil.d.ts.map +1 -1
  26. package/dist/core/tree/deltaUtil.js +1 -13
  27. package/dist/core/tree/deltaUtil.js.map +1 -1
  28. package/dist/core/tree/visitDelta.d.ts +6 -29
  29. package/dist/core/tree/visitDelta.d.ts.map +1 -1
  30. package/dist/core/tree/visitDelta.js +11 -50
  31. package/dist/core/tree/visitDelta.js.map +1 -1
  32. package/dist/core/tree/visitorUtils.d.ts +12 -9
  33. package/dist/core/tree/visitorUtils.d.ts.map +1 -1
  34. package/dist/core/tree/visitorUtils.js +19 -32
  35. package/dist/core/tree/visitorUtils.js.map +1 -1
  36. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  37. package/dist/feature-libraries/chunked-forest/chunkedForest.js +6 -11
  38. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  39. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +1 -1
  40. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
  41. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  42. package/dist/feature-libraries/chunked-forest/codec/nodeShape.d.ts +41 -5
  43. package/dist/feature-libraries/chunked-forest/codec/nodeShape.d.ts.map +1 -1
  44. package/dist/feature-libraries/chunked-forest/codec/nodeShape.js +43 -26
  45. package/dist/feature-libraries/chunked-forest/codec/nodeShape.js.map +1 -1
  46. package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +1 -1
  47. package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
  48. package/dist/feature-libraries/flex-tree/context.d.ts +5 -0
  49. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  50. package/dist/feature-libraries/flex-tree/context.js +4 -0
  51. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  52. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +8 -0
  53. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  54. package/dist/feature-libraries/flex-tree/flexTreeTypes.js +8 -0
  55. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  56. package/dist/feature-libraries/indexing/anchorTreeIndex.d.ts.map +1 -1
  57. package/dist/feature-libraries/indexing/anchorTreeIndex.js +15 -22
  58. package/dist/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  59. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.d.ts.map +1 -1
  60. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.js +29 -25
  61. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.js.map +1 -1
  62. package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  63. package/dist/feature-libraries/object-forest/objectForest.js +1 -7
  64. package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
  65. package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
  66. package/dist/feature-libraries/schema-index/codec.js +1 -1
  67. package/dist/feature-libraries/schema-index/codec.js.map +1 -1
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +3 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/internalTypes.d.ts +1 -1
  73. package/dist/internalTypes.d.ts.map +1 -1
  74. package/dist/internalTypes.js.map +1 -1
  75. package/dist/jsonDomainSchema.d.ts +6 -6
  76. package/dist/jsonDomainSchema.d.ts.map +1 -1
  77. package/dist/jsonDomainSchema.js.map +1 -1
  78. package/dist/legacy.d.ts +2 -11
  79. package/dist/packageVersion.d.ts +1 -1
  80. package/dist/packageVersion.js +1 -1
  81. package/dist/packageVersion.js.map +1 -1
  82. package/dist/public.d.ts +2 -11
  83. package/dist/serializableDomainSchema.d.ts +5 -5
  84. package/dist/shared-tree/checkoutFlexTreeView.d.ts +1 -0
  85. package/dist/shared-tree/checkoutFlexTreeView.d.ts.map +1 -1
  86. package/dist/shared-tree/checkoutFlexTreeView.js +4 -0
  87. package/dist/shared-tree/checkoutFlexTreeView.js.map +1 -1
  88. package/dist/shared-tree/index.d.ts +3 -2
  89. package/dist/shared-tree/index.d.ts.map +1 -1
  90. package/dist/shared-tree/index.js +2 -2
  91. package/dist/shared-tree/index.js.map +1 -1
  92. package/dist/shared-tree/sharedTree.d.ts +4 -35
  93. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  94. package/dist/shared-tree/sharedTree.js +4 -54
  95. package/dist/shared-tree/sharedTree.js.map +1 -1
  96. package/dist/simple-tree/api/index.d.ts +2 -1
  97. package/dist/simple-tree/api/index.d.ts.map +1 -1
  98. package/dist/simple-tree/api/index.js +3 -1
  99. package/dist/simple-tree/api/index.js.map +1 -1
  100. package/dist/simple-tree/api/schemaFactory.d.ts +28 -28
  101. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  102. package/dist/simple-tree/api/schemaFactory.js +11 -11
  103. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  104. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts +6 -7
  105. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  106. package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  107. package/dist/simple-tree/api/schemaFactoryRecursive.d.ts +24 -5
  108. package/dist/simple-tree/api/schemaFactoryRecursive.d.ts.map +1 -1
  109. package/dist/simple-tree/api/schemaFactoryRecursive.js +14 -1
  110. package/dist/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  111. package/dist/simple-tree/api/testRecursiveDomain.d.ts +10 -10
  112. package/dist/simple-tree/api/tree.d.ts +1 -14
  113. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  114. package/dist/simple-tree/api/tree.js.map +1 -1
  115. package/dist/simple-tree/api/typesUnsafe.d.ts +237 -220
  116. package/dist/simple-tree/api/typesUnsafe.d.ts.map +1 -1
  117. package/dist/simple-tree/api/typesUnsafe.js.map +1 -1
  118. package/dist/simple-tree/arrayNode.d.ts +2 -2
  119. package/dist/simple-tree/arrayNode.d.ts.map +1 -1
  120. package/dist/simple-tree/arrayNode.js.map +1 -1
  121. package/dist/simple-tree/core/treeNodeKernel.d.ts +9 -2
  122. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  123. package/dist/simple-tree/core/treeNodeKernel.js +39 -24
  124. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  125. package/dist/simple-tree/core/types.d.ts +14 -4
  126. package/dist/simple-tree/core/types.d.ts.map +1 -1
  127. package/dist/simple-tree/core/types.js +14 -4
  128. package/dist/simple-tree/core/types.js.map +1 -1
  129. package/dist/simple-tree/core/unhydratedFlexTree.d.ts +2 -1
  130. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  131. package/dist/simple-tree/core/unhydratedFlexTree.js +3 -0
  132. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  133. package/dist/simple-tree/index.d.ts +1 -1
  134. package/dist/simple-tree/index.d.ts.map +1 -1
  135. package/dist/simple-tree/index.js +3 -2
  136. package/dist/simple-tree/index.js.map +1 -1
  137. package/dist/simple-tree/objectNode.d.ts +9 -2
  138. package/dist/simple-tree/objectNode.d.ts.map +1 -1
  139. package/dist/simple-tree/objectNode.js +3 -0
  140. package/dist/simple-tree/objectNode.js.map +1 -1
  141. package/dist/simple-tree/schemaTypes.d.ts +10 -5
  142. package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
  143. package/dist/simple-tree/schemaTypes.js +5 -2
  144. package/dist/simple-tree/schemaTypes.js.map +1 -1
  145. package/dist/tableSchema.d.ts +78 -26
  146. package/dist/tableSchema.d.ts.map +1 -1
  147. package/dist/tableSchema.js +53 -24
  148. package/dist/tableSchema.js.map +1 -1
  149. package/dist/treeFactory.d.ts +6 -12
  150. package/dist/treeFactory.d.ts.map +1 -1
  151. package/dist/treeFactory.js +56 -5
  152. package/dist/treeFactory.js.map +1 -1
  153. package/lib/alpha.d.ts +3 -11
  154. package/lib/beta.d.ts +2 -11
  155. package/lib/core/index.d.ts +1 -1
  156. package/lib/core/index.d.ts.map +1 -1
  157. package/lib/core/index.js +1 -1
  158. package/lib/core/index.js.map +1 -1
  159. package/lib/core/schema-stored/index.d.ts +1 -1
  160. package/lib/core/schema-stored/index.d.ts.map +1 -1
  161. package/lib/core/schema-stored/index.js +1 -1
  162. package/lib/core/schema-stored/index.js.map +1 -1
  163. package/lib/core/schema-stored/schema.d.ts +4 -11
  164. package/lib/core/schema-stored/schema.d.ts.map +1 -1
  165. package/lib/core/schema-stored/schema.js +6 -12
  166. package/lib/core/schema-stored/schema.js.map +1 -1
  167. package/lib/core/tree/anchorSet.d.ts.map +1 -1
  168. package/lib/core/tree/anchorSet.js +31 -24
  169. package/lib/core/tree/anchorSet.js.map +1 -1
  170. package/lib/core/tree/deltaUtil.d.ts +1 -4
  171. package/lib/core/tree/deltaUtil.d.ts.map +1 -1
  172. package/lib/core/tree/deltaUtil.js +0 -9
  173. package/lib/core/tree/deltaUtil.js.map +1 -1
  174. package/lib/core/tree/visitDelta.d.ts +6 -29
  175. package/lib/core/tree/visitDelta.d.ts.map +1 -1
  176. package/lib/core/tree/visitDelta.js +12 -51
  177. package/lib/core/tree/visitDelta.js.map +1 -1
  178. package/lib/core/tree/visitorUtils.d.ts +12 -9
  179. package/lib/core/tree/visitorUtils.d.ts.map +1 -1
  180. package/lib/core/tree/visitorUtils.js +19 -32
  181. package/lib/core/tree/visitorUtils.js.map +1 -1
  182. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  183. package/lib/feature-libraries/chunked-forest/chunkedForest.js +6 -11
  184. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  185. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +1 -1
  186. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
  187. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  188. package/lib/feature-libraries/chunked-forest/codec/nodeShape.d.ts +41 -5
  189. package/lib/feature-libraries/chunked-forest/codec/nodeShape.d.ts.map +1 -1
  190. package/lib/feature-libraries/chunked-forest/codec/nodeShape.js +43 -26
  191. package/lib/feature-libraries/chunked-forest/codec/nodeShape.js.map +1 -1
  192. package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +1 -1
  193. package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
  194. package/lib/feature-libraries/flex-tree/context.d.ts +5 -0
  195. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  196. package/lib/feature-libraries/flex-tree/context.js +5 -1
  197. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  198. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +8 -0
  199. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  200. package/lib/feature-libraries/flex-tree/flexTreeTypes.js +8 -0
  201. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  202. package/lib/feature-libraries/indexing/anchorTreeIndex.d.ts.map +1 -1
  203. package/lib/feature-libraries/indexing/anchorTreeIndex.js +15 -22
  204. package/lib/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  205. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.d.ts.map +1 -1
  206. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.js +28 -24
  207. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.js.map +1 -1
  208. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  209. package/lib/feature-libraries/object-forest/objectForest.js +1 -7
  210. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  211. package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
  212. package/lib/feature-libraries/schema-index/codec.js +2 -2
  213. package/lib/feature-libraries/schema-index/codec.js.map +1 -1
  214. package/lib/index.d.ts +1 -1
  215. package/lib/index.d.ts.map +1 -1
  216. package/lib/index.js +1 -1
  217. package/lib/index.js.map +1 -1
  218. package/lib/internalTypes.d.ts +1 -1
  219. package/lib/internalTypes.d.ts.map +1 -1
  220. package/lib/internalTypes.js.map +1 -1
  221. package/lib/jsonDomainSchema.d.ts +6 -6
  222. package/lib/jsonDomainSchema.d.ts.map +1 -1
  223. package/lib/jsonDomainSchema.js.map +1 -1
  224. package/lib/legacy.d.ts +2 -11
  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/public.d.ts +2 -11
  229. package/lib/serializableDomainSchema.d.ts +5 -5
  230. package/lib/shared-tree/checkoutFlexTreeView.d.ts +1 -0
  231. package/lib/shared-tree/checkoutFlexTreeView.d.ts.map +1 -1
  232. package/lib/shared-tree/checkoutFlexTreeView.js +4 -0
  233. package/lib/shared-tree/checkoutFlexTreeView.js.map +1 -1
  234. package/lib/shared-tree/index.d.ts +3 -2
  235. package/lib/shared-tree/index.d.ts.map +1 -1
  236. package/lib/shared-tree/index.js +1 -1
  237. package/lib/shared-tree/index.js.map +1 -1
  238. package/lib/shared-tree/sharedTree.d.ts +4 -35
  239. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  240. package/lib/shared-tree/sharedTree.js +2 -51
  241. package/lib/shared-tree/sharedTree.js.map +1 -1
  242. package/lib/simple-tree/api/index.d.ts +2 -1
  243. package/lib/simple-tree/api/index.d.ts.map +1 -1
  244. package/lib/simple-tree/api/index.js +1 -0
  245. package/lib/simple-tree/api/index.js.map +1 -1
  246. package/lib/simple-tree/api/schemaFactory.d.ts +28 -28
  247. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  248. package/lib/simple-tree/api/schemaFactory.js +11 -11
  249. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  250. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts +6 -7
  251. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  252. package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  253. package/lib/simple-tree/api/schemaFactoryRecursive.d.ts +24 -5
  254. package/lib/simple-tree/api/schemaFactoryRecursive.d.ts.map +1 -1
  255. package/lib/simple-tree/api/schemaFactoryRecursive.js +12 -0
  256. package/lib/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  257. package/lib/simple-tree/api/testRecursiveDomain.d.ts +10 -10
  258. package/lib/simple-tree/api/tree.d.ts +1 -14
  259. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  260. package/lib/simple-tree/api/tree.js.map +1 -1
  261. package/lib/simple-tree/api/typesUnsafe.d.ts +237 -220
  262. package/lib/simple-tree/api/typesUnsafe.d.ts.map +1 -1
  263. package/lib/simple-tree/api/typesUnsafe.js.map +1 -1
  264. package/lib/simple-tree/arrayNode.d.ts +2 -2
  265. package/lib/simple-tree/arrayNode.d.ts.map +1 -1
  266. package/lib/simple-tree/arrayNode.js.map +1 -1
  267. package/lib/simple-tree/core/treeNodeKernel.d.ts +9 -2
  268. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  269. package/lib/simple-tree/core/treeNodeKernel.js +40 -25
  270. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  271. package/lib/simple-tree/core/types.d.ts +14 -4
  272. package/lib/simple-tree/core/types.d.ts.map +1 -1
  273. package/lib/simple-tree/core/types.js +14 -4
  274. package/lib/simple-tree/core/types.js.map +1 -1
  275. package/lib/simple-tree/core/unhydratedFlexTree.d.ts +2 -1
  276. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  277. package/lib/simple-tree/core/unhydratedFlexTree.js +3 -0
  278. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  279. package/lib/simple-tree/index.d.ts +1 -1
  280. package/lib/simple-tree/index.d.ts.map +1 -1
  281. package/lib/simple-tree/index.js +1 -1
  282. package/lib/simple-tree/index.js.map +1 -1
  283. package/lib/simple-tree/objectNode.d.ts +9 -2
  284. package/lib/simple-tree/objectNode.d.ts.map +1 -1
  285. package/lib/simple-tree/objectNode.js +4 -1
  286. package/lib/simple-tree/objectNode.js.map +1 -1
  287. package/lib/simple-tree/schemaTypes.d.ts +10 -5
  288. package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
  289. package/lib/simple-tree/schemaTypes.js +5 -2
  290. package/lib/simple-tree/schemaTypes.js.map +1 -1
  291. package/lib/tableSchema.d.ts +78 -26
  292. package/lib/tableSchema.d.ts.map +1 -1
  293. package/lib/tableSchema.js +53 -24
  294. package/lib/tableSchema.js.map +1 -1
  295. package/lib/treeFactory.d.ts +6 -12
  296. package/lib/treeFactory.d.ts.map +1 -1
  297. package/lib/treeFactory.js +55 -3
  298. package/lib/treeFactory.js.map +1 -1
  299. package/lib/tsdoc-metadata.json +1 -1
  300. package/package.json +22 -23
  301. package/src/core/index.ts +0 -2
  302. package/src/core/schema-stored/index.ts +0 -2
  303. package/src/core/schema-stored/schema.ts +10 -29
  304. package/src/core/tree/anchorSet.ts +32 -24
  305. package/src/core/tree/deltaUtil.ts +1 -13
  306. package/src/core/tree/visitDelta.ts +24 -81
  307. package/src/core/tree/visitorUtils.ts +43 -53
  308. package/src/feature-libraries/chunked-forest/chunkedForest.ts +7 -22
  309. package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +1 -1
  310. package/src/feature-libraries/chunked-forest/codec/nodeShape.ts +45 -27
  311. package/src/feature-libraries/chunked-forest/codec/schemaBasedEncode.ts +1 -1
  312. package/src/feature-libraries/flex-tree/context.ts +11 -1
  313. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +8 -0
  314. package/src/feature-libraries/indexing/anchorTreeIndex.ts +17 -28
  315. package/src/feature-libraries/node-identifier/nodeIdentifierManager.ts +39 -38
  316. package/src/feature-libraries/object-forest/objectForest.ts +1 -18
  317. package/src/feature-libraries/schema-index/codec.ts +1 -2
  318. package/src/index.ts +3 -11
  319. package/src/internalTypes.ts +0 -19
  320. package/src/jsonDomainSchema.ts +1 -3
  321. package/src/packageVersion.ts +1 -1
  322. package/src/shared-tree/checkoutFlexTreeView.ts +6 -0
  323. package/src/shared-tree/index.ts +8 -3
  324. package/src/shared-tree/sharedTree.ts +5 -133
  325. package/src/simple-tree/api/index.ts +2 -22
  326. package/src/simple-tree/api/schemaFactory.ts +40 -54
  327. package/src/simple-tree/api/schemaFactoryAlpha.ts +9 -14
  328. package/src/simple-tree/api/schemaFactoryRecursive.ts +25 -5
  329. package/src/simple-tree/api/tree.ts +1 -14
  330. package/src/simple-tree/api/typesUnsafe.ts +359 -341
  331. package/src/simple-tree/arrayNode.ts +2 -2
  332. package/src/simple-tree/core/treeNodeKernel.ts +46 -28
  333. package/src/simple-tree/core/types.ts +14 -4
  334. package/src/simple-tree/core/unhydratedFlexTree.ts +5 -1
  335. package/src/simple-tree/index.ts +2 -22
  336. package/src/simple-tree/objectNode.ts +13 -4
  337. package/src/simple-tree/schemaTypes.ts +11 -7
  338. package/src/tableSchema.ts +190 -63
  339. package/src/treeFactory.ts +151 -8
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import { fail } from "@fluidframework/core-utils/internal";
7
- import type { ErasedType } from "@fluidframework/core-interfaces";
8
7
  import { DiscriminatedUnionDispatcher } from "../../codec/index.js";
9
8
  import { type MakeNominal, brand, invertMap } from "../../util/index.js";
10
9
  import {
@@ -148,24 +147,6 @@ export const storedEmptyFieldSchema: TreeFieldStoredSchema = {
148
147
  */
149
148
  export const identifierFieldKindIdentifier = "Identifier";
150
149
 
151
- /**
152
- * Opaque type erased handle to the encoded representation of the contents of a stored schema.
153
- */
154
- export interface ErasedTreeNodeSchemaDataFormat
155
- extends ErasedType<"TreeNodeSchemaDataFormat"> {}
156
-
157
- function toErasedTreeNodeSchemaDataFormat(
158
- data: TreeNodeSchemaDataFormat,
159
- ): ErasedTreeNodeSchemaDataFormat {
160
- return data as unknown as ErasedTreeNodeSchemaDataFormat;
161
- }
162
-
163
- export function toTreeNodeSchemaDataFormat(
164
- data: ErasedTreeNodeSchemaDataFormat,
165
- ): TreeNodeSchemaDataFormat {
166
- return data as unknown as TreeNodeSchemaDataFormat;
167
- }
168
-
169
150
  /**
170
151
  */
171
152
  export abstract class TreeNodeStoredSchema {
@@ -177,7 +158,7 @@ export abstract class TreeNodeStoredSchema {
177
158
  * This is uses an opaque type to avoid leaking these types out of the package,
178
159
  * and is runtime validated by the codec.
179
160
  */
180
- public abstract encode(): ErasedTreeNodeSchemaDataFormat;
161
+ public abstract encode(): TreeNodeSchemaDataFormat;
181
162
 
182
163
  /**
183
164
  * Returns the schema for the provided field.
@@ -202,7 +183,7 @@ export class ObjectNodeStoredSchema extends TreeNodeStoredSchema {
202
183
  super();
203
184
  }
204
185
 
205
- public override encode(): ErasedTreeNodeSchemaDataFormat {
186
+ public override encode(): TreeNodeSchemaDataFormat {
206
187
  const fieldsObject: Record<string, FieldSchemaFormat> = Object.create(null);
207
188
  // Sort fields to ensure output is identical for for equivalent schema (since field order is not considered significant).
208
189
  // This makes comparing schema easier, and ensures chunk reuse for schema summaries isn't needlessly broken.
@@ -216,9 +197,9 @@ export class ObjectNodeStoredSchema extends TreeNodeStoredSchema {
216
197
  ),
217
198
  });
218
199
  }
219
- return toErasedTreeNodeSchemaDataFormat({
200
+ return {
220
201
  object: fieldsObject,
221
- });
202
+ };
222
203
  }
223
204
 
224
205
  public override getFieldSchema(field: FieldKey): TreeFieldStoredSchema {
@@ -241,10 +222,10 @@ export class MapNodeStoredSchema extends TreeNodeStoredSchema {
241
222
  super();
242
223
  }
243
224
 
244
- public override encode(): ErasedTreeNodeSchemaDataFormat {
245
- return toErasedTreeNodeSchemaDataFormat({
225
+ public override encode(): TreeNodeSchemaDataFormat {
226
+ return {
246
227
  map: encodeFieldSchema(this.mapFields),
247
- });
228
+ };
248
229
  }
249
230
 
250
231
  public override getFieldSchema(field: FieldKey): TreeFieldStoredSchema {
@@ -271,10 +252,10 @@ export class LeafNodeStoredSchema extends TreeNodeStoredSchema {
271
252
  super();
272
253
  }
273
254
 
274
- public override encode(): ErasedTreeNodeSchemaDataFormat {
275
- return toErasedTreeNodeSchemaDataFormat({
255
+ public override encode(): TreeNodeSchemaDataFormat {
256
+ return {
276
257
  leaf: encodeValueSchema(this.leafValue),
277
- });
258
+ };
278
259
  }
279
260
 
280
261
  public override getFieldSchema(field: FieldKey): TreeFieldStoredSchema {
@@ -730,25 +730,32 @@ export class AnchorSet implements AnchorLocator {
730
730
  */
731
731
  bufferedEvents: [] as BufferedEvent[],
732
732
 
733
- // 'currentDepth' and 'depthThresholdForSubtreeChanged' serve to keep track of when do we need to emit
734
- // subtreeChangedAfterBatch events.
735
- // The algorithm works as follows:
736
- // - Initialize both to 0.
737
- // - As we walk the tree from the root towards the leaves, when we enter a node increment currentDepth by 1.
738
- // - When we edit a node, set depthThresholdForSubtreeChanged = currentDepth.
739
- // Intuitively, depthThresholdForSubtreeChanged means "as you walk the tree towards the root, when you exit a
740
- // node at this depth you should emit a subtreeChangedAfterBatch event".
741
- // - When we exit a node, if d === currentDepth then emit a subtreeChangedAfterBatch and decrement d by 1.
742
- // Then decrement currentDepth unconditionally.
743
- // Note that the event will be emitted when exiting a node that was edited (depthThresholdForSubtreeChanged will
744
- // have been set to the current depth when the edit happened), it will be emitted when exiting a node that is the
745
- // parent of a node that already emitted the event (because both depthThresholdForSubtreeChanged and currentDepth
746
- // get decremented when exiting a node so they stay in sync), and if we're already emitting the event but start
747
- // walking the tree back towards the leaves in a path where no edits happen, currentDepth will be increased again
748
- // as we walk that path, depthThresholdForSubtreeChanged will not, and thus no event will be emitted when walking
749
- // back up that path, until we get back to the depth where we were already emitting the event, and will continue
750
- // emitting it on the way to the root.
733
+ /**
734
+ * 'currentDepth' and 'depthThresholdForSubtreeChanged' serve to keep track of when do we need to emit
735
+ * subtreeChangedAfterBatch events.
736
+ * The algorithm works as follows:
737
+ *
738
+ * - Initialize both to 0.
739
+ * - As we walk the tree from the root towards the leaves, when we enter a node increment currentDepth by 1.
740
+ * - When we edit a node, set depthThresholdForSubtreeChanged = currentDepth.
741
+ * Intuitively, depthThresholdForSubtreeChanged means "as you walk the tree towards the root, when you exit a
742
+ * node at this depth you should emit a subtreeChangedAfterBatch event".
743
+ * - When we exit a node, if d === currentDepth then emit a subtreeChangedAfterBatch and decrement d by 1.
744
+ * Then decrement currentDepth unconditionally.
745
+ *
746
+ * Note that the event will be emitted when exiting a node that was edited (depthThresholdForSubtreeChanged will
747
+ * have been set to the current depth when the edit happened), it will be emitted when exiting a node that is the
748
+ * parent of a node that already emitted the event (because both depthThresholdForSubtreeChanged and currentDepth
749
+ * get decremented when exiting a node so they stay in sync), and if we're already emitting the event but start
750
+ * walking the tree back towards the leaves in a path where no edits happen, currentDepth will be increased again
751
+ * as we walk that path, depthThresholdForSubtreeChanged will not, and thus no event will be emitted when walking
752
+ * back up that path, until we get back to the depth where we were already emitting the event, and will continue
753
+ * emitting it on the way to the root.
754
+ */
751
755
  currentDepth: 0,
756
+ /**
757
+ * See {@link visitor.currentDepth}.
758
+ */
752
759
  depthThresholdForSubtreeChanged: 0,
753
760
 
754
761
  free() {
@@ -909,16 +916,17 @@ export class AnchorSet implements AnchorLocator {
909
916
  },
910
917
  exitNode(index: number): void {
911
918
  assert(this.parent !== undefined, 0x3ac /* Must have parent node */);
912
- this.maybeWithNode((p) => {
913
- p.events.emit("subtreeChanged", p);
914
- if (this.depthThresholdForSubtreeChanged === this.currentDepth) {
919
+ if (this.depthThresholdForSubtreeChanged === this.currentDepth) {
920
+ this.maybeWithNode((p) => {
921
+ p.events.emit("subtreeChanged", p);
922
+
915
923
  this.bufferedEvents.push({
916
924
  node: p,
917
925
  event: "subtreeChangedAfterBatch",
918
926
  });
919
- this.depthThresholdForSubtreeChanged--;
920
- }
921
- });
927
+ });
928
+ this.depthThresholdForSubtreeChanged--;
929
+ }
922
930
  const parent = this.parent;
923
931
  this.parentField = parent.parentField;
924
932
  this.parent = parent.parent;
@@ -7,23 +7,11 @@ import type { Mutable } from "../../util/index.js";
7
7
  import type { FieldKey } from "../schema-stored/index.js";
8
8
  import type { TreeChunk } from "./chunk.js";
9
9
 
10
- import type { DetachedNodeId, FieldChanges, Mark, Root } from "./delta.js";
10
+ import type { DetachedNodeId, FieldChanges, Root } from "./delta.js";
11
11
  import { rootFieldKey } from "./types.js";
12
12
 
13
13
  export const emptyDelta: Root = {};
14
14
 
15
- export function isAttachMark(mark: Mark): boolean {
16
- return mark.attach !== undefined && mark.detach === undefined;
17
- }
18
-
19
- export function isDetachMark(mark: Mark): boolean {
20
- return mark.detach !== undefined && mark.attach === undefined;
21
- }
22
-
23
- export function isReplaceMark(mark: Mark): boolean {
24
- return mark.detach !== undefined && mark.attach !== undefined;
25
- }
26
-
27
15
  export function deltaForRootInitialization(content: TreeChunk): Root {
28
16
  if (content.topLevelLength === 0) {
29
17
  return emptyDelta;
@@ -10,13 +10,7 @@ import type { FieldKey } from "../schema-stored/index.js";
10
10
 
11
11
  import { mapCursorField, type ITreeCursorSynchronous } from "./cursor.js";
12
12
  import type * as Delta from "./delta.js";
13
- import {
14
- areDetachedNodeIdsEqual,
15
- isAttachMark,
16
- isDetachMark,
17
- isReplaceMark,
18
- offsetDetachId,
19
- } from "./deltaUtil.js";
13
+ import { areDetachedNodeIdsEqual, offsetDetachId } from "./deltaUtil.js";
20
14
  import type { DetachedFieldIndex } from "./detachedFieldIndex.js";
21
15
  import type { ForestRootId, Major, Minor } from "./detachedFieldIndexTypes.js";
22
16
  import type { NodeIndex, PlaceIndex, Range } from "./pathTree.js";
@@ -32,24 +26,11 @@ import type { RevisionTag, TreeChunk } from "../index.js";
32
26
  * 4. root destructions
33
27
  *
34
28
  * The core idea is that before content can be attached, it must first exist and be in a detached field.
35
- * The detach pass is therefore responsible for making sure that all roots that needs to be attached during the
36
- * attach pass are detached.
37
- * In practice, this means the detach pass must:
38
- * - Create all subtrees that need to be created
39
- * - Detach all moved nodes
40
- *
41
- * In addition to that, the detach pass also detaches nodes that need removing, with the exception of nodes that get
42
- * replaced. The reason for this exception is that we need to be able to communicate replaces as atomic operations.
43
- * In order to do that, we need to wait until we are sure that the content to attach is available as a detached root.
44
- * Replaces are therefore handled during the attach pass.
45
- * Note that this could theoretically lead to a situation where, in the attach pass, one replace wants to attach
46
- * a node that has yet to be detached by another replace. This does not occur in practice because we do not support
47
- * editing operations that would lead to this situation.
48
29
  *
49
30
  * While the detach pass ensures that nodes to be attached are in a detached state, it does not guarantee that they
50
- * reside in the correct detach field. That is the responsibility of the root transfers phase.
31
+ * reside in the correct detached field. That is the responsibility of the root transfers phase.
51
32
  *
52
- * The attach phase carries out attaches and replaces.
33
+ * The attach phase carries out attaches.
53
34
  *
54
35
  * After the attach phase, roots destruction is carried out.
55
36
  * This needs to happen last to allow modifications to detached roots to be applied before they are destroyed.
@@ -57,9 +38,6 @@ import type { RevisionTag, TreeChunk } from "../index.js";
57
38
  * The details of the delta visit algorithm can impact how/when events are emitted by the objects that own the visitors.
58
39
  * For example, as of 2024-03-27, the subtreeChanged event of an AnchorNode is emitted when exiting a node during a
59
40
  * delta visit, and thus the two-pass nature of the algorithm means the event fires twice for any given change.
60
- * This two-pass nature also means that the event may fire at a time where no change is visible in the tree. E.g.,
61
- * if a node is being replaced, when the event fires during the detach pass no change in the tree has happened so the
62
- * listener won't see any; then when it fires during the attach pass, the change will be visible in the event listener.
63
41
  */
64
42
 
65
43
  /**
@@ -233,7 +211,7 @@ function transferRoots(
233
211
  const oldField = detachedFieldIndex.toFieldKey(oldRootId);
234
212
  const newField = detachedFieldIndex.toFieldKey(newRootId);
235
213
  visitor.enterField(oldField);
236
- visitor.detach({ start: 0, end: 1 }, newField, newId);
214
+ visitor.detach({ start: 0, end: 1 }, newField, newId, false);
237
215
  visitor.exitField(oldField);
238
216
  detachedFieldIndex.deleteEntry(oldId);
239
217
  }
@@ -285,22 +263,15 @@ export interface DeltaVisitor {
285
263
  * @param destination - The key for a new detached field.
286
264
  * A field with this key must not already exist.
287
265
  * @param id - The ID assigned to the first detached node as a result of the detach. The other nodes in the detached range are assigned subsequent IDs.
266
+ * @param isReplaced - Whether the detached content will be replaced by a later attach.
267
+ * This is not guaranteed to be true in all cases where it could be true,
268
+ * but it is guaranteed to be true in all cases where a later attach is needed to keep the data compliant with the schema.
288
269
  */
289
- detach(source: Range, destination: FieldKey, id: Delta.DetachedNodeId): void;
290
- /**
291
- * Replaces a range of nodes in the current field by transferring them out to a new detached field
292
- * and transferring in all the nodes from an existing detached field in their place.
293
- * The number of nodes being detached must match the number of nodes being attached.
294
- * @param newContentSource - The detached field to transfer the new nodes from.
295
- * @param range - The bounds of the range of nodes to replace.
296
- * @param oldContentDestination - The key for a new detached field to transfer the old nodes to.
297
- * @param oldContentId - The ID assigned to the first replaced node as a result of the replace. The other nodes in the replaced range are assigned subsequent IDs.
298
- */
299
- replace(
300
- newContentSource: FieldKey,
301
- range: Range,
302
- oldContentDestination: FieldKey,
303
- oldContentId: Delta.DetachedNodeId,
270
+ detach(
271
+ source: Range,
272
+ destination: FieldKey,
273
+ id: Delta.DetachedNodeId,
274
+ isReplaced: boolean,
304
275
  ): void;
305
276
 
306
277
  /**
@@ -417,13 +388,8 @@ function visitNode(
417
388
 
418
389
  /**
419
390
  * Performs the following:
420
- * - Performs all root creations
421
- * - Collects all roots that may need a detach pass
422
391
  * - Collects all roots that may need an attach pass
423
- * - Collects all relocates
424
- * - Collects all destructions
425
- * - Executes detaches (bottom-up) provided they are not part of a replace
426
- * (because we want to wait until we are sure content to attach is available as a root)
392
+ * - Executes detaches (bottom-up)
427
393
  */
428
394
  function detachPass(
429
395
  fieldChanges: Delta.FieldChanges,
@@ -439,18 +405,18 @@ function detachPass(
439
405
  );
440
406
  visitNode(index, mark.fields, visitor, config);
441
407
  }
442
- if (isDetachMark(mark)) {
408
+ if (mark.detach !== undefined) {
443
409
  for (let i = 0; i < mark.count; i += 1) {
444
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
445
- const id = offsetDetachId(mark.detach!, i);
410
+ const id = offsetDetachId(mark.detach, i);
446
411
  const root = config.detachedFieldIndex.createEntry(id, config.latestRevision);
447
412
  if (mark.fields !== undefined) {
448
413
  config.attachPassRoots.set(root, mark.fields);
449
414
  }
450
415
  const field = config.detachedFieldIndex.toFieldKey(root);
451
- visitor.detach({ start: index, end: index + 1 }, field, id);
416
+ visitor.detach({ start: index, end: index + 1 }, field, id, mark.attach !== undefined);
452
417
  }
453
- } else if (!isAttachMark(mark)) {
418
+ }
419
+ if (mark.detach === undefined && mark.attach === undefined) {
454
420
  index += mark.count;
455
421
  }
456
422
  }
@@ -534,8 +500,6 @@ function collectDestroys(
534
500
  /**
535
501
  * Preforms the following:
536
502
  * - Executes attaches (top-down) applying nested changes on the attached nodes
537
- * - Executes replaces (top-down) applying nested changes on the attached nodes
538
- * - Collects detached roots (from replaces) that need an attach pass
539
503
  */
540
504
  function attachPass(
541
505
  fieldChanges: Delta.FieldChanges,
@@ -544,10 +508,9 @@ function attachPass(
544
508
  ): void {
545
509
  let index = 0;
546
510
  for (const mark of fieldChanges) {
547
- if (isAttachMark(mark) || isReplaceMark(mark)) {
511
+ if (mark.attach !== undefined) {
548
512
  for (let i = 0; i < mark.count; i += 1) {
549
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
550
- const offsetAttachId = offsetDetachId(mark.attach!, i);
513
+ const offsetAttachId = offsetDetachId(mark.attach, i);
551
514
  let sourceRoot = config.detachedFieldIndex.tryGetEntry(offsetAttachId);
552
515
  if (sourceRoot === undefined) {
553
516
  const tree = tryGetFromNestedMap(
@@ -567,28 +530,7 @@ function attachPass(
567
530
  }
568
531
  const sourceField = config.detachedFieldIndex.toFieldKey(sourceRoot);
569
532
  const offsetIndex = index + i;
570
- if (isReplaceMark(mark)) {
571
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
572
- const destinationId = offsetDetachId(mark.detach!, i);
573
- const rootDestination = config.detachedFieldIndex.createEntry(
574
- destinationId,
575
- config.latestRevision,
576
- );
577
- const destinationField = config.detachedFieldIndex.toFieldKey(rootDestination);
578
- visitor.replace(
579
- sourceField,
580
- { start: offsetIndex, end: offsetIndex + 1 },
581
- destinationField,
582
- destinationId,
583
- );
584
- // We may need to do a second pass on the detached nodes
585
- if (mark.fields !== undefined) {
586
- config.attachPassRoots.set(rootDestination, mark.fields);
587
- }
588
- } else {
589
- // This a simple attach
590
- visitor.attach(sourceField, 1, offsetIndex);
591
- }
533
+ visitor.attach(sourceField, 1, offsetIndex);
592
534
  config.detachedFieldIndex.deleteEntry(offsetAttachId);
593
535
  const fields = config.attachPassRoots.get(sourceRoot);
594
536
  if (fields !== undefined) {
@@ -596,10 +538,11 @@ function attachPass(
596
538
  visitNode(offsetIndex, fields, visitor, config);
597
539
  }
598
540
  }
599
- } else if (!isDetachMark(mark) && mark.fields !== undefined) {
541
+ }
542
+ if (mark.detach === undefined && mark.fields !== undefined) {
600
543
  visitNode(index, mark.fields, visitor, config);
601
544
  }
602
- if (!isDetachMark(mark)) {
545
+ if (mark.detach === undefined || mark.attach !== undefined) {
603
546
  index += mark.count;
604
547
  }
605
548
  }
@@ -3,8 +3,6 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert } from "@fluidframework/core-utils/internal";
7
-
8
6
  import type { ICodecOptions } from "../../codec/index.js";
9
7
  import { type IdAllocator, idAllocatorFromMaxId } from "../../util/index.js";
10
8
  import type { RevisionTag, RevisionTagCodec } from "../rebase/index.js";
@@ -51,41 +49,46 @@ export function announceDelta(
51
49
  detachedFieldIndex: DetachedFieldIndex,
52
50
  ): void {
53
51
  const visitor = deltaProcessor.acquireVisitor();
54
- visitDelta(delta, combineVisitors([visitor], [visitor]), detachedFieldIndex, latestRevision);
52
+ visitDelta(delta, combineVisitors([visitor]), detachedFieldIndex, latestRevision);
55
53
  visitor.free();
56
54
  }
57
55
 
56
+ export interface CombinedVisitor extends DeltaVisitor {
57
+ readonly type: "Combined";
58
+
59
+ readonly visitors: readonly CombinableVisitor[];
60
+ }
61
+
62
+ export type CombinableVisitor =
63
+ | (DeltaVisitor & { type?: never })
64
+ | AnnouncedVisitor
65
+ | CombinedVisitor;
66
+
58
67
  /**
68
+ * Combines multiple visitors into a single visitor.
59
69
  * @param visitors - The returned visitor invokes the corresponding events for all these visitors, in order.
60
- * @param announcedVisitors - Subset of `visitors` to also call {@link AnnouncedVisitor} methods on.
61
- * This must be a subset of `visitors`: if not the visitor will not have its path correctly set when the events are triggered.
62
- * When `visitors` are making changes to data, `announcedVisitors` can be used to get extra events before or after all the changes from all the visitors have been made.
63
- * This can, for example, enable visitors to have access to the tree in these extra events despite multiple separate visitors updating different tree related data-structures.
64
70
  * @returns a DeltaVisitor combining all `visitors`.
65
71
  */
66
- export function combineVisitors(
67
- visitors: readonly DeltaVisitor[],
68
- announcedVisitors: readonly AnnouncedVisitor[] = [],
69
- ): DeltaVisitor {
70
- {
71
- const set = new Set(visitors);
72
- for (const item of announcedVisitors) {
73
- assert(set.has(item), 0x8c8 /* AnnouncedVisitor would not get traversed */);
74
- }
75
- }
72
+ export function combineVisitors(visitors: readonly CombinableVisitor[]): CombinedVisitor {
73
+ const allVisitors = visitors.flatMap((v) => (v.type === "Combined" ? v.visitors : [v]));
74
+ const announcedVisitors = allVisitors.filter(
75
+ (v): v is AnnouncedVisitor => v.type === "Announced",
76
+ );
76
77
  return {
78
+ type: "Combined",
79
+ visitors: allVisitors,
77
80
  free: () => visitors.forEach((v) => v.free()),
78
81
  create: (...args) => {
79
- visitors.forEach((v) => v.create(...args));
82
+ allVisitors.forEach((v) => v.create(...args));
80
83
  announcedVisitors.forEach((v) => v.afterCreate(...args));
81
84
  },
82
85
  destroy: (...args) => {
83
86
  announcedVisitors.forEach((v) => v.beforeDestroy(...args));
84
- visitors.forEach((v) => v.destroy(...args));
87
+ allVisitors.forEach((v) => v.destroy(...args));
85
88
  },
86
89
  attach: (source: FieldKey, count: number, destination: PlaceIndex) => {
87
90
  announcedVisitors.forEach((v) => v.beforeAttach(source, count, destination));
88
- visitors.forEach((v) => v.attach(source, count, destination));
91
+ allVisitors.forEach((v) => v.attach(source, count, destination));
89
92
  announcedVisitors.forEach((v) =>
90
93
  v.afterAttach(source, {
91
94
  start: destination,
@@ -93,33 +96,22 @@ export function combineVisitors(
93
96
  }),
94
97
  );
95
98
  },
96
- detach: (source: Range, destination: FieldKey, id: DetachedNodeId) => {
97
- announcedVisitors.forEach((v) => v.beforeDetach(source, destination));
98
- visitors.forEach((v) => v.detach(source, destination, id));
99
- announcedVisitors.forEach((v) =>
100
- v.afterDetach(source.start, source.end - source.start, destination),
101
- );
102
- },
103
- replace: (
104
- newContent: FieldKey,
105
- oldContent: Range,
106
- oldContentDestination: FieldKey,
107
- oldContentId: DetachedNodeId,
99
+ detach: (
100
+ source: Range,
101
+ destination: FieldKey,
102
+ id: DetachedNodeId,
103
+ isReplaced: boolean,
108
104
  ) => {
105
+ announcedVisitors.forEach((v) => v.beforeDetach(source, destination, isReplaced));
106
+ allVisitors.forEach((v) => v.detach(source, destination, id, isReplaced));
109
107
  announcedVisitors.forEach((v) =>
110
- v.beforeReplace(newContent, oldContent, oldContentDestination),
111
- );
112
- visitors.forEach((v) =>
113
- v.replace(newContent, oldContent, oldContentDestination, oldContentId),
114
- );
115
- announcedVisitors.forEach((v) =>
116
- v.afterReplace(newContent, oldContent, oldContentDestination),
108
+ v.afterDetach(source.start, source.end - source.start, destination, isReplaced),
117
109
  );
118
110
  },
119
- enterNode: (...args) => visitors.forEach((v) => v.enterNode(...args)),
120
- exitNode: (...args) => visitors.forEach((v) => v.exitNode(...args)),
121
- enterField: (...args) => visitors.forEach((v) => v.enterField(...args)),
122
- exitField: (...args) => visitors.forEach((v) => v.exitField(...args)),
111
+ enterNode: (...args) => allVisitors.forEach((v) => v.enterNode(...args)),
112
+ exitNode: (...args) => allVisitors.forEach((v) => v.exitNode(...args)),
113
+ enterField: (...args) => allVisitors.forEach((v) => v.enterField(...args)),
114
+ exitField: (...args) => allVisitors.forEach((v) => v.exitField(...args)),
123
115
  };
124
116
  }
125
117
 
@@ -128,6 +120,7 @@ export function combineVisitors(
128
120
  * Must be freed after use.
129
121
  */
130
122
  export interface AnnouncedVisitor extends DeltaVisitor {
123
+ readonly type: "Announced";
131
124
  /**
132
125
  * A hook that is called after all nodes have been created.
133
126
  */
@@ -135,14 +128,13 @@ export interface AnnouncedVisitor extends DeltaVisitor {
135
128
  beforeDestroy(field: FieldKey, count: number): void;
136
129
  beforeAttach(source: FieldKey, count: number, destination: PlaceIndex): void;
137
130
  afterAttach(source: FieldKey, destination: Range): void;
138
- beforeDetach(source: Range, destination: FieldKey): void;
139
- afterDetach(source: PlaceIndex, count: number, destination: FieldKey): void;
140
- beforeReplace(
141
- newContent: FieldKey,
142
- oldContent: Range,
143
- oldContentDestination: FieldKey,
131
+ beforeDetach(source: Range, destination: FieldKey, isReplaced: boolean): void;
132
+ afterDetach(
133
+ source: PlaceIndex,
134
+ count: number,
135
+ destination: FieldKey,
136
+ isReplaced: boolean,
144
137
  ): void;
145
- afterReplace(newContentSource: FieldKey, newContent: Range, oldContent: FieldKey): void;
146
138
  }
147
139
 
148
140
  /**
@@ -154,6 +146,7 @@ export function createAnnouncedVisitor(
154
146
  ): AnnouncedVisitor {
155
147
  const noOp = (): void => {};
156
148
  return {
149
+ type: "Announced",
157
150
  free: visitorFunctions.free ?? noOp,
158
151
  create: visitorFunctions.create ?? noOp,
159
152
  afterCreate: visitorFunctions.afterCreate ?? noOp,
@@ -165,9 +158,6 @@ export function createAnnouncedVisitor(
165
158
  beforeDetach: visitorFunctions.beforeDetach ?? noOp,
166
159
  detach: visitorFunctions.detach ?? noOp,
167
160
  afterDetach: visitorFunctions.afterDetach ?? noOp,
168
- beforeReplace: visitorFunctions.beforeReplace ?? noOp,
169
- replace: visitorFunctions.replace ?? noOp,
170
- afterReplace: visitorFunctions.afterReplace ?? noOp,
171
161
  enterNode: visitorFunctions.enterNode ?? noOp,
172
162
  exitNode: visitorFunctions.exitNode ?? noOp,
173
163
  enterField: visitorFunctions.enterField ?? noOp,
@@ -183,6 +183,12 @@ export class ChunkedForest implements IEditableForest {
183
183
  * If not specified, the detached range is destroyed.
184
184
  */
185
185
  detachEdit(source: Range, destination: FieldKey | undefined): void {
186
+ // TODO: optimize this to perform in-place replace in uniform chunks when attach edits bring the chunk back to its original shape.
187
+ // This should result in 3 cases:
188
+ // 1. In-place update of uniform chunk. No allocations, no ref count changes, no new TreeChunks.
189
+ // 2. Uniform chunk is shared: copy it (and parent path as needed), and update the copy.
190
+ // 3. Fallback to detach then attach (Which will copy parents and convert to basic chunks as needed).
191
+
186
192
  this.forest.#events.emit("beforeChange");
187
193
  const parent = this.getParent();
188
194
  const sourceField = parent.mutableChunk.fields.get(parent.key) ?? [];
@@ -209,24 +215,6 @@ export class ChunkedForest implements IEditableForest {
209
215
  parent.mutableChunk.fields.delete(parent.key);
210
216
  }
211
217
  },
212
- replace(
213
- newContentSource: FieldKey,
214
- range: Range,
215
- oldContentDestination: FieldKey,
216
- oldContentId: DeltaDetachedNodeId,
217
- ): void {
218
- assert(
219
- newContentSource !== oldContentDestination,
220
- 0x7b0 /* Replace detached source field and detached destination field must be different */,
221
- );
222
- // TODO: optimize this to: perform in-place replace in uniform chunks when possible.
223
- // This should result in 3 cases:
224
- // 1. In-place update of uniform chunk. No allocations, no ref count changes, no new TreeChunks.
225
- // 2. Uniform chunk is shared: copy it (and parent path as needed), and update the copy.
226
- // 3. Fallback to detach then attach (Which will copy parents and convert to basic chunks as needed).
227
- this.detachEdit(range, oldContentDestination);
228
- this.attachEdit(newContentSource, range.end - range.start, range.start);
229
- },
230
218
  enterNode(index: number): void {
231
219
  assert(this.mutableChunk === undefined, 0x535 /* should be in field */);
232
220
  const parent = this.getParent();
@@ -289,10 +277,7 @@ export class ChunkedForest implements IEditableForest {
289
277
 
290
278
  const announcedVisitors: AnnouncedVisitor[] = [];
291
279
  this.deltaVisitors.forEach((getVisitor) => announcedVisitors.push(getVisitor()));
292
- const combinedVisitor = combineVisitors(
293
- [forestVisitor, ...announcedVisitors],
294
- announcedVisitors,
295
- );
280
+ const combinedVisitor = combineVisitors([forestVisitor, ...announcedVisitors]);
296
281
  this.activeVisitor = combinedVisitor;
297
282
  return combinedVisitor;
298
283
  }
@@ -66,7 +66,7 @@ export type Shape = ShapeGeneric<EncodedChunkShape>;
66
66
  */
67
67
  export interface KeyedFieldEncoder {
68
68
  readonly key: FieldKey;
69
- readonly shape: FieldEncoder;
69
+ readonly encoder: FieldEncoder;
70
70
  }
71
71
 
72
72
  /**