@fluidframework/tree 2.80.0 → 2.81.0-374083

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 (914) hide show
  1. package/api-report/tree.alpha.api.md +43 -7
  2. package/dist/alpha.d.ts +5 -0
  3. package/dist/core/change-family/changeFamily.d.ts +4 -1
  4. package/dist/core/change-family/changeFamily.d.ts.map +1 -1
  5. package/dist/core/change-family/changeFamily.js.map +1 -1
  6. package/dist/core/change-family/index.d.ts +1 -1
  7. package/dist/core/change-family/index.d.ts.map +1 -1
  8. package/dist/core/change-family/index.js.map +1 -1
  9. package/dist/core/index.d.ts +3 -3
  10. package/dist/core/index.d.ts.map +1 -1
  11. package/dist/core/index.js +6 -4
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/rebase/changeRebaser.d.ts +6 -1
  14. package/dist/core/rebase/changeRebaser.d.ts.map +1 -1
  15. package/dist/core/rebase/changeRebaser.js.map +1 -1
  16. package/dist/core/rebase/index.d.ts +1 -1
  17. package/dist/core/rebase/index.d.ts.map +1 -1
  18. package/dist/core/rebase/index.js +2 -1
  19. package/dist/core/rebase/index.js.map +1 -1
  20. package/dist/core/rebase/types.d.ts +46 -8
  21. package/dist/core/rebase/types.d.ts.map +1 -1
  22. package/dist/core/rebase/types.js +5 -1
  23. package/dist/core/rebase/types.js.map +1 -1
  24. package/dist/core/rebase/utils.d.ts.map +1 -1
  25. package/dist/core/rebase/utils.js +30 -8
  26. package/dist/core/rebase/utils.js.map +1 -1
  27. package/dist/core/tree/anchorSet.js +1 -0
  28. package/dist/core/tree/anchorSet.js.map +1 -1
  29. package/dist/core/tree/detachedFieldIndex.d.ts +6 -0
  30. package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
  31. package/dist/core/tree/detachedFieldIndex.js +9 -0
  32. package/dist/core/tree/detachedFieldIndex.js.map +1 -1
  33. package/dist/core/tree/index.d.ts +1 -1
  34. package/dist/core/tree/index.d.ts.map +1 -1
  35. package/dist/core/tree/index.js +4 -3
  36. package/dist/core/tree/index.js.map +1 -1
  37. package/dist/core/tree/pathTree.d.ts +11 -3
  38. package/dist/core/tree/pathTree.d.ts.map +1 -1
  39. package/dist/core/tree/pathTree.js +14 -2
  40. package/dist/core/tree/pathTree.js.map +1 -1
  41. package/dist/core/tree/visitDelta.d.ts.map +1 -1
  42. package/dist/core/tree/visitDelta.js +3 -2
  43. package/dist/core/tree/visitDelta.js.map +1 -1
  44. package/dist/core/tree/visitorUtils.d.ts.map +1 -1
  45. package/dist/core/tree/visitorUtils.js +58 -18
  46. package/dist/core/tree/visitorUtils.js.map +1 -1
  47. package/dist/feature-libraries/changeAtomIdBTree.d.ts +10 -4
  48. package/dist/feature-libraries/changeAtomIdBTree.d.ts.map +1 -1
  49. package/dist/feature-libraries/changeAtomIdBTree.js +16 -2
  50. package/dist/feature-libraries/changeAtomIdBTree.js.map +1 -1
  51. package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  52. package/dist/feature-libraries/chunked-forest/basicChunk.js +7 -0
  53. package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  54. package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
  55. package/dist/feature-libraries/chunked-forest/chunkTree.js +4 -1
  56. package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  57. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  58. package/dist/feature-libraries/chunked-forest/chunkedForest.js +3 -1
  59. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  60. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js +1 -0
  61. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  62. package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts +93 -44
  63. package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
  64. package/dist/feature-libraries/default-schema/defaultEditBuilder.js +238 -69
  65. package/dist/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  66. package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
  67. package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  68. package/dist/feature-libraries/default-schema/defaultFieldKinds.js +11 -2
  69. package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  70. package/dist/feature-libraries/default-schema/index.d.ts +2 -1
  71. package/dist/feature-libraries/default-schema/index.d.ts.map +1 -1
  72. package/dist/feature-libraries/default-schema/index.js +5 -2
  73. package/dist/feature-libraries/default-schema/index.js.map +1 -1
  74. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
  75. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
  76. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js +132 -0
  77. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
  78. package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts +7 -6
  79. package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
  80. package/dist/feature-libraries/default-schema/mappedEditBuilder.js +15 -0
  81. package/dist/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
  82. package/dist/feature-libraries/deltaUtils.d.ts +1 -0
  83. package/dist/feature-libraries/deltaUtils.d.ts.map +1 -1
  84. package/dist/feature-libraries/deltaUtils.js +6 -1
  85. package/dist/feature-libraries/deltaUtils.js.map +1 -1
  86. package/dist/feature-libraries/flex-tree/context.d.ts +9 -0
  87. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  88. package/dist/feature-libraries/flex-tree/context.js +6 -0
  89. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  90. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
  91. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  92. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  93. package/dist/feature-libraries/flex-tree/lazyField.d.ts +8 -7
  94. package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  95. package/dist/feature-libraries/flex-tree/lazyField.js +40 -9
  96. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  97. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  98. package/dist/feature-libraries/forest-summary/forestSummarizer.js +3 -1
  99. package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  100. package/dist/feature-libraries/index.d.ts +3 -3
  101. package/dist/feature-libraries/index.d.ts.map +1 -1
  102. package/dist/feature-libraries/index.js +8 -3
  103. package/dist/feature-libraries/index.js.map +1 -1
  104. package/dist/feature-libraries/mapTreeCursor.d.ts.map +1 -1
  105. package/dist/feature-libraries/mapTreeCursor.js +1 -0
  106. package/dist/feature-libraries/mapTreeCursor.js.map +1 -1
  107. package/dist/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
  108. package/dist/feature-libraries/mitigatedChangeFamily.js +2 -2
  109. package/dist/feature-libraries/mitigatedChangeFamily.js.map +1 -1
  110. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
  111. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  112. package/dist/feature-libraries/modular-schema/crossFieldQueries.js +4 -7
  113. package/dist/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  114. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +20 -51
  115. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  116. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  117. package/dist/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  118. package/dist/feature-libraries/modular-schema/genericFieldKind.js +3 -9
  119. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  120. package/dist/feature-libraries/modular-schema/index.d.ts +4 -4
  121. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  122. package/dist/feature-libraries/modular-schema/index.js +2 -2
  123. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  124. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +11 -28
  125. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
  126. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +255 -161
  127. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
  128. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
  129. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
  130. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js +393 -0
  131. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
  132. package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  133. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +8 -1
  134. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  135. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +48 -20
  136. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  137. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +1350 -476
  138. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  139. package/dist/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -1
  140. package/dist/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -1
  141. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
  142. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
  143. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js +32 -0
  144. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
  145. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +47 -11
  146. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  147. package/dist/feature-libraries/modular-schema/modularChangeTypes.js +3 -3
  148. package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  149. package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  150. package/dist/feature-libraries/object-forest/objectForest.js +3 -1
  151. package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
  152. package/dist/feature-libraries/optional-field/index.d.ts +2 -2
  153. package/dist/feature-libraries/optional-field/index.d.ts.map +1 -1
  154. package/dist/feature-libraries/optional-field/index.js +1 -2
  155. package/dist/feature-libraries/optional-field/index.js.map +1 -1
  156. package/dist/feature-libraries/optional-field/optionalField.d.ts +5 -26
  157. package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  158. package/dist/feature-libraries/optional-field/optionalField.js +221 -443
  159. package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
  160. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
  161. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
  162. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +31 -0
  163. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
  164. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
  165. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
  166. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
  167. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
  168. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
  169. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js +57 -28
  170. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
  171. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
  172. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
  173. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js +57 -0
  174. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
  175. package/dist/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
  176. package/dist/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
  177. package/dist/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
  178. package/dist/feature-libraries/sequence-field/compose.d.ts +6 -7
  179. package/dist/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  180. package/dist/feature-libraries/sequence-field/compose.js +83 -259
  181. package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
  182. package/dist/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
  183. package/dist/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
  184. package/dist/feature-libraries/sequence-field/helperTypes.js.map +1 -1
  185. package/dist/feature-libraries/sequence-field/index.d.ts +2 -3
  186. package/dist/feature-libraries/sequence-field/index.d.ts.map +1 -1
  187. package/dist/feature-libraries/sequence-field/index.js +1 -3
  188. package/dist/feature-libraries/sequence-field/index.js.map +1 -1
  189. package/dist/feature-libraries/sequence-field/invert.d.ts +3 -3
  190. package/dist/feature-libraries/sequence-field/invert.d.ts.map +1 -1
  191. package/dist/feature-libraries/sequence-field/invert.js +65 -167
  192. package/dist/feature-libraries/sequence-field/invert.js.map +1 -1
  193. package/dist/feature-libraries/sequence-field/markQueue.d.ts +2 -2
  194. package/dist/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  195. package/dist/feature-libraries/sequence-field/markQueue.js.map +1 -1
  196. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
  197. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  198. package/dist/feature-libraries/sequence-field/moveEffectTable.js +7 -90
  199. package/dist/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  200. package/dist/feature-libraries/sequence-field/rebase.d.ts +3 -3
  201. package/dist/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
  202. package/dist/feature-libraries/sequence-field/rebase.js +107 -114
  203. package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
  204. package/dist/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
  205. package/dist/feature-libraries/sequence-field/replaceRevisions.js +18 -31
  206. package/dist/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
  207. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  208. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
  209. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  210. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
  211. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
  212. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js +365 -187
  213. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
  214. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
  215. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js +20 -62
  216. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
  217. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
  218. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
  219. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
  220. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
  221. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
  222. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
  223. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js +14 -109
  224. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
  225. package/dist/feature-libraries/sequence-field/types.d.ts +30 -59
  226. package/dist/feature-libraries/sequence-field/types.d.ts.map +1 -1
  227. package/dist/feature-libraries/sequence-field/types.js.map +1 -1
  228. package/dist/feature-libraries/sequence-field/utils.d.ts +15 -24
  229. package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  230. package/dist/feature-libraries/sequence-field/utils.js +116 -305
  231. package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
  232. package/dist/index.d.ts +3 -2
  233. package/dist/index.d.ts.map +1 -1
  234. package/dist/index.js +4 -1
  235. package/dist/index.js.map +1 -1
  236. package/dist/packageVersion.d.ts +1 -1
  237. package/dist/packageVersion.d.ts.map +1 -1
  238. package/dist/packageVersion.js +1 -1
  239. package/dist/packageVersion.js.map +1 -1
  240. package/dist/shared-tree/index.d.ts +1 -1
  241. package/dist/shared-tree/index.d.ts.map +1 -1
  242. package/dist/shared-tree/index.js.map +1 -1
  243. package/dist/shared-tree/schematizeTree.d.ts +4 -4
  244. package/dist/shared-tree/schematizeTree.d.ts.map +1 -1
  245. package/dist/shared-tree/schematizeTree.js +2 -1
  246. package/dist/shared-tree/schematizeTree.js.map +1 -1
  247. package/dist/shared-tree/schematizingTreeView.d.ts +1 -5
  248. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  249. package/dist/shared-tree/schematizingTreeView.js +38 -35
  250. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  251. package/dist/shared-tree/sharedTree.d.ts +9 -3
  252. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  253. package/dist/shared-tree/sharedTree.js +11 -0
  254. package/dist/shared-tree/sharedTree.js.map +1 -1
  255. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
  256. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  257. package/dist/shared-tree/sharedTreeChangeCodecs.js +1 -0
  258. package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  259. package/dist/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  260. package/dist/shared-tree/sharedTreeChangeEnricher.js +1 -1
  261. package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  262. package/dist/shared-tree/sharedTreeChangeFamily.d.ts +5 -5
  263. package/dist/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
  264. package/dist/shared-tree/sharedTreeChangeFamily.js +10 -4
  265. package/dist/shared-tree/sharedTreeChangeFamily.js.map +1 -1
  266. package/dist/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
  267. package/dist/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
  268. package/dist/shared-tree/sharedTreeEditBuilder.js +14 -7
  269. package/dist/shared-tree/sharedTreeEditBuilder.js.map +1 -1
  270. package/dist/shared-tree/treeCheckout.d.ts +9 -10
  271. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  272. package/dist/shared-tree/treeCheckout.js +63 -8
  273. package/dist/shared-tree/treeCheckout.js.map +1 -1
  274. package/dist/shared-tree-core/branch.d.ts +3 -2
  275. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  276. package/dist/shared-tree-core/branch.js +9 -4
  277. package/dist/shared-tree-core/branch.js.map +1 -1
  278. package/dist/shared-tree-core/editManager.d.ts +2 -2
  279. package/dist/shared-tree-core/editManager.d.ts.map +1 -1
  280. package/dist/shared-tree-core/editManager.js +20 -14
  281. package/dist/shared-tree-core/editManager.js.map +1 -1
  282. package/dist/shared-tree-core/editManagerCodecs.d.ts +4 -0
  283. package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  284. package/dist/shared-tree-core/editManagerCodecs.js +10 -2
  285. package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
  286. package/dist/shared-tree-core/editManagerFormatCommons.d.ts +1 -0
  287. package/dist/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
  288. package/dist/shared-tree-core/editManagerFormatCommons.js +6 -0
  289. package/dist/shared-tree-core/editManagerFormatCommons.js.map +1 -1
  290. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
  291. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
  292. package/dist/shared-tree-core/editManagerFormatV1toV4.js +1 -0
  293. package/dist/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
  294. package/dist/shared-tree-core/index.d.ts +2 -2
  295. package/dist/shared-tree-core/index.d.ts.map +1 -1
  296. package/dist/shared-tree-core/index.js +3 -1
  297. package/dist/shared-tree-core/index.js.map +1 -1
  298. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
  299. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
  300. package/dist/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
  301. package/dist/shared-tree-core/messageCodecs.d.ts +4 -0
  302. package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
  303. package/dist/shared-tree-core/messageCodecs.js +10 -2
  304. package/dist/shared-tree-core/messageCodecs.js.map +1 -1
  305. package/dist/shared-tree-core/messageFormat.d.ts +1 -0
  306. package/dist/shared-tree-core/messageFormat.d.ts.map +1 -1
  307. package/dist/shared-tree-core/messageFormat.js +6 -0
  308. package/dist/shared-tree-core/messageFormat.js.map +1 -1
  309. package/dist/shared-tree-core/messageFormatV1ToV4.d.ts +2 -2
  310. package/dist/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
  311. package/dist/shared-tree-core/messageFormatV1ToV4.js +1 -0
  312. package/dist/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
  313. package/dist/shared-tree-core/sharedTreeCore.d.ts +1 -0
  314. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  315. package/dist/shared-tree-core/sharedTreeCore.js +1 -1
  316. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  317. package/dist/simple-tree/api/index.d.ts +1 -1
  318. package/dist/simple-tree/api/index.d.ts.map +1 -1
  319. package/dist/simple-tree/api/index.js +2 -1
  320. package/dist/simple-tree/api/index.js.map +1 -1
  321. package/dist/simple-tree/api/schemaFactoryAlpha.js +1 -1
  322. package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  323. package/dist/simple-tree/api/schemaFactoryBeta.js +1 -1
  324. package/dist/simple-tree/api/schemaFactoryBeta.js.map +1 -1
  325. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +4 -4
  326. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  327. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
  328. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
  329. package/dist/simple-tree/api/snapshotCompatibilityChecker.js +297 -1
  330. package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  331. package/dist/simple-tree/api/tree.d.ts +3 -1
  332. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  333. package/dist/simple-tree/api/tree.js.map +1 -1
  334. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  335. package/dist/simple-tree/core/treeNodeKernel.js +6 -2
  336. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  337. package/dist/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
  338. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  339. package/dist/simple-tree/core/unhydratedFlexTree.js +59 -8
  340. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  341. package/dist/simple-tree/fieldSchema.d.ts +4 -4
  342. package/dist/simple-tree/fieldSchema.d.ts.map +1 -1
  343. package/dist/simple-tree/fieldSchema.js.map +1 -1
  344. package/dist/simple-tree/index.d.ts +3 -3
  345. package/dist/simple-tree/index.d.ts.map +1 -1
  346. package/dist/simple-tree/index.js +4 -3
  347. package/dist/simple-tree/index.js.map +1 -1
  348. package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  349. package/dist/simple-tree/node-kinds/array/arrayNode.js +7 -5
  350. package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  351. package/dist/simple-tree/node-kinds/common.d.ts.map +1 -1
  352. package/dist/simple-tree/node-kinds/common.js +1 -1
  353. package/dist/simple-tree/node-kinds/common.js.map +1 -1
  354. package/dist/simple-tree/node-kinds/map/mapNode.js +2 -2
  355. package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  356. package/dist/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  357. package/dist/simple-tree/node-kinds/object/objectNode.js +19 -19
  358. package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  359. package/dist/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
  360. package/dist/simple-tree/node-kinds/record/recordNode.js +4 -2
  361. package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  362. package/dist/simple-tree/prepareForInsertion.d.ts +54 -47
  363. package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
  364. package/dist/simple-tree/prepareForInsertion.js +184 -126
  365. package/dist/simple-tree/prepareForInsertion.js.map +1 -1
  366. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
  367. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
  368. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +31 -13
  369. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  370. package/dist/text/index.d.ts +6 -0
  371. package/dist/text/index.d.ts.map +1 -0
  372. package/dist/text/index.js +10 -0
  373. package/dist/text/index.js.map +1 -0
  374. package/dist/text/textDomain.d.ts +138 -0
  375. package/dist/text/textDomain.d.ts.map +1 -0
  376. package/dist/text/textDomain.js +121 -0
  377. package/dist/text/textDomain.js.map +1 -0
  378. package/dist/treeFactory.d.ts.map +1 -1
  379. package/dist/treeFactory.js +12 -2
  380. package/dist/treeFactory.js.map +1 -1
  381. package/dist/util/bTreeUtils.d.ts +12 -4
  382. package/dist/util/bTreeUtils.d.ts.map +1 -1
  383. package/dist/util/bTreeUtils.js +14 -18
  384. package/dist/util/bTreeUtils.js.map +1 -1
  385. package/dist/util/index.d.ts +1 -1
  386. package/dist/util/index.d.ts.map +1 -1
  387. package/dist/util/index.js +2 -1
  388. package/dist/util/index.js.map +1 -1
  389. package/dist/util/nestedMap.js +12 -12
  390. package/dist/util/nestedMap.js.map +1 -1
  391. package/dist/util/rangeMap.d.ts +24 -12
  392. package/dist/util/rangeMap.d.ts.map +1 -1
  393. package/dist/util/rangeMap.js +46 -6
  394. package/dist/util/rangeMap.js.map +1 -1
  395. package/docs/user-facing/merge-semantics.md +3 -2
  396. package/eslint.config.mts +4 -32
  397. package/lib/alpha.d.ts +5 -0
  398. package/lib/core/change-family/changeFamily.d.ts +4 -1
  399. package/lib/core/change-family/changeFamily.d.ts.map +1 -1
  400. package/lib/core/change-family/changeFamily.js.map +1 -1
  401. package/lib/core/change-family/index.d.ts +1 -1
  402. package/lib/core/change-family/index.d.ts.map +1 -1
  403. package/lib/core/change-family/index.js.map +1 -1
  404. package/lib/core/index.d.ts +3 -3
  405. package/lib/core/index.d.ts.map +1 -1
  406. package/lib/core/index.js +2 -2
  407. package/lib/core/index.js.map +1 -1
  408. package/lib/core/rebase/changeRebaser.d.ts +6 -1
  409. package/lib/core/rebase/changeRebaser.d.ts.map +1 -1
  410. package/lib/core/rebase/changeRebaser.js.map +1 -1
  411. package/lib/core/rebase/index.d.ts +1 -1
  412. package/lib/core/rebase/index.d.ts.map +1 -1
  413. package/lib/core/rebase/index.js +1 -1
  414. package/lib/core/rebase/index.js.map +1 -1
  415. package/lib/core/rebase/types.d.ts +46 -8
  416. package/lib/core/rebase/types.d.ts.map +1 -1
  417. package/lib/core/rebase/types.js +3 -0
  418. package/lib/core/rebase/types.js.map +1 -1
  419. package/lib/core/rebase/utils.d.ts.map +1 -1
  420. package/lib/core/rebase/utils.js +30 -8
  421. package/lib/core/rebase/utils.js.map +1 -1
  422. package/lib/core/tree/anchorSet.js +1 -0
  423. package/lib/core/tree/anchorSet.js.map +1 -1
  424. package/lib/core/tree/detachedFieldIndex.d.ts +6 -0
  425. package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
  426. package/lib/core/tree/detachedFieldIndex.js +10 -1
  427. package/lib/core/tree/detachedFieldIndex.js.map +1 -1
  428. package/lib/core/tree/index.d.ts +1 -1
  429. package/lib/core/tree/index.d.ts.map +1 -1
  430. package/lib/core/tree/index.js +1 -1
  431. package/lib/core/tree/index.js.map +1 -1
  432. package/lib/core/tree/pathTree.d.ts +11 -3
  433. package/lib/core/tree/pathTree.d.ts.map +1 -1
  434. package/lib/core/tree/pathTree.js +12 -1
  435. package/lib/core/tree/pathTree.js.map +1 -1
  436. package/lib/core/tree/visitDelta.d.ts.map +1 -1
  437. package/lib/core/tree/visitDelta.js +3 -2
  438. package/lib/core/tree/visitDelta.js.map +1 -1
  439. package/lib/core/tree/visitorUtils.d.ts.map +1 -1
  440. package/lib/core/tree/visitorUtils.js +58 -18
  441. package/lib/core/tree/visitorUtils.js.map +1 -1
  442. package/lib/feature-libraries/changeAtomIdBTree.d.ts +10 -4
  443. package/lib/feature-libraries/changeAtomIdBTree.d.ts.map +1 -1
  444. package/lib/feature-libraries/changeAtomIdBTree.js +14 -1
  445. package/lib/feature-libraries/changeAtomIdBTree.js.map +1 -1
  446. package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  447. package/lib/feature-libraries/chunked-forest/basicChunk.js +8 -1
  448. package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  449. package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
  450. package/lib/feature-libraries/chunked-forest/chunkTree.js +4 -1
  451. package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  452. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  453. package/lib/feature-libraries/chunked-forest/chunkedForest.js +3 -1
  454. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  455. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js +1 -0
  456. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  457. package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts +93 -44
  458. package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
  459. package/lib/feature-libraries/default-schema/defaultEditBuilder.js +236 -70
  460. package/lib/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  461. package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
  462. package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  463. package/lib/feature-libraries/default-schema/defaultFieldKinds.js +11 -2
  464. package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  465. package/lib/feature-libraries/default-schema/index.d.ts +2 -1
  466. package/lib/feature-libraries/default-schema/index.d.ts.map +1 -1
  467. package/lib/feature-libraries/default-schema/index.js +2 -1
  468. package/lib/feature-libraries/default-schema/index.js.map +1 -1
  469. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
  470. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
  471. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js +128 -0
  472. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
  473. package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts +7 -6
  474. package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
  475. package/lib/feature-libraries/default-schema/mappedEditBuilder.js +15 -0
  476. package/lib/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
  477. package/lib/feature-libraries/deltaUtils.d.ts +1 -0
  478. package/lib/feature-libraries/deltaUtils.d.ts.map +1 -1
  479. package/lib/feature-libraries/deltaUtils.js +5 -1
  480. package/lib/feature-libraries/deltaUtils.js.map +1 -1
  481. package/lib/feature-libraries/flex-tree/context.d.ts +9 -0
  482. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  483. package/lib/feature-libraries/flex-tree/context.js +6 -0
  484. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  485. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
  486. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  487. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  488. package/lib/feature-libraries/flex-tree/lazyField.d.ts +8 -7
  489. package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  490. package/lib/feature-libraries/flex-tree/lazyField.js +41 -10
  491. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  492. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  493. package/lib/feature-libraries/forest-summary/forestSummarizer.js +3 -1
  494. package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  495. package/lib/feature-libraries/index.d.ts +3 -3
  496. package/lib/feature-libraries/index.d.ts.map +1 -1
  497. package/lib/feature-libraries/index.js +2 -2
  498. package/lib/feature-libraries/index.js.map +1 -1
  499. package/lib/feature-libraries/mapTreeCursor.d.ts.map +1 -1
  500. package/lib/feature-libraries/mapTreeCursor.js +2 -1
  501. package/lib/feature-libraries/mapTreeCursor.js.map +1 -1
  502. package/lib/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
  503. package/lib/feature-libraries/mitigatedChangeFamily.js +2 -2
  504. package/lib/feature-libraries/mitigatedChangeFamily.js.map +1 -1
  505. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
  506. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  507. package/lib/feature-libraries/modular-schema/crossFieldQueries.js +3 -5
  508. package/lib/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  509. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +20 -51
  510. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  511. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  512. package/lib/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  513. package/lib/feature-libraries/modular-schema/genericFieldKind.js +3 -9
  514. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  515. package/lib/feature-libraries/modular-schema/index.d.ts +4 -4
  516. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  517. package/lib/feature-libraries/modular-schema/index.js +2 -2
  518. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  519. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +11 -28
  520. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
  521. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +247 -143
  522. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
  523. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
  524. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
  525. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js +389 -0
  526. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
  527. package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  528. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +8 -1
  529. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  530. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +48 -20
  531. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  532. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1344 -479
  533. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  534. package/lib/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -1
  535. package/lib/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -1
  536. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
  537. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
  538. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js +29 -0
  539. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
  540. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +47 -11
  541. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  542. package/lib/feature-libraries/modular-schema/modularChangeTypes.js +1 -1
  543. package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  544. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  545. package/lib/feature-libraries/object-forest/objectForest.js +3 -1
  546. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  547. package/lib/feature-libraries/optional-field/index.d.ts +2 -2
  548. package/lib/feature-libraries/optional-field/index.d.ts.map +1 -1
  549. package/lib/feature-libraries/optional-field/index.js +1 -1
  550. package/lib/feature-libraries/optional-field/index.js.map +1 -1
  551. package/lib/feature-libraries/optional-field/optionalField.d.ts +5 -26
  552. package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  553. package/lib/feature-libraries/optional-field/optionalField.js +221 -441
  554. package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
  555. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
  556. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
  557. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +27 -0
  558. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
  559. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
  560. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
  561. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
  562. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
  563. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
  564. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js +55 -26
  565. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
  566. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
  567. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
  568. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js +53 -0
  569. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
  570. package/lib/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
  571. package/lib/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
  572. package/lib/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
  573. package/lib/feature-libraries/sequence-field/compose.d.ts +6 -7
  574. package/lib/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  575. package/lib/feature-libraries/sequence-field/compose.js +85 -261
  576. package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
  577. package/lib/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
  578. package/lib/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
  579. package/lib/feature-libraries/sequence-field/helperTypes.js.map +1 -1
  580. package/lib/feature-libraries/sequence-field/index.d.ts +2 -3
  581. package/lib/feature-libraries/sequence-field/index.d.ts.map +1 -1
  582. package/lib/feature-libraries/sequence-field/index.js +0 -1
  583. package/lib/feature-libraries/sequence-field/index.js.map +1 -1
  584. package/lib/feature-libraries/sequence-field/invert.d.ts +3 -3
  585. package/lib/feature-libraries/sequence-field/invert.d.ts.map +1 -1
  586. package/lib/feature-libraries/sequence-field/invert.js +67 -169
  587. package/lib/feature-libraries/sequence-field/invert.js.map +1 -1
  588. package/lib/feature-libraries/sequence-field/markQueue.d.ts +2 -2
  589. package/lib/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  590. package/lib/feature-libraries/sequence-field/markQueue.js.map +1 -1
  591. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
  592. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  593. package/lib/feature-libraries/sequence-field/moveEffectTable.js +6 -84
  594. package/lib/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  595. package/lib/feature-libraries/sequence-field/rebase.d.ts +3 -3
  596. package/lib/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
  597. package/lib/feature-libraries/sequence-field/rebase.js +109 -116
  598. package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
  599. package/lib/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
  600. package/lib/feature-libraries/sequence-field/replaceRevisions.js +18 -31
  601. package/lib/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
  602. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  603. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
  604. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  605. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
  606. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
  607. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js +356 -182
  608. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
  609. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
  610. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js +21 -63
  611. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
  612. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
  613. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
  614. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
  615. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
  616. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
  617. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
  618. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js +14 -109
  619. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
  620. package/lib/feature-libraries/sequence-field/types.d.ts +30 -59
  621. package/lib/feature-libraries/sequence-field/types.d.ts.map +1 -1
  622. package/lib/feature-libraries/sequence-field/types.js.map +1 -1
  623. package/lib/feature-libraries/sequence-field/utils.d.ts +15 -24
  624. package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  625. package/lib/feature-libraries/sequence-field/utils.js +112 -298
  626. package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
  627. package/lib/index.d.ts +3 -2
  628. package/lib/index.d.ts.map +1 -1
  629. package/lib/index.js +2 -1
  630. package/lib/index.js.map +1 -1
  631. package/lib/packageVersion.d.ts +1 -1
  632. package/lib/packageVersion.d.ts.map +1 -1
  633. package/lib/packageVersion.js +1 -1
  634. package/lib/packageVersion.js.map +1 -1
  635. package/lib/shared-tree/index.d.ts +1 -1
  636. package/lib/shared-tree/index.d.ts.map +1 -1
  637. package/lib/shared-tree/index.js.map +1 -1
  638. package/lib/shared-tree/schematizeTree.d.ts +4 -4
  639. package/lib/shared-tree/schematizeTree.d.ts.map +1 -1
  640. package/lib/shared-tree/schematizeTree.js +3 -2
  641. package/lib/shared-tree/schematizeTree.js.map +1 -1
  642. package/lib/shared-tree/schematizingTreeView.d.ts +1 -5
  643. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  644. package/lib/shared-tree/schematizingTreeView.js +41 -38
  645. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  646. package/lib/shared-tree/sharedTree.d.ts +9 -3
  647. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  648. package/lib/shared-tree/sharedTree.js +11 -0
  649. package/lib/shared-tree/sharedTree.js.map +1 -1
  650. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
  651. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  652. package/lib/shared-tree/sharedTreeChangeCodecs.js +1 -0
  653. package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  654. package/lib/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  655. package/lib/shared-tree/sharedTreeChangeEnricher.js +2 -2
  656. package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  657. package/lib/shared-tree/sharedTreeChangeFamily.d.ts +5 -5
  658. package/lib/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
  659. package/lib/shared-tree/sharedTreeChangeFamily.js +11 -5
  660. package/lib/shared-tree/sharedTreeChangeFamily.js.map +1 -1
  661. package/lib/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
  662. package/lib/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
  663. package/lib/shared-tree/sharedTreeEditBuilder.js +12 -6
  664. package/lib/shared-tree/sharedTreeEditBuilder.js.map +1 -1
  665. package/lib/shared-tree/treeCheckout.d.ts +9 -10
  666. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  667. package/lib/shared-tree/treeCheckout.js +66 -11
  668. package/lib/shared-tree/treeCheckout.js.map +1 -1
  669. package/lib/shared-tree-core/branch.d.ts +3 -2
  670. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  671. package/lib/shared-tree-core/branch.js +9 -4
  672. package/lib/shared-tree-core/branch.js.map +1 -1
  673. package/lib/shared-tree-core/editManager.d.ts +2 -2
  674. package/lib/shared-tree-core/editManager.d.ts.map +1 -1
  675. package/lib/shared-tree-core/editManager.js +20 -14
  676. package/lib/shared-tree-core/editManager.js.map +1 -1
  677. package/lib/shared-tree-core/editManagerCodecs.d.ts +4 -0
  678. package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  679. package/lib/shared-tree-core/editManagerCodecs.js +8 -1
  680. package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
  681. package/lib/shared-tree-core/editManagerFormatCommons.d.ts +1 -0
  682. package/lib/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
  683. package/lib/shared-tree-core/editManagerFormatCommons.js +6 -0
  684. package/lib/shared-tree-core/editManagerFormatCommons.js.map +1 -1
  685. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
  686. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
  687. package/lib/shared-tree-core/editManagerFormatV1toV4.js +1 -0
  688. package/lib/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
  689. package/lib/shared-tree-core/index.d.ts +2 -2
  690. package/lib/shared-tree-core/index.d.ts.map +1 -1
  691. package/lib/shared-tree-core/index.js +2 -2
  692. package/lib/shared-tree-core/index.js.map +1 -1
  693. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
  694. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
  695. package/lib/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
  696. package/lib/shared-tree-core/messageCodecs.d.ts +4 -0
  697. package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
  698. package/lib/shared-tree-core/messageCodecs.js +8 -1
  699. package/lib/shared-tree-core/messageCodecs.js.map +1 -1
  700. package/lib/shared-tree-core/messageFormat.d.ts +1 -0
  701. package/lib/shared-tree-core/messageFormat.d.ts.map +1 -1
  702. package/lib/shared-tree-core/messageFormat.js +6 -0
  703. package/lib/shared-tree-core/messageFormat.js.map +1 -1
  704. package/lib/shared-tree-core/messageFormatV1ToV4.d.ts +2 -2
  705. package/lib/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
  706. package/lib/shared-tree-core/messageFormatV1ToV4.js +1 -0
  707. package/lib/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
  708. package/lib/shared-tree-core/sharedTreeCore.d.ts +1 -0
  709. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  710. package/lib/shared-tree-core/sharedTreeCore.js +1 -1
  711. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  712. package/lib/simple-tree/api/index.d.ts +1 -1
  713. package/lib/simple-tree/api/index.d.ts.map +1 -1
  714. package/lib/simple-tree/api/index.js +1 -1
  715. package/lib/simple-tree/api/index.js.map +1 -1
  716. package/lib/simple-tree/api/schemaFactoryAlpha.js +1 -1
  717. package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  718. package/lib/simple-tree/api/schemaFactoryBeta.js +1 -1
  719. package/lib/simple-tree/api/schemaFactoryBeta.js.map +1 -1
  720. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +4 -4
  721. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  722. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
  723. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
  724. package/lib/simple-tree/api/snapshotCompatibilityChecker.js +270 -0
  725. package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  726. package/lib/simple-tree/api/tree.d.ts +3 -1
  727. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  728. package/lib/simple-tree/api/tree.js.map +1 -1
  729. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  730. package/lib/simple-tree/core/treeNodeKernel.js +6 -2
  731. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  732. package/lib/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
  733. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  734. package/lib/simple-tree/core/unhydratedFlexTree.js +58 -8
  735. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  736. package/lib/simple-tree/fieldSchema.d.ts +4 -4
  737. package/lib/simple-tree/fieldSchema.d.ts.map +1 -1
  738. package/lib/simple-tree/fieldSchema.js.map +1 -1
  739. package/lib/simple-tree/index.d.ts +3 -3
  740. package/lib/simple-tree/index.d.ts.map +1 -1
  741. package/lib/simple-tree/index.js +2 -2
  742. package/lib/simple-tree/index.js.map +1 -1
  743. package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  744. package/lib/simple-tree/node-kinds/array/arrayNode.js +8 -6
  745. package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  746. package/lib/simple-tree/node-kinds/common.d.ts.map +1 -1
  747. package/lib/simple-tree/node-kinds/common.js +2 -2
  748. package/lib/simple-tree/node-kinds/common.js.map +1 -1
  749. package/lib/simple-tree/node-kinds/map/mapNode.js +2 -2
  750. package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  751. package/lib/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  752. package/lib/simple-tree/node-kinds/object/objectNode.js +20 -20
  753. package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  754. package/lib/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
  755. package/lib/simple-tree/node-kinds/record/recordNode.js +4 -2
  756. package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  757. package/lib/simple-tree/prepareForInsertion.d.ts +54 -47
  758. package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
  759. package/lib/simple-tree/prepareForInsertion.js +184 -125
  760. package/lib/simple-tree/prepareForInsertion.js.map +1 -1
  761. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
  762. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
  763. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +28 -11
  764. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  765. package/lib/text/index.d.ts +6 -0
  766. package/lib/text/index.d.ts.map +1 -0
  767. package/lib/text/index.js +6 -0
  768. package/lib/text/index.js.map +1 -0
  769. package/lib/text/textDomain.d.ts +138 -0
  770. package/lib/text/textDomain.d.ts.map +1 -0
  771. package/lib/text/textDomain.js +118 -0
  772. package/lib/text/textDomain.js.map +1 -0
  773. package/lib/treeFactory.d.ts.map +1 -1
  774. package/lib/treeFactory.js +13 -3
  775. package/lib/treeFactory.js.map +1 -1
  776. package/lib/util/bTreeUtils.d.ts +12 -4
  777. package/lib/util/bTreeUtils.d.ts.map +1 -1
  778. package/lib/util/bTreeUtils.js +15 -19
  779. package/lib/util/bTreeUtils.js.map +1 -1
  780. package/lib/util/index.d.ts +1 -1
  781. package/lib/util/index.d.ts.map +1 -1
  782. package/lib/util/index.js +1 -1
  783. package/lib/util/index.js.map +1 -1
  784. package/lib/util/nestedMap.js +12 -12
  785. package/lib/util/nestedMap.js.map +1 -1
  786. package/lib/util/rangeMap.d.ts +24 -12
  787. package/lib/util/rangeMap.d.ts.map +1 -1
  788. package/lib/util/rangeMap.js +44 -5
  789. package/lib/util/rangeMap.js.map +1 -1
  790. package/package.json +25 -25
  791. package/src/core/change-family/changeFamily.ts +5 -0
  792. package/src/core/change-family/index.ts +1 -0
  793. package/src/core/index.ts +6 -1
  794. package/src/core/rebase/changeRebaser.ts +6 -1
  795. package/src/core/rebase/index.ts +3 -0
  796. package/src/core/rebase/types.ts +65 -21
  797. package/src/core/rebase/utils.ts +39 -11
  798. package/src/core/tree/anchorSet.ts +1 -0
  799. package/src/core/tree/detachedFieldIndex.ts +17 -1
  800. package/src/core/tree/index.ts +2 -1
  801. package/src/core/tree/pathTree.ts +16 -4
  802. package/src/core/tree/visitDelta.ts +6 -2
  803. package/src/core/tree/visitorUtils.ts +55 -19
  804. package/src/feature-libraries/changeAtomIdBTree.ts +37 -5
  805. package/src/feature-libraries/chunked-forest/basicChunk.ts +7 -1
  806. package/src/feature-libraries/chunked-forest/chunkTree.ts +6 -1
  807. package/src/feature-libraries/chunked-forest/chunkedForest.ts +3 -1
  808. package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +1 -0
  809. package/src/feature-libraries/default-schema/defaultEditBuilder.ts +399 -127
  810. package/src/feature-libraries/default-schema/defaultFieldKinds.ts +13 -5
  811. package/src/feature-libraries/default-schema/index.ts +17 -5
  812. package/src/feature-libraries/default-schema/locationBasedEditBuilder.ts +188 -0
  813. package/src/feature-libraries/default-schema/mappedEditBuilder.ts +35 -9
  814. package/src/feature-libraries/deltaUtils.ts +6 -1
  815. package/src/feature-libraries/flex-tree/context.ts +17 -0
  816. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +7 -8
  817. package/src/feature-libraries/flex-tree/lazyField.ts +68 -25
  818. package/src/feature-libraries/forest-summary/forestSummarizer.ts +3 -1
  819. package/src/feature-libraries/index.ts +22 -8
  820. package/src/feature-libraries/mapTreeCursor.ts +2 -1
  821. package/src/feature-libraries/mitigatedChangeFamily.ts +3 -1
  822. package/src/feature-libraries/modular-schema/crossFieldQueries.ts +142 -44
  823. package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +32 -58
  824. package/src/feature-libraries/modular-schema/genericFieldKind.ts +6 -18
  825. package/src/feature-libraries/modular-schema/index.ts +16 -15
  826. package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +560 -351
  827. package/src/feature-libraries/modular-schema/modularChangeCodecV3.ts +760 -0
  828. package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +14 -1
  829. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +2620 -782
  830. package/src/feature-libraries/modular-schema/modularChangeFormatV1.ts +1 -0
  831. package/src/feature-libraries/modular-schema/modularChangeFormatV3.ts +62 -0
  832. package/src/feature-libraries/modular-schema/modularChangeTypes.ts +58 -11
  833. package/src/feature-libraries/object-forest/objectForest.ts +3 -1
  834. package/src/feature-libraries/optional-field/index.ts +1 -3
  835. package/src/feature-libraries/optional-field/optionalField.ts +318 -562
  836. package/src/feature-libraries/optional-field/optionalFieldChangeFormatV3.ts +45 -0
  837. package/src/feature-libraries/optional-field/optionalFieldChangeTypes.ts +24 -38
  838. package/src/feature-libraries/optional-field/optionalFieldCodecV2.ts +89 -35
  839. package/src/feature-libraries/optional-field/optionalFieldCodecV3.ts +94 -0
  840. package/src/feature-libraries/optional-field/optionalFieldCodecs.ts +5 -1
  841. package/src/feature-libraries/sequence-field/compose.ts +139 -522
  842. package/src/feature-libraries/sequence-field/helperTypes.ts +34 -19
  843. package/src/feature-libraries/sequence-field/index.ts +0 -9
  844. package/src/feature-libraries/sequence-field/invert.ts +103 -228
  845. package/src/feature-libraries/sequence-field/markQueue.ts +2 -2
  846. package/src/feature-libraries/sequence-field/moveEffectTable.ts +8 -195
  847. package/src/feature-libraries/sequence-field/rebase.ts +169 -205
  848. package/src/feature-libraries/sequence-field/replaceRevisions.ts +25 -47
  849. package/src/feature-libraries/sequence-field/sequenceFieldChangeHandler.ts +0 -2
  850. package/src/feature-libraries/sequence-field/sequenceFieldCodecV2.ts +648 -228
  851. package/src/feature-libraries/sequence-field/sequenceFieldCodecV3.ts +56 -70
  852. package/src/feature-libraries/sequence-field/sequenceFieldEditor.ts +25 -27
  853. package/src/feature-libraries/sequence-field/sequenceFieldToDelta.ts +19 -129
  854. package/src/feature-libraries/sequence-field/types.ts +34 -64
  855. package/src/feature-libraries/sequence-field/utils.ts +139 -353
  856. package/src/index.ts +7 -0
  857. package/src/packageVersion.ts +1 -1
  858. package/src/shared-tree/index.ts +3 -2
  859. package/src/shared-tree/schematizeTree.ts +21 -8
  860. package/src/shared-tree/schematizingTreeView.ts +56 -70
  861. package/src/shared-tree/sharedTree.ts +20 -3
  862. package/src/shared-tree/sharedTreeChangeCodecs.ts +5 -1
  863. package/src/shared-tree/sharedTreeChangeEnricher.ts +2 -0
  864. package/src/shared-tree/sharedTreeChangeFamily.ts +15 -5
  865. package/src/shared-tree/sharedTreeEditBuilder.ts +39 -8
  866. package/src/shared-tree/treeCheckout.ts +95 -24
  867. package/src/shared-tree-core/branch.ts +13 -3
  868. package/src/shared-tree-core/editManager.ts +42 -28
  869. package/src/shared-tree-core/editManagerCodecs.ts +11 -1
  870. package/src/shared-tree-core/editManagerFormatCommons.ts +6 -0
  871. package/src/shared-tree-core/editManagerFormatV1toV4.ts +3 -1
  872. package/src/shared-tree-core/index.ts +2 -0
  873. package/src/shared-tree-core/messageCodecV1ToV4.ts +2 -1
  874. package/src/shared-tree-core/messageCodecs.ts +11 -1
  875. package/src/shared-tree-core/messageFormat.ts +6 -0
  876. package/src/shared-tree-core/messageFormatV1ToV4.ts +3 -1
  877. package/src/shared-tree-core/sharedTreeCore.ts +4 -1
  878. package/src/simple-tree/api/index.ts +3 -0
  879. package/src/simple-tree/api/schemaFactoryAlpha.ts +1 -1
  880. package/src/simple-tree/api/schemaFactoryBeta.ts +1 -1
  881. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +4 -4
  882. package/src/simple-tree/api/snapshotCompatibilityChecker.ts +501 -0
  883. package/src/simple-tree/api/tree.ts +3 -1
  884. package/src/simple-tree/core/treeNodeKernel.ts +6 -2
  885. package/src/simple-tree/core/unhydratedFlexTree.ts +87 -36
  886. package/src/simple-tree/fieldSchema.ts +6 -4
  887. package/src/simple-tree/index.ts +5 -1
  888. package/src/simple-tree/node-kinds/array/arrayNode.ts +9 -7
  889. package/src/simple-tree/node-kinds/common.ts +2 -5
  890. package/src/simple-tree/node-kinds/map/mapNode.ts +4 -4
  891. package/src/simple-tree/node-kinds/object/objectNode.ts +26 -26
  892. package/src/simple-tree/node-kinds/record/recordNode.ts +10 -9
  893. package/src/simple-tree/prepareForInsertion.ts +343 -201
  894. package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +43 -15
  895. package/src/text/README.md +8 -0
  896. package/src/text/index.ts +6 -0
  897. package/src/text/textDomain.ts +199 -0
  898. package/src/treeFactory.ts +14 -4
  899. package/src/util/bTreeUtils.ts +33 -22
  900. package/src/util/index.ts +3 -0
  901. package/src/util/nestedMap.ts +12 -12
  902. package/src/util/rangeMap.ts +72 -18
  903. package/.eslintrc.cjs +0 -125
  904. package/assertTagging.config.mjs +0 -14
  905. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
  906. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
  907. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -50
  908. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
  909. package/docs/main/sequence-field/move-composition.md +0 -46
  910. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
  911. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
  912. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -46
  913. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
  914. package/src/feature-libraries/sequence-field/relevantRemovedRoots.ts +0 -57
@@ -6,14 +6,14 @@ import { assert, fail } from "@fluidframework/core-utils/internal";
6
6
  import { BTree } from "@tylerbu/sorted-btree-es6";
7
7
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
8
  import { FluidClientVersion, } from "../../codec/index.js";
9
- import { EditBuilder, makeDetachedNodeId, revisionMetadataSourceFromInfo, areEqualChangeAtomIds, areEqualChangeAtomIdOpts, tagChange, makeAnonChange, newChangeAtomIdRangeMap, mapTaggedChange, } from "../../core/index.js";
9
+ import { EditBuilder, makeDetachedNodeId, revisionMetadataSourceFromInfo, areEqualChangeAtomIds, areEqualChangeAtomIdOpts, tagChange, makeAnonChange, mapTaggedChange, newChangeAtomIdRangeMap, newChangeAtomIdTransform, offsetChangeAtomId, isDetachedUpPathRoot, subtractChangeAtomIds, makeChangeAtomId, } from "../../core/index.js";
10
10
  import { brand, idAllocatorFromMaxId, idAllocatorFromState, getOrCreate, newTupleBTree, mergeTupleBTrees, RangeMap, balancedReduce, } from "../../util/index.js";
11
- import { CrossFieldTarget, getFirstFromCrossFieldMap, setInCrossFieldMap, } from "./crossFieldQueries.js";
11
+ import { CrossFieldTarget, setInCrossFieldMap, } from "./crossFieldQueries.js";
12
12
  import { NodeAttachState, } from "./fieldChangeHandler.js";
13
13
  import { convertGenericChange, genericFieldKind } from "./genericFieldKind.js";
14
- import { newCrossFieldKeyTable, } from "./modularChangeTypes.js";
15
- import { getFromChangeAtomIdMap, setInChangeAtomIdMap, } from "../changeAtomIdBTree.js";
14
+ import { newCrossFieldRangeTable, } from "./modularChangeTypes.js";
16
15
  import { lt } from "semver-ts";
16
+ import { getFromChangeAtomIdMap, rangeQueryChangeAtomIdMap, setInChangeAtomIdMap, } from "../changeAtomIdBTree.js";
17
17
  /**
18
18
  * Implementation of ChangeFamily which delegates work in a given field to the appropriate FieldKind
19
19
  * as determined by the schema.
@@ -69,21 +69,22 @@ export class ModularChangeFamily {
69
69
  return convertedChange;
70
70
  }
71
71
  compose(changes) {
72
- const { revInfos, maxId } = getRevInfoFromTaggedChanges(changes);
72
+ const { maxId } = getRevInfoFromTaggedChanges(changes);
73
73
  const idState = { maxId };
74
74
  const pairwiseDelegate = (left, right) => {
75
- return this.composePair(left, right, revInfos, idState);
75
+ return this.composePair(left, right, idState);
76
76
  };
77
77
  const innerChanges = changes.map((change) => change.change);
78
78
  return balancedReduce(innerChanges, pairwiseDelegate, makeModularChangeset);
79
79
  }
80
- composePair(change1, change2, revInfos, idState) {
81
- const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys } = this.composeAllFields(change1, change2, revInfos, idState);
80
+ composePair(change1, change2, idState) {
81
+ const revInfos = composeRevInfos(change1.revisions, change2.revisions);
82
+ const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, rootNodes } = this.composeAllFields(change1, change2, revInfos, idState);
82
83
  const { allBuilds, allDestroys, allRefreshers } = composeBuildsDestroysAndRefreshers(change1, change2);
83
84
  // The composed changeset has a "no change" constraint if either change has one
84
85
  const noChangeConstraint = change1.noChangeConstraint ?? change2.noChangeConstraint;
85
86
  const noChangeConstraintOnRevert = change1.noChangeConstraintOnRevert ?? change2.noChangeConstraintOnRevert;
86
- return makeModularChangeset({
87
+ const composed = makeModularChangeset({
87
88
  fieldChanges,
88
89
  nodeChanges,
89
90
  nodeToParent,
@@ -91,12 +92,16 @@ export class ModularChangeFamily {
91
92
  crossFieldKeys,
92
93
  maxId: idState.maxId,
93
94
  revisions: revInfos,
94
- noChangeConstraint,
95
- noChangeConstraintOnRevert,
95
+ rootNodes,
96
96
  builds: allBuilds,
97
97
  destroys: allDestroys,
98
98
  refreshers: allRefreshers,
99
+ noChangeConstraint,
100
+ noChangeConstraintOnRevert,
99
101
  });
102
+ // XXX: This is an expensive assert which should be disabled before merging.
103
+ validateChangeset(composed, this.fieldKinds);
104
+ return composed;
100
105
  }
101
106
  composeAllFields(potentiallyConflictedChange1, potentiallyConflictedChange2, revInfos, idState) {
102
107
  // Our current cell ordering scheme in sequences depends on being able to rebase over a change with conflicts.
@@ -117,34 +122,50 @@ export class ModularChangeFamily {
117
122
  const composedNodeChanges = brand(mergeTupleBTrees(change1.nodeChanges, change2.nodeChanges));
118
123
  const composedNodeToParent = brand(mergeTupleBTrees(change1.nodeToParent, change2.nodeToParent));
119
124
  const composedNodeAliases = brand(mergeTupleBTrees(change1.nodeAliases, change2.nodeAliases));
120
- const crossFieldTable = newComposeTable(change1, change2, composedNodeToParent);
125
+ const pendingCompositions = {
126
+ nodeIdsToCompose: [],
127
+ affectedBaseFields: newTupleBTree(),
128
+ };
129
+ const movedCrossFieldKeys = newCrossFieldRangeTable();
130
+ const removedCrossFieldKeys = newCrossFieldRangeTable();
131
+ const composedRoots = composeRootTables(change1, change2, composedNodeToParent, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions);
132
+ const crossFieldTable = newComposeTable(change1, change2, composedRoots, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions);
121
133
  const composedFields = this.composeFieldMaps(change1.fieldChanges, change2.fieldChanges, undefined, genId, crossFieldTable, revisionMetadata);
122
134
  this.composeInvalidatedElements(crossFieldTable, composedFields, composedNodeChanges, composedNodeToParent, composedNodeAliases, genId, revisionMetadata);
123
- // Currently no field kinds require making changes to cross-field keys during composition, so we can just merge the two tables.
124
- const composedCrossFieldKeys = RangeMap.union(change1.crossFieldKeys, change2.crossFieldKeys);
135
+ for (const entry of crossFieldTable.renamesToDelete.entries()) {
136
+ deleteNodeRenameFrom(crossFieldTable.composedRootNodes, entry.start, entry.length);
137
+ }
138
+ for (const [nodeId, location] of crossFieldTable.movedNodeToParent.entries()) {
139
+ // Moved nodes are from change2.
140
+ // If there is a corresponding node in change1, then composedNodeToParent will already have the correct entry,
141
+ // because the location of the node is the same in change1 and the composed change
142
+ // (since they have the same input context).
143
+ if (crossFieldTable.newToBaseNodeId.get(nodeId) === undefined) {
144
+ composedNodeToParent.set(nodeId, location);
145
+ }
146
+ }
125
147
  return {
126
148
  fieldChanges: composedFields,
127
149
  nodeChanges: composedNodeChanges,
128
150
  nodeToParent: composedNodeToParent,
129
151
  nodeAliases: composedNodeAliases,
130
- crossFieldKeys: composedCrossFieldKeys,
152
+ crossFieldKeys: composeCrossFieldKeyTables(change1.crossFieldKeys, change2.crossFieldKeys, crossFieldTable.movedCrossFieldKeys, crossFieldTable.removedCrossFieldKeys),
153
+ rootNodes: composedRoots,
131
154
  };
132
155
  }
133
156
  composeInvalidatedField(fieldChange, crossFieldTable, genId, revisionMetadata) {
134
157
  const context = crossFieldTable.fieldToContext.get(fieldChange);
135
158
  assert(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
136
- const { fieldId, change1: fieldChange1, change2: fieldChange2, composedChange } = context;
159
+ const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
160
+ crossFieldTable.pendingCompositions.affectedBaseFields.delete(fieldIdKeyFromFieldId(context.fieldId));
137
161
  const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
138
162
  const composeNodes = (child1, child2) => {
139
- if (child1 !== undefined &&
140
- child2 !== undefined &&
141
- getFromChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2) === undefined) {
142
- setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
143
- crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
163
+ if (child1 !== undefined && child2 !== undefined) {
164
+ addNodesToCompose(crossFieldTable, child1, child2);
144
165
  }
145
166
  return child1 ?? child2 ?? fail(0xb22 /* Should not compose two undefined nodes */);
146
167
  };
147
- const amendedChange = rebaser.compose(fieldChange1, fieldChange2, composeNodes, genId, new ComposeManager(crossFieldTable, fieldChange, fieldId, false), revisionMetadata);
168
+ const amendedChange = rebaser.compose(fieldChange1, fieldChange2, composeNodes, genId, new ComposeNodeManagerI(crossFieldTable, context.fieldId, false), revisionMetadata);
148
169
  composedChange.change = brand(amendedChange);
149
170
  }
150
171
  /**
@@ -153,32 +174,23 @@ export class ModularChangeFamily {
153
174
  * - discovering that two node changesets refer to the same node (`nodeIdsToCompose`)
154
175
  * - a previously composed field being invalidated by a cross field effect (`invalidatedFields`)
155
176
  * - a field which was copied directly from an input changeset being invalidated by a cross field effect
156
- * (`affectedBaseFields` and `affectedNewFields`)
177
+ * (`affectedBaseFields`)
157
178
  *
158
179
  * Updating an element may invalidate further elements. This function runs until there is no more invalidation.
159
180
  */
160
181
  composeInvalidatedElements(table, composedFields, composedNodes, composedNodeToParent, nodeAliases, genId, metadata) {
161
182
  const pending = table.pendingCompositions;
162
- while (table.invalidatedFields.size > 0 ||
163
- pending.nodeIdsToCompose.length > 0 ||
164
- pending.affectedBaseFields.length > 0 ||
165
- pending.affectedNewFields.length > 0) {
166
- // Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
167
- for (const [id1, id2] of pending.nodeIdsToCompose) {
168
- this.composeNodesById(table.baseChange.nodeChanges, table.newChange.nodeChanges, composedNodes, composedNodeToParent, nodeAliases, id1, id2, genId, table, metadata);
169
- }
170
- pending.nodeIdsToCompose.length = 0;
171
- this.composeAffectedFields(table, table.baseChange, true, pending.affectedBaseFields, composedFields, composedNodes, genId, metadata);
172
- this.composeAffectedFields(table, table.newChange, false, pending.affectedNewFields, composedFields, composedNodes, genId, metadata);
173
- this.processInvalidatedCompositions(table, genId, metadata);
183
+ while (pending.nodeIdsToCompose.length > 0 || pending.affectedBaseFields.length > 0) {
184
+ this.processPendingNodeCompositions(table, composedNodes, composedNodeToParent, nodeAliases, genId, metadata);
185
+ this.composeAffectedFields(table, table.baseChange, pending.affectedBaseFields, composedFields, composedNodes, genId, metadata);
174
186
  }
175
187
  }
176
- processInvalidatedCompositions(table, genId, metadata) {
177
- const fieldsToUpdate = table.invalidatedFields;
178
- table.invalidatedFields = new Set();
179
- for (const fieldChange of fieldsToUpdate) {
180
- this.composeInvalidatedField(fieldChange, table, genId, metadata);
188
+ processPendingNodeCompositions(table, composedNodes, composedNodeToParent, nodeAliases, genId, metadata) {
189
+ // Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
190
+ for (const [id1, id2] of table.pendingCompositions.nodeIdsToCompose) {
191
+ this.composeNodesById(table.baseChange, table.newChange, composedNodes, composedNodeToParent, nodeAliases, id1, id2, genId, table, metadata);
181
192
  }
193
+ table.pendingCompositions.nodeIdsToCompose.length = 0;
182
194
  }
183
195
  /**
184
196
  * Ensures that each field in `affectedFields` has been updated in the composition output.
@@ -191,38 +203,36 @@ export class ModularChangeFamily {
191
203
  * If not, they are assumed to be part of the new changeset.
192
204
  * @param affectedFields - The set of fields to process.
193
205
  */
194
- composeAffectedFields(table, change, areBaseFields, affectedFields, composedFields, composedNodes, genId, metadata) {
195
- for (const fieldIdKey of affectedFields.keys()) {
196
- const fieldId = normalizeFieldId(fieldIdFromFieldIdKey(fieldIdKey), change.nodeAliases);
197
- const fieldChange = fieldChangeFromId(change.fieldChanges, change.nodeChanges, fieldId);
206
+ composeAffectedFields(table, change, affectedFields, composedFields, composedNodes, genId, metadata) {
207
+ const fieldsToProcess = affectedFields.clone();
208
+ affectedFields.clear();
209
+ for (const fieldIdKey of fieldsToProcess.keys()) {
210
+ const fieldId = fieldIdFromFieldIdKey(fieldIdKey);
211
+ const fieldChange = fieldChangeFromId(change, fieldId);
198
212
  if (table.fieldToContext.has(fieldChange) ||
199
213
  table.newFieldToBaseField.has(fieldChange)) {
200
- // This function handles fields which were not part of the intersection of the two changesets but which need to be updated anyway.
201
- // If we've already processed this field then either it is up to date
202
- // or there is pending inval which will be handled in processInvalidatedCompositions.
203
- continue;
204
- }
205
- const emptyChange = this.createEmptyFieldChange(fieldChange.fieldKind);
206
- const [change1, change2] = areBaseFields
207
- ? [fieldChange, emptyChange]
208
- : [emptyChange, fieldChange];
209
- const composedField = this.composeFieldChanges(fieldId, change1, change2, genId, table, metadata);
210
- if (fieldId.nodeId === undefined) {
211
- composedFields.set(fieldId.field, composedField);
212
- continue;
214
+ this.composeInvalidatedField(fieldChange, table, genId, metadata);
213
215
  }
214
- const nodeId = getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId;
215
- let nodeChangeset = nodeChangeFromId(composedNodes, nodeId);
216
- if (!table.composedNodes.has(nodeChangeset)) {
217
- nodeChangeset = cloneNodeChangeset(nodeChangeset);
218
- setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
219
- }
220
- if (nodeChangeset.fieldChanges === undefined) {
221
- nodeChangeset.fieldChanges = new Map();
216
+ else {
217
+ this.composeFieldWithNoNewChange(table, fieldChange, fieldId, composedFields, composedNodes, genId, metadata);
222
218
  }
223
- nodeChangeset.fieldChanges.set(fieldId.field, composedField);
224
219
  }
225
- affectedFields.clear();
220
+ }
221
+ composeFieldWithNoNewChange(table, baseFieldChange, fieldId, composedFields, composedNodes, genId, metadata) {
222
+ const emptyChange = this.createEmptyFieldChange(baseFieldChange.fieldKind);
223
+ const composedField = this.composeFieldChanges(fieldId, baseFieldChange, emptyChange, genId, table, metadata);
224
+ if (fieldId.nodeId === undefined) {
225
+ composedFields.set(fieldId.field, composedField);
226
+ return;
227
+ }
228
+ const nodeId = normalizeNodeId(getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId, table.baseChange.nodeAliases);
229
+ // We clone the node changeset before mutating it, as it may be from one of the input changesets.
230
+ const nodeChangeset = cloneNodeChangeset(nodeChangeFromId(composedNodes, table.baseChange.nodeAliases, nodeId));
231
+ setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
232
+ if (nodeChangeset.fieldChanges === undefined) {
233
+ nodeChangeset.fieldChanges = new Map();
234
+ }
235
+ nodeChangeset.fieldChanges.set(fieldId.field, composedField);
226
236
  }
227
237
  composeFieldMaps(change1, change2, parentId, genId, crossFieldTable, revisionMetadata) {
228
238
  const composedFields = new Map();
@@ -232,6 +242,14 @@ export class ModularChangeFamily {
232
242
  for (const [field, fieldChange1] of change1) {
233
243
  const fieldId = { nodeId: parentId, field };
234
244
  const fieldChange2 = change2.get(field);
245
+ const cachedComposedFieldChange = crossFieldTable.fieldToContext.get(fieldChange1)?.composedChange;
246
+ if (fieldChange2 === undefined && cachedComposedFieldChange !== undefined) {
247
+ // This can happen if the field was previous processed in `composeFieldWithNoNewChange`.
248
+ // If `change2` does not have a change for this field, then without this check we would
249
+ // lose the composed field change and instead simply have `change1`'s change.
250
+ composedFields.set(field, cachedComposedFieldChange);
251
+ continue;
252
+ }
235
253
  const composedField = fieldChange2 === undefined
236
254
  ? fieldChange1
237
255
  : this.composeFieldChanges(fieldId, fieldChange1, fieldChange2, genId, crossFieldTable, revisionMetadata);
@@ -251,17 +269,16 @@ export class ModularChangeFamily {
251
269
  * will be added to `crossFieldTable.pendingCompositions.nodeIdsToCompose`.
252
270
  *
253
271
  * Any fields which had cross-field information sent to them as part of this field composition
254
- * will be added to either `affectedBaseFields` or `affectedNewFields` in `crossFieldTable.pendingCompositions`.
272
+ * will be added to `affectedBaseFields` in `crossFieldTable.pendingCompositions`.
255
273
  *
256
274
  * Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
257
275
  */
258
276
  composeFieldChanges(fieldId, change1, change2, idAllocator, crossFieldTable, revisionMetadata) {
259
277
  const { fieldKind, changeHandler, change1: change1Normalized, change2: change2Normalized, } = this.normalizeFieldChanges(change1, change2);
260
- const manager = new ComposeManager(crossFieldTable, change1, fieldId);
278
+ const manager = new ComposeNodeManagerI(crossFieldTable, fieldId);
261
279
  const composedChange = changeHandler.rebaser.compose(change1Normalized, change2Normalized, (child1, child2) => {
262
280
  if (child1 !== undefined && child2 !== undefined) {
263
- setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
264
- crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
281
+ addNodesToCompose(crossFieldTable, child1, child2);
265
282
  }
266
283
  return child1 ?? child2 ?? fail(0xb23 /* Should not compose two undefined nodes */);
267
284
  }, idAllocator, manager, revisionMetadata);
@@ -278,19 +295,18 @@ export class ModularChangeFamily {
278
295
  crossFieldTable.newFieldToBaseField.set(change2, change1);
279
296
  return composedField;
280
297
  }
281
- composeNodesById(nodeChanges1, nodeChanges2, composedNodes, composedNodeToParent, nodeAliases, id1, id2, idAllocator, crossFieldTable, revisionMetadata) {
282
- const nodeChangeset1 = nodeChangeFromId(nodeChanges1, id1);
283
- const nodeChangeset2 = nodeChangeFromId(nodeChanges2, id2);
298
+ composeNodesById(change1, change2, composedNodes, composedNodeToParent, composedAliases, id1, id2, idAllocator, crossFieldTable, revisionMetadata) {
299
+ const nodeChangeset1 = nodeChangeFromId(change1.nodeChanges, change1.nodeAliases, id1);
300
+ const nodeChangeset2 = nodeChangeFromId(change2.nodeChanges, change2.nodeAliases, id2);
284
301
  const composedNodeChangeset = this.composeNodeChanges(id1, nodeChangeset1, nodeChangeset2, idAllocator, crossFieldTable, revisionMetadata);
285
302
  setInChangeAtomIdMap(composedNodes, id1, composedNodeChangeset);
286
303
  if (!areEqualChangeAtomIds(id1, id2)) {
287
304
  composedNodes.delete([id2.revision, id2.localId]);
288
305
  composedNodeToParent.delete([id2.revision, id2.localId]);
289
- setInChangeAtomIdMap(nodeAliases, id2, id1);
306
+ setInChangeAtomIdMap(composedAliases, id2, id1);
290
307
  // We need to delete id1 to avoid forming a cycle in case id1 already had an alias.
291
- nodeAliases.delete([id1.revision, id1.localId]);
308
+ composedAliases.delete([id1.revision, id1.localId]);
292
309
  }
293
- crossFieldTable.composedNodes.add(composedNodeChangeset);
294
310
  }
295
311
  composeNodeChanges(nodeId, change1, change2, genId, crossFieldTable, revisionMetadata) {
296
312
  // WARNING: this composition logic assumes that we never make compositions of the following form:
@@ -343,9 +359,14 @@ export class ModularChangeFamily {
343
359
  }
344
360
  const genId = idAllocatorFromMaxId(change.change.maxId ?? -1);
345
361
  const crossFieldTable = {
346
- ...newCrossFieldTable(),
362
+ change: change.change,
363
+ entries: newChangeAtomIdRangeMap(),
347
364
  originalFieldToContext: new Map(),
365
+ invertRevision: revisionForInvert,
348
366
  invertedNodeToParent: brand(change.change.nodeToParent.clone()),
367
+ invalidatedFields: new Set(),
368
+ invertedRoots: invertRootTable(change.change, isRollback),
369
+ attachToDetachId: newChangeAtomIdTransform(),
349
370
  };
350
371
  const { revInfos: oldRevInfos } = getRevInfoFromTaggedChanges([change]);
351
372
  const revisionMetadata = revisionMetadataSourceFromInfo(oldRevInfos);
@@ -361,16 +382,18 @@ export class ModularChangeFamily {
361
382
  const originalFieldChange = fieldChange.change;
362
383
  const context = crossFieldTable.originalFieldToContext.get(fieldChange);
363
384
  assert(context !== undefined, 0x851 /* Should have context for every invalidated field */);
364
- const { invertedField, fieldId } = context;
365
- const amendedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(originalFieldChange, isRollback, genId, revisionForInvert, new InvertManager(crossFieldTable, fieldChange, fieldId), revisionMetadata);
385
+ const { invertedField } = context;
386
+ const amendedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(originalFieldChange, isRollback, genId, revisionForInvert, new InvertNodeManagerI(crossFieldTable, context.fieldId), revisionMetadata);
366
387
  invertedField.change = brand(amendedChange);
367
388
  }
368
389
  }
369
390
  const crossFieldKeys = this.makeCrossFieldKeyTable(invertedFields, invertedNodes);
391
+ this.processInvertRenames(crossFieldTable);
370
392
  return makeModularChangeset({
371
393
  fieldChanges: invertedFields,
372
394
  nodeChanges: invertedNodes,
373
395
  nodeToParent: crossFieldTable.invertedNodeToParent,
396
+ rootNodes: crossFieldTable.invertedRoots,
374
397
  nodeAliases: change.change.nodeAliases,
375
398
  crossFieldKeys,
376
399
  maxId: genId.getMaxId(),
@@ -386,7 +409,7 @@ export class ModularChangeFamily {
386
409
  const invertedFields = new Map();
387
410
  for (const [field, fieldChange] of changes) {
388
411
  const fieldId = { nodeId: parentId, field };
389
- const manager = new InvertManager(crossFieldTable, fieldChange, fieldId);
412
+ const manager = new InvertNodeManagerI(crossFieldTable, fieldId);
390
413
  const invertedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(fieldChange.change, isRollback, genId, revisionForInvert, manager, revisionMetadata);
391
414
  const invertedFieldChange = {
392
415
  ...fieldChange,
@@ -418,6 +441,12 @@ export class ModularChangeFamily {
418
441
  }
419
442
  return inverse;
420
443
  }
444
+ processInvertRenames(table) {
445
+ for (const { start: newAttachId, value: originalDetachId, length, } of table.attachToDetachId.entries()) {
446
+ // Note that the detach location is already set in `invertDetach`.
447
+ addNodeRename(table.invertedRoots, originalDetachId, newAttachId, length, undefined);
448
+ }
449
+ }
421
450
  rebase(taggedChange, potentiallyConflictedOver, revisionMetadata) {
422
451
  // Our current cell ordering scheme in sequences depends on being able to rebase over a change with conflicts.
423
452
  // This means that we must rebase over a muted version of the conflicted changeset.
@@ -428,17 +457,26 @@ export class ModularChangeFamily {
428
457
  const maxId = Math.max(change.maxId ?? -1, over.change.maxId ?? -1);
429
458
  const idState = { maxId };
430
459
  const genId = idAllocatorFromState(idState);
460
+ const affectedBaseFields = newTupleBTree();
461
+ const nodesToRebase = [];
462
+ const rebasedNodeToParent = brand(change.nodeToParent.clone());
463
+ const rebaseVersion = Math.max(change.rebaseVersion, over.change.rebaseVersion);
464
+ const rebasedRootNodes = rebaseRoots(change, over.change, affectedBaseFields, nodesToRebase, rebasedNodeToParent, rebaseVersion);
431
465
  const crossFieldTable = {
432
- ...newCrossFieldTable(),
466
+ rebaseVersion,
467
+ entries: newDetachedEntryMap(),
433
468
  newChange: change,
434
469
  baseChange: over.change,
435
470
  baseFieldToContext: new Map(),
471
+ baseRoots: over.change.rootNodes,
472
+ rebasedRootNodes,
436
473
  baseToRebasedNodeId: newTupleBTree(),
437
474
  rebasedFields: new Set(),
438
- rebasedNodeToParent: brand(change.nodeToParent.clone()),
439
- rebasedCrossFieldKeys: change.crossFieldKeys.clone(),
475
+ rebasedNodeToParent,
476
+ rebasedDetachLocations: newChangeAtomIdRangeMap(),
477
+ movedDetaches: newChangeAtomIdRangeMap(),
440
478
  nodeIdPairs: [],
441
- affectedBaseFields: newTupleBTree(),
479
+ affectedBaseFields,
442
480
  fieldsWithUnattachedChild: new Set(),
443
481
  };
444
482
  const getBaseRevisions = () => revisionInfoFromTaggedChange(over).map((info) => info.revision);
@@ -448,8 +486,9 @@ export class ModularChangeFamily {
448
486
  getBaseRevisions,
449
487
  };
450
488
  const rebasedNodes = brand(change.nodeChanges.clone());
451
- const rebasedFields = this.rebaseIntersectingFields(crossFieldTable, rebasedNodes, genId, rebaseMetadata);
452
- this.rebaseInvalidatedElements(rebasedFields, rebasedNodes, crossFieldTable, rebaseMetadata, genId);
489
+ const rebasedFields = this.rebaseIntersectingFields(nodesToRebase, crossFieldTable, rebasedNodes, genId, rebaseMetadata);
490
+ this.rebaseInvalidatedFields(rebasedFields, rebasedNodes, crossFieldTable, rebaseMetadata, genId);
491
+ fixupRebasedDetachLocations(crossFieldTable);
453
492
  const constraintState = newConstraintState(change.constraintViolationCount ?? 0);
454
493
  const revertConstraintState = newConstraintState(change.constraintViolationCountOnRevert ?? 0);
455
494
  let noChangeConstraint = change.noChangeConstraint;
@@ -457,13 +496,17 @@ export class ModularChangeFamily {
457
496
  noChangeConstraint = { violated: true };
458
497
  constraintState.violationCount += 1;
459
498
  }
460
- this.updateConstraintsForFields(rebasedFields, NodeAttachState.Attached, NodeAttachState.Attached, constraintState, revertConstraintState, rebasedNodes);
499
+ this.updateConstraints(rebasedFields, rebasedNodes, rebasedRootNodes, constraintState, revertConstraintState);
500
+ const fieldsWithRootMoves = getFieldsWithRootMoves(crossFieldTable.rebasedRootNodes, change.nodeAliases);
501
+ const fieldToRootChanges = getFieldToRootChanges(crossFieldTable.rebasedRootNodes, change.nodeAliases);
461
502
  const rebased = makeModularChangeset({
462
- fieldChanges: this.pruneFieldMap(rebasedFields, rebasedNodes),
503
+ fieldChanges: this.pruneFieldMap(rebasedFields, undefined, rebasedNodes, crossFieldTable.rebasedNodeToParent, change.nodeAliases, crossFieldTable.rebasedRootNodes, fieldsWithRootMoves, fieldToRootChanges),
463
504
  nodeChanges: rebasedNodes,
464
505
  nodeToParent: crossFieldTable.rebasedNodeToParent,
506
+ rootNodes: this.pruneRoots(crossFieldTable.rebasedRootNodes, rebasedNodes, crossFieldTable.rebasedNodeToParent, change.nodeAliases, fieldsWithRootMoves, fieldToRootChanges),
507
+ // TODO: Do we need to include aliases for node changesets added during rebasing?
465
508
  nodeAliases: change.nodeAliases,
466
- crossFieldKeys: crossFieldTable.rebasedCrossFieldKeys,
509
+ crossFieldKeys: rebaseCrossFieldKeys(change.crossFieldKeys, crossFieldTable.movedDetaches, crossFieldTable.rebasedDetachLocations),
467
510
  maxId: idState.maxId,
468
511
  revisions: change.revisions,
469
512
  constraintViolationCount: constraintState.violationCount,
@@ -473,15 +516,22 @@ export class ModularChangeFamily {
473
516
  builds: change.builds,
474
517
  destroys: change.destroys,
475
518
  refreshers: change.refreshers,
519
+ rebaseVersion,
476
520
  });
521
+ // XXX: This is an expensive assert which should be disabled before merging.
522
+ validateChangeset(rebased, this.fieldKinds);
477
523
  return rebased;
478
524
  }
479
525
  // This performs a first pass on all fields which have both new and base changes.
480
526
  // TODO: Can we also handle additional passes in this method?
481
- rebaseIntersectingFields(crossFieldTable, rebasedNodes, genId, metadata) {
527
+ rebaseIntersectingFields(rootChanges, crossFieldTable, rebasedNodes, genId, metadata) {
482
528
  const change = crossFieldTable.newChange;
483
529
  const baseChange = crossFieldTable.baseChange;
484
530
  const rebasedFields = this.rebaseFieldMap(change.fieldChanges, baseChange.fieldChanges, undefined, genId, crossFieldTable, metadata);
531
+ for (const [newChildChange, baseChildChange] of rootChanges) {
532
+ const rebasedNode = this.rebaseNodeChange(newChildChange, baseChildChange, genId, crossFieldTable, metadata);
533
+ setInChangeAtomIdMap(rebasedNodes, newChildChange, rebasedNode);
534
+ }
485
535
  // This loop processes all fields which have both base and new changes.
486
536
  // Note that the call to `rebaseNodeChange` can add entries to `crossFieldTable.nodeIdPairs`.
487
537
  for (const [newId, baseId, _attachState] of crossFieldTable.nodeIdPairs) {
@@ -490,87 +540,85 @@ export class ModularChangeFamily {
490
540
  }
491
541
  return rebasedFields;
492
542
  }
493
- // This processes fields which have no new changes but have been invalidated by another field.
494
- rebaseFieldsWithoutNewChanges(rebasedFields, rebasedNodes, crossFieldTable, genId, metadata) {
495
- const baseChange = crossFieldTable.baseChange;
496
- for (const [revision, localId, fieldKey] of crossFieldTable.affectedBaseFields.keys()) {
497
- const baseNodeId = localId === undefined
498
- ? undefined
499
- : normalizeNodeId({ revision, localId }, baseChange.nodeAliases);
500
- const baseFieldChange = fieldMapFromNodeId(baseChange.fieldChanges, baseChange.nodeChanges, baseNodeId).get(fieldKey);
501
- assert(baseFieldChange !== undefined, 0x9c2 /* Cross field key registered for empty field */);
502
- if (crossFieldTable.baseFieldToContext.has(baseFieldChange)) {
503
- // This field has already been processed because there were changes to rebase.
504
- continue;
505
- }
506
- // This field has no changes in the new changeset, otherwise it would have been added to
507
- // `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
508
- const rebaseChild = (child, baseChild, stateChange) => {
509
- assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
543
+ rebaseFieldWithoutNewChanges(baseFieldChange, baseFieldId, crossFieldTable, rebasedFields, rebasedNodes, genId, metadata,
544
+ /**
545
+ * The ID of a node in `baseFieldChange` which should be included in the rebased field change.
546
+ */
547
+ baseNodeId) {
548
+ // This field has no changes in the new changeset, otherwise it would have been added to
549
+ // `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
550
+ const rebaseChild = (child, baseChild, stateChange) => {
551
+ assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
552
+ if (baseChild === undefined || baseNodeId === undefined) {
510
553
  return undefined;
511
- };
512
- const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
513
- const fieldChange = {
514
- ...baseFieldChange,
515
- change: brand(handler.createEmpty()),
516
- };
517
- const rebasedNodeId = baseNodeId === undefined
518
- ? undefined
519
- : rebasedNodeIdFromBaseNodeId(crossFieldTable, baseNodeId);
520
- const fieldId = { nodeId: rebasedNodeId, field: fieldKey };
521
- const rebasedField = handler.rebaser.rebase(fieldChange.change, baseFieldChange.change, rebaseChild, genId, new RebaseManager(crossFieldTable, baseFieldChange, fieldId), metadata);
522
- const rebasedFieldChange = {
523
- ...baseFieldChange,
524
- change: brand(rebasedField),
525
- };
526
- // TODO: Deduplicate
527
- crossFieldTable.baseFieldToContext.set(baseFieldChange, {
528
- newChange: fieldChange,
529
- baseChange: baseFieldChange,
530
- rebasedChange: rebasedFieldChange,
531
- fieldId,
532
- baseNodeIds: [],
533
- });
534
- crossFieldTable.rebasedFields.add(rebasedFieldChange);
535
- this.attachRebasedField(rebasedFields, rebasedNodes, crossFieldTable, rebasedFieldChange, fieldId, genId, metadata);
536
- }
537
- }
538
- rebaseInvalidatedElements(rebasedFields, rebasedNodes, table, metadata, idAllocator) {
539
- this.rebaseFieldsWithoutNewChanges(rebasedFields, rebasedNodes, table, idAllocator, metadata);
540
- this.rebaseFieldsWithUnattachedChild(table, metadata, idAllocator);
541
- this.rebaseInvalidatedFields(table, metadata, idAllocator);
542
- }
543
- rebaseInvalidatedFields(crossFieldTable, rebaseMetadata, genId) {
544
- const fieldsToUpdate = crossFieldTable.invalidatedFields;
545
- crossFieldTable.invalidatedFields = new Set();
546
- for (const field of fieldsToUpdate) {
547
- this.rebaseInvalidatedField(field, crossFieldTable, rebaseMetadata, genId);
548
- }
549
- }
550
- rebaseFieldsWithUnattachedChild(table, metadata, idAllocator) {
551
- for (const field of table.fieldsWithUnattachedChild) {
552
- table.invalidatedFields.delete(field);
553
- this.rebaseInvalidatedField(field, table, metadata, idAllocator, true);
554
+ }
555
+ return areEqualChangeAtomIds(normalizeNodeId(baseChild, crossFieldTable.baseChange.nodeAliases), baseNodeId)
556
+ ? baseNodeId
557
+ : undefined;
558
+ };
559
+ const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
560
+ const fieldChange = {
561
+ ...baseFieldChange,
562
+ change: brand(handler.createEmpty()),
563
+ };
564
+ const rebasedNodeId = baseFieldId.nodeId === undefined
565
+ ? undefined
566
+ : rebasedNodeIdFromBaseNodeId(crossFieldTable, baseFieldId.nodeId);
567
+ const fieldId = { nodeId: rebasedNodeId, field: baseFieldId.field };
568
+ const rebasedField = handler.rebaser.rebase(fieldChange.change, baseFieldChange.change, rebaseChild, genId, new RebaseNodeManagerI(crossFieldTable, fieldId), metadata, crossFieldTable.rebaseVersion);
569
+ const rebasedFieldChange = {
570
+ ...baseFieldChange,
571
+ change: brand(rebasedField),
572
+ };
573
+ const context = {
574
+ newChange: fieldChange,
575
+ baseChange: baseFieldChange,
576
+ rebasedChange: rebasedFieldChange,
577
+ fieldId,
578
+ baseNodeIds: newTupleBTree(),
579
+ };
580
+ if (baseNodeId !== undefined) {
581
+ setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true);
582
+ }
583
+ crossFieldTable.baseFieldToContext.set(baseFieldChange, context);
584
+ crossFieldTable.rebasedFields.add(rebasedFieldChange);
585
+ this.attachRebasedField(rebasedFields, rebasedNodes, crossFieldTable, rebasedFieldChange, fieldId, genId, metadata);
586
+ }
587
+ rebaseInvalidatedFields(rebasedFields, rebasedNodes, crossFieldTable, rebaseMetadata, genId) {
588
+ while (crossFieldTable.affectedBaseFields.size > 0) {
589
+ const baseFields = crossFieldTable.affectedBaseFields.clone();
590
+ crossFieldTable.affectedBaseFields.clear();
591
+ for (const baseFieldIdKey of baseFields.keys()) {
592
+ const baseFieldId = normalizeFieldId(fieldIdFromFieldIdKey(baseFieldIdKey), crossFieldTable.baseChange.nodeAliases);
593
+ const baseField = fieldChangeFromId(crossFieldTable.baseChange, baseFieldId);
594
+ assert(baseField !== undefined, 0x9c2 /* Cross field key registered for empty field */);
595
+ const context = crossFieldTable.baseFieldToContext.get(baseField);
596
+ if (context === undefined) {
597
+ this.rebaseFieldWithoutNewChanges(baseField, baseFieldId, crossFieldTable, rebasedFields, rebasedNodes, genId, rebaseMetadata);
598
+ }
599
+ else {
600
+ this.rebaseInvalidatedField(baseField, crossFieldTable, context, rebaseMetadata, genId);
601
+ }
602
+ }
554
603
  }
555
604
  }
556
- rebaseInvalidatedField(baseField, crossFieldTable, rebaseMetadata, genId, allowInval = false) {
557
- const context = crossFieldTable.baseFieldToContext.get(baseField);
558
- assert(context !== undefined, 0x852 /* Every field should have a context */);
605
+ rebaseInvalidatedField(baseField, crossFieldTable, context, rebaseMetadata, genId) {
559
606
  const { changeHandler, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(context.newChange, context.baseChange);
560
607
  const rebaseChild = (curr, base) => {
561
608
  if (curr !== undefined) {
562
609
  return curr;
563
610
  }
564
- if (base !== undefined) {
565
- for (const id of context.baseNodeIds) {
566
- if (areEqualChangeAtomIds(base, id)) {
567
- return base;
568
- }
569
- }
611
+ if (base !== undefined && getFromChangeAtomIdMap(context.baseNodeIds, base) === true) {
612
+ return base;
570
613
  }
571
614
  return undefined;
572
615
  };
573
- context.rebasedChange.change = brand(changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, new RebaseManager(crossFieldTable, baseField, context.fieldId, allowInval), rebaseMetadata));
616
+ let allowInval = false;
617
+ if (crossFieldTable.fieldsWithUnattachedChild.has(baseField)) {
618
+ crossFieldTable.fieldsWithUnattachedChild.delete(baseField);
619
+ allowInval = true;
620
+ }
621
+ context.rebasedChange.change = brand(changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, new RebaseNodeManagerI(crossFieldTable, context.fieldId, allowInval), rebaseMetadata, crossFieldTable.rebaseVersion));
574
622
  }
575
623
  attachRebasedField(rebasedFields, rebasedNodes, table, rebasedField, { nodeId, field: fieldKey }, idAllocator, metadata) {
576
624
  if (nodeId === undefined) {
@@ -579,12 +627,14 @@ export class ModularChangeFamily {
579
627
  }
580
628
  const rebasedNode = getFromChangeAtomIdMap(rebasedNodes, nodeId);
581
629
  if (rebasedNode !== undefined) {
582
- if (rebasedNode.fieldChanges === undefined) {
583
- rebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
630
+ const updatedRebasedNode = cloneNodeChangeset(rebasedNode);
631
+ setInChangeAtomIdMap(rebasedNodes, nodeId, updatedRebasedNode);
632
+ if (updatedRebasedNode.fieldChanges === undefined) {
633
+ updatedRebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
584
634
  return;
585
635
  }
586
- assert(!rebasedNode.fieldChanges.has(fieldKey), 0x9c4 /* Expected an empty field */);
587
- rebasedNode.fieldChanges.set(fieldKey, rebasedField);
636
+ assert(!updatedRebasedNode.fieldChanges.has(fieldKey), 0x9c4 /* Expected an empty field */);
637
+ updatedRebasedNode.fieldChanges.set(fieldKey, rebasedField);
588
638
  return;
589
639
  }
590
640
  const newNode = {
@@ -592,39 +642,51 @@ export class ModularChangeFamily {
592
642
  };
593
643
  setInChangeAtomIdMap(rebasedNodes, nodeId, newNode);
594
644
  setInChangeAtomIdMap(table.baseToRebasedNodeId, nodeId, nodeId);
595
- const parentFieldId = getParentFieldId(table.baseChange, nodeId);
596
- this.attachRebasedNode(rebasedFields, rebasedNodes, table, nodeId, parentFieldId, idAllocator, metadata);
597
- }
598
- attachRebasedNode(rebasedFields, rebasedNodes, table, baseNodeId, parentFieldIdBase, idAllocator, metadata) {
599
- const baseFieldChange = fieldChangeFromId(table.baseChange.fieldChanges, table.baseChange.nodeChanges, parentFieldIdBase);
645
+ const parentBase = getNodeParent(table.baseChange, nodeId);
646
+ this.attachRebasedNode(rebasedFields, rebasedNodes, table, nodeId, parentBase, idAllocator, metadata);
647
+ }
648
+ attachRebasedNode(rebasedFields, rebasedNodes, table, baseNodeId, parentBase, idAllocator, metadata) {
649
+ if (parentBase.root !== undefined) {
650
+ const renamedRoot = firstAttachIdFromDetachId(table.baseChange.rootNodes, parentBase.root, 1).value;
651
+ const attachField = table.baseChange.crossFieldKeys.getFirst({ ...renamedRoot, target: CrossFieldTarget.Destination }, 1).value;
652
+ if (attachField === undefined) {
653
+ const baseDetachLocation = table.baseChange.rootNodes.detachLocations.getFirst(parentBase.root, 1).value;
654
+ assignRootChange(table.rebasedRootNodes, table.rebasedNodeToParent, renamedRoot, baseNodeId, baseDetachLocation, table.rebaseVersion);
655
+ // We need to make sure the rebased changeset includes the detach location,
656
+ // so we add that field to `affectedBaseFields` unless it's already been processed.
657
+ if (baseDetachLocation !== undefined &&
658
+ !table.baseFieldToContext.has(fieldChangeFromId(table.baseChange, baseDetachLocation))) {
659
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(baseDetachLocation), true);
660
+ }
661
+ }
662
+ else {
663
+ // The base change inserts this node into `attachField`, so the rebased change should represent this node there.
664
+ const normalizedAttachField = normalizeFieldId(attachField, table.baseChange.nodeAliases);
665
+ const entry = table.entries.getFirst(renamedRoot, 1).value ?? {};
666
+ table.entries.set(renamedRoot, 1, { ...entry, nodeChange: baseNodeId });
667
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(normalizedAttachField), true);
668
+ this.attachRebasedNode(rebasedFields, rebasedNodes, table, baseNodeId, { field: normalizedAttachField }, idAllocator, metadata);
669
+ }
670
+ return;
671
+ }
672
+ const parentFieldIdBase = parentBase.field;
673
+ const baseFieldChange = fieldChangeFromId(table.baseChange, parentFieldIdBase);
600
674
  const rebasedFieldId = rebasedFieldIdFromBaseId(table, parentFieldIdBase);
601
- setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, rebasedFieldId);
675
+ setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, { field: rebasedFieldId });
602
676
  const context = table.baseFieldToContext.get(baseFieldChange);
603
677
  if (context !== undefined) {
604
678
  // We've already processed this field.
605
- // The new child node will be attached in rebaseFieldsWithUnattachedChild.
606
- context.baseNodeIds.push(baseNodeId);
607
- table.fieldsWithUnattachedChild.add(baseFieldChange);
679
+ // The new child node will be attached in the next pass.
680
+ // Note that adding to `fieldsWithUnattachedChild` allows that field to generate new invalidations,
681
+ // so to avoid invalidation cycles we make sure we only add to it once per new unattached child.
682
+ // This is done by checking whether `context.baseNodeIds` already contained `baseNodeId`.
683
+ if (setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true)) {
684
+ table.fieldsWithUnattachedChild.add(baseFieldChange);
685
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(parentFieldIdBase), true);
686
+ }
608
687
  return;
609
688
  }
610
- const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
611
- const fieldChange = {
612
- ...baseFieldChange,
613
- change: brand(handler.createEmpty()),
614
- };
615
- const rebasedChangeset = handler.rebaser.rebase(handler.createEmpty(), baseFieldChange.change, (_idNew, idBase) => idBase !== undefined && areEqualChangeAtomIds(idBase, baseNodeId)
616
- ? baseNodeId
617
- : undefined, idAllocator, new RebaseManager(table, baseFieldChange, rebasedFieldId), metadata);
618
- const rebasedField = { ...baseFieldChange, change: brand(rebasedChangeset) };
619
- table.rebasedFields.add(rebasedField);
620
- table.baseFieldToContext.set(baseFieldChange, {
621
- newChange: fieldChange,
622
- baseChange: baseFieldChange,
623
- rebasedChange: rebasedField,
624
- fieldId: rebasedFieldId,
625
- baseNodeIds: [],
626
- });
627
- this.attachRebasedField(rebasedFields, rebasedNodes, table, rebasedField, rebasedFieldId, idAllocator, metadata);
689
+ this.rebaseFieldWithoutNewChanges(baseFieldChange, parentFieldIdBase, table, rebasedFields, rebasedNodes, idAllocator, metadata, baseNodeId);
628
690
  }
629
691
  rebaseFieldMap(change, over, parentId, genId, crossFieldTable, revisionMetadata) {
630
692
  const rebasedFields = new Map();
@@ -642,8 +704,8 @@ export class ModularChangeFamily {
642
704
  continue;
643
705
  }
644
706
  const { fieldKind, changeHandler, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(fieldChange, baseChange);
645
- const manager = new RebaseManager(crossFieldTable, baseChange, fieldId);
646
- const rebasedField = changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, manager, revisionMetadata);
707
+ const manager = new RebaseNodeManagerI(crossFieldTable, fieldId);
708
+ const rebasedField = changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, manager, revisionMetadata, crossFieldTable.rebaseVersion);
647
709
  const rebasedFieldChange = {
648
710
  fieldKind,
649
711
  change: brand(rebasedField),
@@ -654,15 +716,15 @@ export class ModularChangeFamily {
654
716
  newChange: fieldChange,
655
717
  rebasedChange: rebasedFieldChange,
656
718
  fieldId,
657
- baseNodeIds: [],
719
+ baseNodeIds: newTupleBTree(),
658
720
  });
659
721
  crossFieldTable.rebasedFields.add(rebasedFieldChange);
660
722
  }
661
723
  return rebasedFields;
662
724
  }
663
725
  rebaseNodeChange(newId, baseId, genId, crossFieldTable, revisionMetadata) {
664
- const change = nodeChangeFromId(crossFieldTable.newChange.nodeChanges, newId);
665
- const over = nodeChangeFromId(crossFieldTable.baseChange.nodeChanges, baseId);
726
+ const change = nodeChangeFromId(crossFieldTable.newChange.nodeChanges, crossFieldTable.newChange.nodeAliases, newId);
727
+ const over = nodeChangeFromId(crossFieldTable.baseChange.nodeChanges, crossFieldTable.baseChange.nodeAliases, baseId);
666
728
  const baseMap = over?.fieldChanges ?? new Map();
667
729
  const fieldChanges = change.fieldChanges !== undefined && over.fieldChanges !== undefined
668
730
  ? this.rebaseFieldMap(change?.fieldChanges ?? new Map(), baseMap, newId, genId, crossFieldTable, revisionMetadata)
@@ -680,28 +742,37 @@ export class ModularChangeFamily {
680
742
  setInChangeAtomIdMap(crossFieldTable.baseToRebasedNodeId, baseId, newId);
681
743
  return rebasedChange;
682
744
  }
745
+ updateConstraints(rebasedFields, rebasedNodes, rebasedRoots, constraintState, revertConstraintState) {
746
+ this.updateConstraintsForFields(rebasedFields, NodeAttachState.Attached, NodeAttachState.Attached, constraintState, revertConstraintState, rebasedNodes);
747
+ for (const [_detachId, nodeId] of rebasedRoots.nodeChanges.entries()) {
748
+ // XXX: This is incorrect if the rebased changeset attaches the node.
749
+ // Efficiently computing whether the changeset attaches the node would require maintaining a mapping from node ID to attach ID.
750
+ const detachedInOutput = true;
751
+ this.updateConstraintsForNode(nodeId, NodeAttachState.Detached, detachedInOutput ? NodeAttachState.Detached : NodeAttachState.Attached, rebasedNodes, constraintState, revertConstraintState);
752
+ }
753
+ }
683
754
  updateConstraintsForFields(fields, parentInputAttachState, parentOutputAttachState, constraintState, revertConstraintState, nodes) {
684
755
  for (const field of fields.values()) {
685
756
  const handler = getChangeHandler(this.fieldKinds, field.fieldKind);
686
- for (const [nodeId, inputIndex, outputIndex] of handler.getNestedChanges(field.change)) {
687
- const isInputDetached = inputIndex === undefined;
688
- const inputAttachState = parentInputAttachState === NodeAttachState.Detached || isInputDetached
689
- ? NodeAttachState.Detached
690
- : NodeAttachState.Attached;
691
- const isOutputDetached = outputIndex === undefined;
757
+ for (const [nodeId] of handler.getNestedChanges(field.change)) {
758
+ // XXX: This is incorrect if the rebased changeset detaches this node.
759
+ // Efficiently computing whether the changeset detaches the node would require maintaining a mapping from node ID to detach ID.
760
+ const isOutputDetached = false;
692
761
  const outputAttachState = parentOutputAttachState === NodeAttachState.Detached || isOutputDetached
693
762
  ? NodeAttachState.Detached
694
763
  : NodeAttachState.Attached;
695
- this.updateConstraintsForNode(nodeId, inputAttachState, outputAttachState, nodes, constraintState, revertConstraintState);
764
+ this.updateConstraintsForNode(nodeId, parentInputAttachState, outputAttachState, nodes, constraintState, revertConstraintState);
696
765
  }
697
766
  }
698
767
  }
699
768
  updateConstraintsForNode(nodeId, inputAttachState, outputAttachState, nodes, constraintState, revertConstraintState) {
700
- const node = nodes.get([nodeId.revision, nodeId.localId]) ?? fail(0xb24 /* Unknown node ID */);
769
+ const node = getFromChangeAtomIdMap(nodes, nodeId) ?? fail(0xb24 /* Unknown node ID */);
770
+ const updatedNode = { ...node };
771
+ setInChangeAtomIdMap(nodes, nodeId, updatedNode);
701
772
  if (node.nodeExistsConstraint !== undefined) {
702
773
  const isNowViolated = inputAttachState === NodeAttachState.Detached;
703
774
  if (node.nodeExistsConstraint.violated !== isNowViolated) {
704
- node.nodeExistsConstraint = {
775
+ updatedNode.nodeExistsConstraint = {
705
776
  ...node.nodeExistsConstraint,
706
777
  violated: isNowViolated,
707
778
  };
@@ -711,7 +782,7 @@ export class ModularChangeFamily {
711
782
  if (node.nodeExistsConstraintOnRevert !== undefined) {
712
783
  const isNowViolated = outputAttachState === NodeAttachState.Detached;
713
784
  if (node.nodeExistsConstraintOnRevert.violated !== isNowViolated) {
714
- node.nodeExistsConstraintOnRevert = {
785
+ updatedNode.nodeExistsConstraintOnRevert = {
715
786
  ...node.nodeExistsConstraintOnRevert,
716
787
  violated: isNowViolated,
717
788
  };
@@ -722,41 +793,82 @@ export class ModularChangeFamily {
722
793
  this.updateConstraintsForFields(node.fieldChanges, inputAttachState, outputAttachState, constraintState, revertConstraintState, nodes);
723
794
  }
724
795
  }
725
- pruneFieldMap(changeset, nodeMap) {
796
+ pruneFieldMap(changeset, parentId, nodeMap, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges) {
726
797
  if (changeset === undefined) {
727
798
  return undefined;
728
799
  }
729
800
  const prunedChangeset = new Map();
730
801
  for (const [field, fieldChange] of changeset) {
731
802
  const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
732
- const prunedFieldChangeset = handler.rebaser.prune(fieldChange.change, (nodeId) => this.pruneNodeChange(nodeId, nodeMap));
733
- if (!handler.isEmpty(prunedFieldChangeset)) {
803
+ const prunedFieldChangeset = handler.rebaser.prune(fieldChange.change, (nodeId) => this.pruneNodeChange(nodeId, nodeMap, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges));
804
+ const fieldId = { nodeId: parentId, field };
805
+ const fieldIdKey = fieldIdKeyFromFieldId(fieldId);
806
+ const rootsWithChanges = fieldsToRootChanges.get(fieldIdKey) ?? [];
807
+ let hasRootWithNodeChange = false;
808
+ for (const rootId of rootsWithChanges) {
809
+ const nodeId = getFromChangeAtomIdMap(roots.nodeChanges, rootId) ?? fail("No root change found");
810
+ const isRootChangeEmpty = this.pruneNodeChange(nodeId, nodeMap, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges) === undefined;
811
+ if (isRootChangeEmpty) {
812
+ roots.nodeChanges.delete([rootId.revision, rootId.localId]);
813
+ tryRemoveDetachLocation(roots, rootId, 1);
814
+ }
815
+ else {
816
+ hasRootWithNodeChange = true;
817
+ }
818
+ }
819
+ const hasRootChanges = hasRootWithNodeChange || fieldsWithRootMoves.get(fieldIdKey) === true;
820
+ if (!handler.isEmpty(prunedFieldChangeset) || hasRootChanges) {
734
821
  prunedChangeset.set(field, { ...fieldChange, change: brand(prunedFieldChangeset) });
735
822
  }
736
823
  }
737
824
  return prunedChangeset.size > 0 ? prunedChangeset : undefined;
738
825
  }
739
- pruneNodeChange(nodeId, nodeMap) {
740
- const changeset = nodeChangeFromId(nodeMap, nodeId);
826
+ pruneRoots(roots, nodeMap, nodeToParent, aliases, fieldsWithRootMoves, fieldsToRootChanges) {
827
+ const pruned = { ...roots, nodeChanges: newTupleBTree() };
828
+ for (const [rootIdKey, nodeId] of roots.nodeChanges.entries()) {
829
+ const rootId = { revision: rootIdKey[0], localId: rootIdKey[1] };
830
+ const hasDetachLocation = roots.detachLocations.getFirst(rootId, 1).value !== undefined;
831
+ // If the root has a detach location it should be pruned by recursion when pruning the field it was detached from.
832
+ const prunedId = hasDetachLocation
833
+ ? nodeId
834
+ : this.pruneNodeChange(nodeId, nodeMap, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges);
835
+ if (prunedId !== undefined) {
836
+ pruned.nodeChanges.set(rootIdKey, prunedId);
837
+ }
838
+ tryRemoveDetachLocation(pruned, rootId, 1);
839
+ }
840
+ return pruned;
841
+ }
842
+ pruneNodeChange(nodeId, nodes, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges) {
843
+ const changeset = nodeChangeFromId(nodes, aliases, nodeId);
741
844
  const prunedFields = changeset.fieldChanges === undefined
742
845
  ? undefined
743
- : this.pruneFieldMap(changeset.fieldChanges, nodeMap);
846
+ : this.pruneFieldMap(changeset.fieldChanges, nodeId, nodes, nodeToParent, aliases, roots, fieldsWithRootMoves, fieldsToRootChanges);
744
847
  const prunedChange = { ...changeset, fieldChanges: prunedFields };
745
848
  if (prunedChange.fieldChanges === undefined) {
746
849
  delete prunedChange.fieldChanges;
747
850
  }
748
851
  if (isEmptyNodeChangeset(prunedChange)) {
749
- nodeMap.delete([nodeId.revision, nodeId.localId]);
852
+ const nodeIdKey = [
853
+ nodeId.revision,
854
+ nodeId.localId,
855
+ ];
856
+ // TODO: Shouldn't we also delete all aliases associated with this node?
857
+ nodes.delete(nodeIdKey);
858
+ nodeToParent.delete(nodeIdKey);
750
859
  return undefined;
751
860
  }
752
861
  else {
753
- setInChangeAtomIdMap(nodeMap, nodeId, prunedChange);
862
+ setInChangeAtomIdMap(nodes, nodeId, prunedChange);
754
863
  return nodeId;
755
864
  }
756
865
  }
757
866
  getRevisions(change) {
867
+ if (change.revisions === undefined || change.revisions.length === 0) {
868
+ return new Set([undefined]);
869
+ }
758
870
  const aggregated = new Set();
759
- for (const revInfo of change.revisions ?? [{ revision: undefined }]) {
871
+ for (const revInfo of change.revisions) {
760
872
  aggregated.add(revInfo.revision);
761
873
  }
762
874
  return aggregated;
@@ -764,12 +876,13 @@ export class ModularChangeFamily {
764
876
  changeRevision(change, replacer) {
765
877
  const updatedFields = this.replaceFieldMapRevisions(change.fieldChanges, replacer);
766
878
  const updatedNodes = replaceIdMapRevisions(change.nodeChanges, replacer, (nodeChangeset) => this.replaceNodeChangesetRevisions(nodeChangeset, replacer));
767
- const updatedNodeToParent = replaceIdMapRevisions(change.nodeToParent, replacer, (fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, change.nodeAliases), replacer));
879
+ const updatedNodeToParent = replaceIdMapRevisions(change.nodeToParent, replacer, (location) => replaceNodeLocationRevision(normalizeNodeLocation(location, change.nodeAliases), replacer));
768
880
  const updated = {
769
881
  ...change,
770
882
  fieldChanges: updatedFields,
771
883
  nodeChanges: updatedNodes,
772
884
  nodeToParent: updatedNodeToParent,
885
+ rootNodes: replaceRootTableRevision(change.rootNodes, replacer, change.nodeAliases),
773
886
  // We've updated all references to old node IDs, so we no longer need an alias table.
774
887
  nodeAliases: newTupleBTree(),
775
888
  crossFieldKeys: replaceCrossFieldKeyTableRevisions(change.crossFieldKeys, replacer, change.nodeAliases),
@@ -802,7 +915,7 @@ export class ModularChangeFamily {
802
915
  return updatedFields;
803
916
  }
804
917
  makeCrossFieldKeyTable(fields, nodes) {
805
- const keys = newCrossFieldKeyTable();
918
+ const keys = newCrossFieldRangeTable();
806
919
  this.populateCrossFieldKeyTableForFieldMap(keys, fields, undefined);
807
920
  nodes.forEachPair(([revision, localId], node) => {
808
921
  if (node.fieldChanges !== undefined) {
@@ -829,42 +942,6 @@ export class ModularChangeFamily {
829
942
  const emptyChange = getChangeHandler(this.fieldKinds, fieldKind).createEmpty();
830
943
  return { fieldKind, change: brand(emptyChange) };
831
944
  }
832
- validateChangeset(change) {
833
- let numNodes = this.validateFieldChanges(change, change.fieldChanges, undefined);
834
- for (const [[revision, localId], node] of change.nodeChanges.entries()) {
835
- if (node.fieldChanges === undefined) {
836
- continue;
837
- }
838
- const nodeId = { revision, localId };
839
- const numChildren = this.validateFieldChanges(change, node.fieldChanges, nodeId);
840
- numNodes += numChildren;
841
- }
842
- assert(numNodes === change.nodeChanges.size, 0xa4d /* Node table contains unparented nodes */);
843
- }
844
- /**
845
- * Asserts that each child and cross field key in each field has a correct entry in
846
- * `nodeToParent` or `crossFieldKeyTable`.
847
- * @returns the number of children found.
848
- */
849
- validateFieldChanges(change, fieldChanges, nodeParent) {
850
- let numChildren = 0;
851
- for (const [field, fieldChange] of fieldChanges.entries()) {
852
- const fieldId = { nodeId: nodeParent, field };
853
- const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
854
- for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
855
- const parentFieldId = getParentFieldId(change, child);
856
- assert(areEqualFieldIds(parentFieldId, fieldId), 0xa4e /* Inconsistent node parentage */);
857
- numChildren += 1;
858
- }
859
- for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
860
- const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
861
- assert(fields.length === 1 &&
862
- fields[0] !== undefined &&
863
- areEqualFieldIds(fields[0], fieldId), 0xa4f /* Inconsistent cross field keys */);
864
- }
865
- }
866
- return numChildren;
867
- }
868
945
  getEffectiveChange(change) {
869
946
  if (hasConflicts(change)) {
870
947
  return this.muteChange(change);
@@ -877,7 +954,8 @@ export class ModularChangeFamily {
877
954
  muteChange(change) {
878
955
  const muted = {
879
956
  ...change,
880
- crossFieldKeys: newCrossFieldKeyTable(),
957
+ rootNodes: muteRootChanges(change.rootNodes),
958
+ crossFieldKeys: newCrossFieldRangeTable(),
881
959
  fieldChanges: this.muteFieldChanges(change.fieldChanges),
882
960
  nodeChanges: brand(change.nodeChanges.mapValues((v) => this.muteNodeChange(v))),
883
961
  };
@@ -905,7 +983,7 @@ export class ModularChangeFamily {
905
983
  }
906
984
  ModularChangeFamily.emptyChange = makeModularChangeset();
907
985
  function replaceCrossFieldKeyTableRevisions(table, replacer, nodeAliases) {
908
- const updated = newCrossFieldKeyTable();
986
+ const updated = newCrossFieldRangeTable();
909
987
  for (const entry of table.entries()) {
910
988
  const key = entry.start;
911
989
  const updatedKey = replacer.getUpdatedAtomId(key);
@@ -964,6 +1042,19 @@ function composeBuildsDestroysAndRefreshers(change1, change2) {
964
1042
  }
965
1043
  }
966
1044
  }
1045
+ // It's possible to have a build and a refresher for the same root because an attach operation need not be performed in the same changeset as the corresponding build.
1046
+ if (change1.builds !== undefined && change2.refreshers !== undefined) {
1047
+ for (const [key, chunk] of change2.refreshers.entries()) {
1048
+ assert(chunk.topLevelLength === 1, "Expected refresher chunk to have length 1");
1049
+ const match = change1.builds.getPairOrNextLower(key);
1050
+ if (match !== undefined) {
1051
+ const [buildKey, buildChunk] = match;
1052
+ if (buildKey[0] === key[0] && buildKey[1] + buildChunk.topLevelLength > key[1]) {
1053
+ allRefreshers.delete(key);
1054
+ }
1055
+ }
1056
+ }
1057
+ }
967
1058
  return { allBuilds, allDestroys, allRefreshers };
968
1059
  }
969
1060
  function invertBuilds(builds) {
@@ -988,18 +1079,51 @@ function invertBuilds(builds) {
988
1079
  * @param fieldKinds - The field kinds to delegate to.
989
1080
  */
990
1081
  export function* relevantRemovedRoots(change, fieldKinds) {
991
- yield* relevantRemovedRootsFromFields(change.fieldChanges, change.nodeChanges, fieldKinds);
1082
+ const rootIds = newChangeAtomIdRangeMap();
1083
+ addAttachesToSet(change, rootIds);
1084
+ addRenamesToSet(change, rootIds);
1085
+ for (const [[revision, localId]] of change.rootNodes.nodeChanges.entries()) {
1086
+ rootIds.set({ revision, localId }, 1, true);
1087
+ }
1088
+ for (const entry of rootIds.entries()) {
1089
+ for (let offset = 0; offset < entry.length; offset++) {
1090
+ const detachId = offsetChangeAtomId(entry.start, offset);
1091
+ yield makeDetachedNodeId(detachId.revision, detachId.localId);
1092
+ }
1093
+ }
992
1094
  }
993
- function* relevantRemovedRootsFromFields(change, nodeChanges, fieldKinds) {
994
- for (const [_, fieldChange] of change) {
995
- const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
996
- const delegate = function* (node) {
997
- const nodeChangeset = nodeChangeFromId(nodeChanges, node);
998
- if (nodeChangeset.fieldChanges !== undefined) {
999
- yield* relevantRemovedRootsFromFields(nodeChangeset.fieldChanges, nodeChanges, fieldKinds);
1095
+ export function* getBuildIds(change) {
1096
+ if (change.builds !== undefined) {
1097
+ for (const [[revision, localId]] of change.builds.entries()) {
1098
+ yield makeDetachedNodeId(revision, localId);
1099
+ }
1100
+ }
1101
+ }
1102
+ function addAttachesToSet(change, rootIds) {
1103
+ // This includes each attach which does not have a corresponding detach.
1104
+ for (const entry of change.crossFieldKeys.entries()) {
1105
+ if (entry.start.target !== CrossFieldTarget.Destination) {
1106
+ continue;
1107
+ }
1108
+ for (const detachIdEntry of change.rootNodes.newToOldId.getAll2(entry.start, entry.length)) {
1109
+ const detachId = detachIdEntry.value ?? offsetChangeAtomId(entry.start, detachIdEntry.offset);
1110
+ for (const detachEntry of change.crossFieldKeys.getAll2({ ...detachId, target: CrossFieldTarget.Source }, detachIdEntry.length)) {
1111
+ if (detachEntry.value === undefined) {
1112
+ rootIds.set(offsetChangeAtomId(detachId, detachEntry.offset), detachEntry.length, true);
1113
+ }
1000
1114
  }
1001
- };
1002
- yield* handler.relevantRemovedRoots(fieldChange.change, delegate);
1115
+ }
1116
+ }
1117
+ }
1118
+ function addRenamesToSet(change, rootIds) {
1119
+ for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
1120
+ for (const detachEntry of change.crossFieldKeys.getAll2({ ...renameEntry.start, target: CrossFieldTarget.Source }, renameEntry.length)) {
1121
+ // We only want to include renames of nodes which are detached in the input context of the changeset.
1122
+ // So if there is a detach for the node, the rename is not relevant.
1123
+ if (detachEntry.value === undefined) {
1124
+ rootIds.set(renameEntry.start, renameEntry.length, true);
1125
+ }
1126
+ }
1003
1127
  }
1004
1128
  }
1005
1129
  /**
@@ -1045,13 +1169,14 @@ export function updateRefreshers(change, getDetachedNode, removedRoots, requireR
1045
1169
  refreshers.set([root.major, brand(root.minor)], node);
1046
1170
  }
1047
1171
  }
1048
- const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, maxId, revisions, constraintViolationCount, constraintViolationCountOnRevert, builds, destroys, } = change;
1172
+ const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, maxId, revisions, constraintViolationCount, constraintViolationCountOnRevert, builds, destroys, rootNodes, } = change;
1049
1173
  return makeModularChangeset({
1050
1174
  fieldChanges,
1051
1175
  nodeChanges,
1052
1176
  nodeToParent,
1053
1177
  nodeAliases,
1054
1178
  crossFieldKeys,
1179
+ rootNodes,
1055
1180
  maxId: maxId,
1056
1181
  revisions,
1057
1182
  constraintViolationCount,
@@ -1070,11 +1195,24 @@ export function updateRefreshers(change, getDetachedNode, removedRoots, requireR
1070
1195
  export function intoDelta(taggedChange, fieldKinds) {
1071
1196
  const change = taggedChange.change;
1072
1197
  const rootDelta = {};
1073
- const global = [];
1074
- const rename = [];
1075
1198
  if (!hasConflicts(change)) {
1076
1199
  // If there are no constraint violations, then tree changes apply.
1077
- const fieldDeltas = intoDeltaImpl(change.fieldChanges, change.nodeChanges, fieldKinds, global, rename);
1200
+ const fieldDeltas = intoDeltaImpl(change.fieldChanges, change.nodeChanges, change.nodeAliases, fieldKinds);
1201
+ const global = [];
1202
+ for (const [[major, minor], nodeId] of change.rootNodes.nodeChanges.entries()) {
1203
+ global.push({
1204
+ id: { major, minor },
1205
+ fields: deltaFromNodeChange(nodeChangeFromId(change.nodeChanges, change.nodeAliases, nodeId), change.nodeChanges, change.nodeAliases, fieldKinds),
1206
+ });
1207
+ }
1208
+ const rename = [];
1209
+ for (const { start: oldId, value: newId, length, } of change.rootNodes.oldToNewId.entries()) {
1210
+ rename.push({
1211
+ count: length,
1212
+ oldId: makeDetachedNodeId(oldId.revision, oldId.localId),
1213
+ newId: makeDetachedNodeId(newId.revision, newId.localId),
1214
+ });
1215
+ }
1078
1216
  if (fieldDeltas.size > 0) {
1079
1217
  rootDelta.fields = fieldDeltas;
1080
1218
  }
@@ -1120,24 +1258,22 @@ function copyDetachedNodes(detachedNodes) {
1120
1258
  /**
1121
1259
  * @param change - The change to convert into a delta.
1122
1260
  */
1123
- function intoDeltaImpl(change, nodeChanges, fieldKinds, global, rename) {
1261
+ function intoDeltaImpl(change, nodeChanges, nodeAliases, fieldKinds) {
1124
1262
  const delta = new Map();
1125
1263
  for (const [field, fieldChange] of change) {
1126
- const { local: fieldChanges, global: fieldGlobal, rename: fieldRename, } = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(fieldChange.change, (childChange) => {
1127
- const nodeChange = nodeChangeFromId(nodeChanges, childChange);
1128
- return deltaFromNodeChange(nodeChange, nodeChanges, fieldKinds, global, rename);
1264
+ const fieldDelta = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(fieldChange.change, (childChange) => {
1265
+ const nodeChange = nodeChangeFromId(nodeChanges, nodeAliases, childChange);
1266
+ return deltaFromNodeChange(nodeChange, nodeChanges, nodeAliases, fieldKinds);
1129
1267
  });
1130
- if (fieldChanges !== undefined && fieldChanges.length > 0) {
1131
- delta.set(field, fieldChanges);
1268
+ if (fieldDelta !== undefined && fieldDelta.length > 0) {
1269
+ delta.set(field, fieldDelta);
1132
1270
  }
1133
- fieldGlobal?.forEach((c) => global.push(c));
1134
- fieldRename?.forEach((r) => rename.push(r));
1135
1271
  }
1136
1272
  return delta;
1137
1273
  }
1138
- function deltaFromNodeChange(change, nodeChanges, fieldKinds, global, rename) {
1274
+ function deltaFromNodeChange(change, nodeChanges, nodeAliases, fieldKinds) {
1139
1275
  if (change.fieldChanges !== undefined) {
1140
- return intoDeltaImpl(change.fieldChanges, nodeChanges, fieldKinds, global, rename);
1276
+ return intoDeltaImpl(change.fieldChanges, nodeChanges, nodeAliases, fieldKinds);
1141
1277
  }
1142
1278
  // TODO: update the API to allow undefined to be returned here
1143
1279
  return new Map();
@@ -1184,30 +1320,22 @@ export function getFieldKind(fieldKinds, kind) {
1184
1320
  export function getChangeHandler(fieldKinds, kind) {
1185
1321
  return getFieldKind(fieldKinds, kind).changeHandler;
1186
1322
  }
1187
- function newComposeTable(baseChange, newChange, composedNodeToParent) {
1323
+ function newComposeTable(baseChange, newChange, composedRootNodes, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions) {
1188
1324
  return {
1189
- ...newCrossFieldTable(),
1325
+ rebaseVersion: Math.max(baseChange.rebaseVersion, newChange.rebaseVersion),
1326
+ entries: newDetachedEntryMap(),
1190
1327
  baseChange,
1191
1328
  newChange,
1192
1329
  fieldToContext: new Map(),
1193
1330
  newFieldToBaseField: new Map(),
1194
1331
  newToBaseNodeId: newTupleBTree(),
1195
1332
  composedNodes: new Set(),
1196
- composedNodeToParent,
1197
- pendingCompositions: {
1198
- nodeIdsToCompose: [],
1199
- affectedBaseFields: newTupleBTree(),
1200
- affectedNewFields: newTupleBTree(),
1201
- },
1202
- };
1203
- }
1204
- function newCrossFieldTable() {
1205
- return {
1206
- srcTable: newChangeAtomIdRangeMap(),
1207
- dstTable: newChangeAtomIdRangeMap(),
1208
- srcDependents: newChangeAtomIdRangeMap(),
1209
- dstDependents: newChangeAtomIdRangeMap(),
1210
- invalidatedFields: new Set(),
1333
+ movedNodeToParent: newTupleBTree(),
1334
+ composedRootNodes,
1335
+ movedCrossFieldKeys,
1336
+ removedCrossFieldKeys,
1337
+ renamesToDelete: newChangeAtomIdRangeMap(),
1338
+ pendingCompositions,
1211
1339
  };
1212
1340
  }
1213
1341
  function newConstraintState(violationCount) {
@@ -1215,175 +1343,381 @@ function newConstraintState(violationCount) {
1215
1343
  violationCount,
1216
1344
  };
1217
1345
  }
1218
- class CrossFieldManagerI {
1219
- constructor(crossFieldTable, currentFieldKey, allowInval = true) {
1220
- this.crossFieldTable = crossFieldTable;
1221
- this.currentFieldKey = currentFieldKey;
1222
- this.allowInval = allowInval;
1346
+ class InvertNodeManagerI {
1347
+ constructor(table, fieldId) {
1348
+ this.table = table;
1349
+ this.fieldId = fieldId;
1223
1350
  }
1224
- set(target, revision, id, count, newValue, invalidateDependents) {
1225
- if (invalidateDependents && this.allowInval) {
1226
- const lastChangedId = id + count - 1;
1227
- let firstId = id;
1228
- while (firstId <= lastChangedId) {
1229
- const dependentEntry = getFirstFromCrossFieldMap(this.getDependents(target), revision, firstId, lastChangedId - firstId + 1);
1230
- if (dependentEntry.value !== undefined) {
1231
- this.crossFieldTable.invalidatedFields.add(dependentEntry.value);
1351
+ invertDetach(detachId, count, nodeChange, newAttachId) {
1352
+ if (nodeChange !== undefined) {
1353
+ assert(count === 1, "A node change should only affect one node");
1354
+ const attachEntry = firstAttachIdFromDetachId(this.table.change.rootNodes, detachId, count);
1355
+ const attachFieldEntry = this.table.change.crossFieldKeys.getFirst({ target: CrossFieldTarget.Destination, ...attachEntry.value }, count);
1356
+ if (attachFieldEntry.value === undefined) {
1357
+ assignRootChange(this.table.invertedRoots, this.table.invertedNodeToParent, attachEntry.value, nodeChange, this.fieldId, this.table.change.rebaseVersion);
1358
+ }
1359
+ else {
1360
+ setInCrossFieldMap(this.table.entries, attachEntry.value, count, nodeChange);
1361
+ this.table.invalidatedFields.add(fieldChangeFromId(this.table.change, attachFieldEntry.value));
1362
+ }
1363
+ }
1364
+ if (!areEqualChangeAtomIds(detachId, newAttachId)) {
1365
+ for (const entry of doesChangeAttachNodes(this.table.change.crossFieldKeys, detachId, count)) {
1366
+ if (!entry.value) {
1367
+ this.table.attachToDetachId.set(newAttachId, count, detachId);
1368
+ this.table.invertedRoots.detachLocations.set(detachId, count, this.fieldId);
1232
1369
  }
1233
- firstId = brand(firstId + dependentEntry.length);
1234
1370
  }
1235
1371
  }
1236
- setInCrossFieldMap(this.getMap(target), revision, id, count, newValue);
1237
1372
  }
1238
- get(target, revision, id, count, addDependency) {
1239
- if (addDependency) {
1240
- // We assume that if there is already an entry for this ID it is because
1241
- // a field handler has called compose on the same node multiple times.
1242
- // In this case we only want to update the latest version, so we overwrite the dependency.
1243
- setInCrossFieldMap(this.getDependents(target), revision, id, count, this.currentFieldKey);
1373
+ invertAttach(attachId, count) {
1374
+ let countToProcess = count;
1375
+ const detachIdEntry = firstDetachIdFromAttachId(this.table.change.rootNodes, attachId, countToProcess);
1376
+ countToProcess = detachIdEntry.length;
1377
+ const detachEntry = getFirstFieldForCrossFieldKey(this.table.change, { target: CrossFieldTarget.Source, ...detachIdEntry.value }, countToProcess);
1378
+ countToProcess = detachEntry.length;
1379
+ let result;
1380
+ if (detachEntry.value === undefined) {
1381
+ // This node is detached in the input context of the original change.
1382
+ const nodeIdEntry = rangeQueryChangeAtomIdMap(this.table.change.rootNodes.nodeChanges, detachIdEntry.value, countToProcess);
1383
+ countToProcess = nodeIdEntry.length;
1384
+ result = {
1385
+ value: { nodeChange: nodeIdEntry.value, detachId: detachIdEntry.value },
1386
+ length: countToProcess,
1387
+ };
1244
1388
  }
1245
- return getFirstFromCrossFieldMap(this.getMap(target), revision, id, count);
1246
- }
1247
- getMap(target) {
1248
- return target === CrossFieldTarget.Source
1249
- ? this.crossFieldTable.srcTable
1250
- : this.crossFieldTable.dstTable;
1251
- }
1252
- getDependents(target) {
1253
- return target === CrossFieldTarget.Source
1254
- ? this.crossFieldTable.srcDependents
1255
- : this.crossFieldTable.dstDependents;
1256
- }
1257
- }
1258
- class InvertManager extends CrossFieldManagerI {
1259
- constructor(table, field, fieldId, allowInval = true) {
1260
- super(table, field, allowInval);
1261
- this.fieldId = fieldId;
1262
- }
1263
- onMoveIn(id) {
1264
- setInChangeAtomIdMap(this.table.invertedNodeToParent, id, this.fieldId);
1265
- }
1266
- moveKey(target, revision, id, count) {
1267
- assert(false, 0x9c5 /* Keys should not be moved manually during invert */);
1268
- }
1269
- get table() {
1270
- return this.crossFieldTable;
1389
+ else {
1390
+ const moveEntry = this.table.entries.getFirst(attachId, countToProcess);
1391
+ result = { ...moveEntry, value: { nodeChange: moveEntry.value } };
1392
+ }
1393
+ if (result.value?.nodeChange !== undefined) {
1394
+ setInChangeAtomIdMap(this.table.invertedNodeToParent, result.value.nodeChange, {
1395
+ field: this.fieldId,
1396
+ });
1397
+ }
1398
+ return result;
1271
1399
  }
1272
1400
  }
1273
- class RebaseManager extends CrossFieldManagerI {
1274
- constructor(table, currentField, fieldId, allowInval = true) {
1275
- super(table, currentField, allowInval);
1401
+ class RebaseNodeManagerI {
1402
+ constructor(table, fieldId, allowInval = true) {
1403
+ this.table = table;
1276
1404
  this.fieldId = fieldId;
1405
+ this.allowInval = allowInval;
1277
1406
  }
1278
- set(target, revision, id, count, newValue, invalidateDependents) {
1279
- if (invalidateDependents && this.allowInval) {
1280
- const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, {
1281
- target,
1282
- revision,
1283
- localId: id,
1284
- }, count);
1285
- assert(newFieldIds.length === 0, 0x9c6 /* TODO: Modifying a cross-field key from the new changeset is currently unsupported */);
1286
- const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, {
1287
- target,
1288
- revision,
1289
- localId: id,
1290
- }, count);
1291
- assert(baseFieldIds.length > 0, 0x9c7 /* Cross field key not registered in base or new change */);
1292
- for (const baseFieldId of baseFieldIds) {
1293
- this.table.affectedBaseFields.set([baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field], true);
1407
+ getNewChangesForBaseAttach(baseAttachId, count) {
1408
+ let countToProcess = count;
1409
+ const detachEntry = firstDetachIdFromAttachId(this.table.baseChange.rootNodes, baseAttachId, countToProcess);
1410
+ countToProcess = detachEntry.length;
1411
+ const nodeEntry = rangeQueryChangeAtomIdMap(this.table.newChange.rootNodes.nodeChanges, detachEntry.value, countToProcess);
1412
+ countToProcess = nodeEntry.length;
1413
+ const newNodeId = nodeEntry.value;
1414
+ const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(detachEntry.value, countToProcess);
1415
+ countToProcess = newRenameEntry.length;
1416
+ let result;
1417
+ // eslint-disable-next-line unicorn/prefer-ternary
1418
+ if (newNodeId !== undefined || newRenameEntry.value !== undefined) {
1419
+ result = {
1420
+ ...newRenameEntry,
1421
+ value: { detachId: newRenameEntry.value, nodeChange: newNodeId },
1422
+ };
1423
+ }
1424
+ else {
1425
+ // This handles the case where the base changeset has moved these nodes,
1426
+ // meaning they were attached in the input context of the base changeset.
1427
+ result = this.table.entries.getFirst(baseAttachId, countToProcess);
1428
+ }
1429
+ // TODO: Consider moving these two checks into a separate method so that this function has no side effects.
1430
+ if (result.value?.detachId !== undefined) {
1431
+ this.table.rebasedDetachLocations.set(result.value.detachId, result.length, this.fieldId);
1432
+ }
1433
+ if (result.value?.nodeChange !== undefined) {
1434
+ setInChangeAtomIdMap(this.table.rebasedNodeToParent, result.value.nodeChange, {
1435
+ field: this.fieldId,
1436
+ });
1437
+ }
1438
+ return result;
1439
+ }
1440
+ rebaseOverDetach(baseDetachId, count, newDetachId, nodeChange, cellRename) {
1441
+ let countToProcess = count;
1442
+ const attachIdEntry = firstAttachIdFromDetachId(this.table.baseRoots, baseDetachId, countToProcess);
1443
+ const baseAttachId = attachIdEntry.value;
1444
+ countToProcess = attachIdEntry.length;
1445
+ const attachFieldEntry = getFirstFieldForCrossFieldKey(this.table.baseChange, { ...baseAttachId, target: CrossFieldTarget.Destination }, countToProcess);
1446
+ countToProcess = attachFieldEntry.length;
1447
+ const detachedMoveEntry = this.table.baseChange.rootNodes.outputDetachLocations.getFirst(baseDetachId, countToProcess);
1448
+ countToProcess = detachedMoveEntry.length;
1449
+ const destinationField = attachFieldEntry.value ?? detachedMoveEntry.value;
1450
+ if (destinationField !== undefined) {
1451
+ // The base detach is part of a move (or move of detach location) in the base changeset.
1452
+ setInCrossFieldMap(this.table.entries, baseAttachId, countToProcess, {
1453
+ nodeChange,
1454
+ detachId: newDetachId,
1455
+ cellRename,
1456
+ });
1457
+ if (nodeChange !== undefined || newDetachId !== undefined) {
1458
+ this.invalidateBaseFields([destinationField]);
1459
+ }
1460
+ }
1461
+ if (attachFieldEntry.value === undefined) {
1462
+ // These nodes are detached in the output context of the base changeset.
1463
+ if (nodeChange !== undefined) {
1464
+ assignRootChange(this.table.rebasedRootNodes, this.table.rebasedNodeToParent, baseAttachId, nodeChange, this.fieldId, this.table.rebaseVersion);
1465
+ }
1466
+ if (newDetachId !== undefined) {
1467
+ addNodeRename(this.table.rebasedRootNodes, baseAttachId, newDetachId, countToProcess, this.fieldId);
1468
+ }
1469
+ }
1470
+ if (newDetachId !== undefined) {
1471
+ this.table.movedDetaches.set(newDetachId, countToProcess, true);
1472
+ }
1473
+ if (countToProcess < count) {
1474
+ const remainingCount = count - countToProcess;
1475
+ const nextDetachId = newDetachId === undefined
1476
+ ? undefined
1477
+ : offsetChangeAtomId(newDetachId, countToProcess);
1478
+ this.rebaseOverDetach(offsetChangeAtomId(baseDetachId, countToProcess), remainingCount, nextDetachId, nodeChange);
1479
+ }
1480
+ }
1481
+ addDetach(id, count) {
1482
+ this.table.rebasedDetachLocations.set(id, count, this.fieldId);
1483
+ }
1484
+ removeDetach(id, count) {
1485
+ this.table.movedDetaches.set(id, count, true);
1486
+ }
1487
+ doesBaseAttachNodes(id, count) {
1488
+ let countToProcess = count;
1489
+ const attachEntry = getFirstAttachField(this.table.baseChange.crossFieldKeys, id, countToProcess);
1490
+ countToProcess = attachEntry.length;
1491
+ return { start: id, value: attachEntry.value !== undefined, length: countToProcess };
1492
+ }
1493
+ getBaseRename(id, count) {
1494
+ return this.table.baseChange.rootNodes.oldToNewId.getFirst(id, count);
1495
+ }
1496
+ getNewRenameForBaseRename(baseRenameTo, count) {
1497
+ let countToProcess = count;
1498
+ const inputEntry = firstDetachIdFromAttachId(this.table.baseChange.rootNodes, baseRenameTo, countToProcess);
1499
+ const attachEntry = getFirstAttachField(this.table.baseChange.crossFieldKeys, baseRenameTo, countToProcess);
1500
+ countToProcess = attachEntry.length;
1501
+ if (attachEntry.value !== undefined) {
1502
+ // These nodes are attached in the output context of the base changeset.
1503
+ return { value: undefined, length: countToProcess };
1504
+ }
1505
+ countToProcess = inputEntry.length;
1506
+ const inputId = inputEntry.value;
1507
+ const moveEntry = this.table.entries.getFirst(inputId, countToProcess);
1508
+ countToProcess = moveEntry.length;
1509
+ if (moveEntry.value !== undefined) {
1510
+ return { ...moveEntry, value: moveEntry.value.cellRename ?? moveEntry.value.detachId };
1511
+ }
1512
+ return this.table.newChange.rootNodes.oldToNewId.getFirst(inputId, countToProcess);
1513
+ }
1514
+ invalidateBaseFields(fields) {
1515
+ if (this.allowInval) {
1516
+ for (const fieldId of fields) {
1517
+ this.table.affectedBaseFields.set(fieldIdKeyFromFieldId(fieldId), true);
1294
1518
  }
1295
1519
  }
1296
- super.set(target, revision, id, count, newValue, invalidateDependents);
1297
- }
1298
- onMoveIn(id) {
1299
- setInChangeAtomIdMap(this.table.rebasedNodeToParent, id, this.fieldId);
1300
- }
1301
- moveKey(target, revision, id, count) {
1302
- this.table.rebasedCrossFieldKeys.set({ target, revision, localId: id }, count, this.fieldId);
1303
1520
  }
1304
- get table() {
1305
- return this.crossFieldTable;
1521
+ }
1522
+ function assignRootChange(table, nodeToParent, detachId, nodeId, detachLocation, rebaseVersion) {
1523
+ assert(rebaseVersion >= 2 || detachLocation !== undefined, "All root changes need a detach location to support compatibility with older client versions");
1524
+ setInChangeAtomIdMap(table.nodeChanges, detachId, nodeId);
1525
+ if (nodeToParent !== undefined) {
1526
+ setInChangeAtomIdMap(nodeToParent, nodeId, { root: detachId });
1306
1527
  }
1528
+ table.detachLocations.set(detachId, 1, detachLocation);
1307
1529
  }
1308
- // TODO: Deduplicate this with RebaseTable
1309
- class ComposeManager extends CrossFieldManagerI {
1310
- constructor(table, currentField, fieldId, allowInval = true) {
1311
- super(table, currentField, allowInval);
1530
+ class ComposeNodeManagerI {
1531
+ constructor(table, fieldId, allowInval = true) {
1532
+ this.table = table;
1312
1533
  this.fieldId = fieldId;
1534
+ this.allowInval = allowInval;
1313
1535
  }
1314
- set(target, revision, id, count, newValue, invalidateDependents) {
1315
- if (invalidateDependents && this.allowInval) {
1316
- const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, {
1317
- target,
1318
- revision,
1319
- localId: id,
1320
- }, count);
1321
- if (newFieldIds.length > 0) {
1322
- for (const newFieldId of newFieldIds) {
1323
- this.table.pendingCompositions.affectedNewFields.set([newFieldId.nodeId?.revision, newFieldId.nodeId?.localId, newFieldId.field], true);
1324
- }
1536
+ getNewChangesForBaseDetach(baseDetachId, count) {
1537
+ let countToProcess = count;
1538
+ const baseAttachEntry = getFirstFieldForCrossFieldKey(this.table.baseChange, { target: CrossFieldTarget.Destination, ...baseDetachId }, countToProcess);
1539
+ countToProcess = baseAttachEntry.length;
1540
+ let result;
1541
+ if (baseAttachEntry.value === undefined) {
1542
+ // The detached nodes are still detached in the new change's input context.
1543
+ const rootEntry = rangeQueryChangeAtomIdMap(this.table.newChange.rootNodes.nodeChanges, baseDetachId, countToProcess);
1544
+ countToProcess = rootEntry.length;
1545
+ const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(baseDetachId, countToProcess);
1546
+ countToProcess = newRenameEntry.length;
1547
+ result = {
1548
+ value: { nodeChange: rootEntry.value, detachId: newRenameEntry.value },
1549
+ length: countToProcess,
1550
+ };
1551
+ }
1552
+ else {
1553
+ // The base detach was part of a move.
1554
+ // We check if we've previously seen a node change at the move destination.
1555
+ const entry = this.table.entries.getFirst(baseDetachId, countToProcess);
1556
+ result = { value: entry.value, length: entry.length };
1557
+ }
1558
+ // TODO: Consider moving this to a separate method so that this method can be side-effect free.
1559
+ if (result.value?.nodeChange !== undefined) {
1560
+ setInChangeAtomIdMap(this.table.movedNodeToParent, result.value.nodeChange, {
1561
+ field: this.fieldId,
1562
+ });
1563
+ }
1564
+ return result;
1565
+ }
1566
+ composeAttachDetach(baseAttachId, newDetachId, count) {
1567
+ let countToProcess = count;
1568
+ const newAttachEntry = getFirstAttachField(this.table.newChange.crossFieldKeys, newDetachId, countToProcess);
1569
+ countToProcess = newAttachEntry.length;
1570
+ // Both changes can have the same ID if they came from inverse changesets.
1571
+ // If the new detach is part of a move,
1572
+ // then both input changesets contain the attach cross-field key for this ID.
1573
+ // The new attach may still exist in the composed changeset so we do not remove it here.
1574
+ // The new attach will typically cancel with a base detach,
1575
+ // in which case the cross-field key will be removed in `composeDetachAttach`.
1576
+ const hasNewAttachWithBaseAttachId = areEqualChangeAtomIds(baseAttachId, newDetachId) && newAttachEntry.value !== undefined;
1577
+ if (!hasNewAttachWithBaseAttachId) {
1578
+ this.table.removedCrossFieldKeys.set({ ...baseAttachId, target: CrossFieldTarget.Destination }, countToProcess, true);
1579
+ }
1580
+ const baseDetachEntry = getFirstDetachField(this.table.baseChange.crossFieldKeys, baseAttachId, countToProcess);
1581
+ countToProcess = baseDetachEntry.length;
1582
+ const baseRootIdEntry = firstDetachIdFromAttachId(this.table.baseChange.rootNodes, baseAttachId, countToProcess);
1583
+ countToProcess = baseRootIdEntry.length;
1584
+ const baseDetachId = baseRootIdEntry.value;
1585
+ if (baseDetachEntry.value === undefined) {
1586
+ const baseDetachLocationEntry = this.table.baseChange.rootNodes.detachLocations.getFirst(baseDetachId, countToProcess);
1587
+ countToProcess = baseDetachLocationEntry.length;
1588
+ // These nodes were detached in the base change's input context,
1589
+ // so the net effect of the two changes is a rename.
1590
+ appendNodeRename(this.table.composedRootNodes, baseAttachId, newDetachId, baseDetachEntry.length, this.table.baseChange.rootNodes, baseDetachLocationEntry.value ?? this.fieldId);
1591
+ this.table.removedCrossFieldKeys.set({ ...newDetachId, target: CrossFieldTarget.Source }, countToProcess, true);
1592
+ }
1593
+ else {
1594
+ // The base change moves these nodes.
1595
+ const prevEntry = this.table.entries.getFirst(baseAttachId, baseDetachEntry.length).value ?? {};
1596
+ this.table.entries.set(baseAttachId, baseDetachEntry.length, {
1597
+ ...prevEntry,
1598
+ detachId: newDetachId,
1599
+ });
1600
+ // The new detach will replace the base detach, so we remove the key for the base detach, unless they have the same ID.
1601
+ if (!areEqualChangeAtomIds(baseAttachId, newDetachId)) {
1602
+ this.table.removedCrossFieldKeys.set({ ...baseAttachId, target: CrossFieldTarget.Source }, countToProcess, true);
1603
+ }
1604
+ this.table.movedCrossFieldKeys.set({ ...newDetachId, target: CrossFieldTarget.Source }, countToProcess, baseDetachEntry.value);
1605
+ this.invalidateBaseFields([baseDetachEntry.value]);
1606
+ }
1607
+ if (newAttachEntry.value === undefined) {
1608
+ const newOutputDetachLocationEntry = this.table.newChange.rootNodes.outputDetachLocations.getFirst(newDetachId, countToProcess);
1609
+ countToProcess = newOutputDetachLocationEntry.length;
1610
+ this.table.composedRootNodes.outputDetachLocations.set(newDetachId, countToProcess, newOutputDetachLocationEntry.value ?? this.fieldId);
1611
+ }
1612
+ if (countToProcess < count) {
1613
+ const remainingCount = count - countToProcess;
1614
+ this.composeAttachDetach(offsetChangeAtomId(baseAttachId, countToProcess), offsetChangeAtomId(newDetachId, countToProcess), remainingCount);
1615
+ }
1616
+ }
1617
+ sendNewChangesToBaseSourceLocation(baseAttachId, newChanges) {
1618
+ const { value: baseDetachId } = firstDetachIdFromAttachId(this.table.baseChange.rootNodes, baseAttachId, 1);
1619
+ const detachFields = getFieldsForCrossFieldKey(this.table.baseChange, {
1620
+ ...baseDetachId,
1621
+ target: CrossFieldTarget.Source,
1622
+ }, 1);
1623
+ if (detachFields.length > 0) {
1624
+ // The base attach is part of a move in the base changeset.
1625
+ const prevEntry = this.table.entries.getFirst(baseDetachId, 1).value ?? {};
1626
+ this.table.entries.set(baseDetachId, 1, { ...prevEntry, nodeChange: newChanges });
1627
+ if (newChanges !== undefined) {
1628
+ this.invalidateBaseFields(detachFields);
1629
+ }
1630
+ }
1631
+ else {
1632
+ const baseNodeId = getFromChangeAtomIdMap(this.table.baseChange.rootNodes.nodeChanges, baseDetachId);
1633
+ if (baseNodeId === undefined) {
1634
+ assignRootChange(this.table.composedRootNodes, this.table.movedNodeToParent, baseDetachId, newChanges, this.fieldId, this.table.rebaseVersion);
1325
1635
  }
1326
1636
  else {
1327
- const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, {
1328
- target,
1329
- revision,
1330
- localId: id,
1331
- }, count);
1332
- assert(baseFieldIds.length > 0, 0x9c8 /* Cross field key not registered in base or new change */);
1333
- for (const baseFieldId of baseFieldIds) {
1334
- this.table.pendingCompositions.affectedBaseFields.set([baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field], true);
1335
- }
1637
+ addNodesToCompose(this.table, baseNodeId, newChanges);
1336
1638
  }
1337
1639
  }
1338
- super.set(target, revision, id, count, newValue, invalidateDependents);
1339
- }
1340
- onMoveIn(id) {
1341
- setInChangeAtomIdMap(this.table.composedNodeToParent, id, this.fieldId);
1342
1640
  }
1343
- moveKey(target, revision, id, count) {
1344
- throw new Error("Moving cross-field keys during compose is currently unsupported");
1641
+ areSameNodes(baseDetachId, newAttachId, count) {
1642
+ const renamedDetachEntry = firstAttachIdFromDetachId(this.table.composedRootNodes, baseDetachId, count);
1643
+ const isReattachOfSameNodes = areEqualChangeAtomIds(renamedDetachEntry.value, newAttachId);
1644
+ return { ...renamedDetachEntry, value: isReattachOfSameNodes };
1645
+ }
1646
+ composeDetachAttach(baseDetachId, newAttachId, count, composeToPin) {
1647
+ const areSameEntry = this.areSameNodes(baseDetachId, newAttachId, count);
1648
+ const countToProcess = areSameEntry.length;
1649
+ if (areSameEntry.value) {
1650
+ // These nodes have been moved back to their original location, so the composed changeset should not have any renames for them.
1651
+ // Note that deleting the rename from `this.table.composedRootNodes` would change the result of this method
1652
+ // if it were rerun due to the field being invalidated, so we instead record that the rename should be deleted later.
1653
+ this.table.renamesToDelete.set(baseDetachId, countToProcess, true);
1654
+ }
1655
+ if (composeToPin) {
1656
+ this.table.movedCrossFieldKeys.set({ target: CrossFieldTarget.Source, ...newAttachId }, countToProcess, this.fieldId);
1657
+ if (!areEqualChangeAtomIds(baseDetachId, newAttachId)) {
1658
+ // The pin will have `newAttachId` as both its detach and attach ID.
1659
+ // So we remove `baseDetachId` unless that is equal to the pin's detach ID.
1660
+ this.table.removedCrossFieldKeys.set({ target: CrossFieldTarget.Source, ...baseDetachId }, countToProcess, true);
1661
+ }
1662
+ // Note that while change2 should already have this key, change1 may have a rollback for the same ID in a different location.
1663
+ // In that case, change1's attach should be canceled out by a detach from change2.
1664
+ // Here we make sure that the composed change has the correct location (this field) for the attach ID.
1665
+ this.table.movedCrossFieldKeys.set({ target: CrossFieldTarget.Destination, ...newAttachId }, countToProcess, this.fieldId);
1666
+ }
1667
+ else {
1668
+ this.table.removedCrossFieldKeys.set({ target: CrossFieldTarget.Source, ...baseDetachId }, countToProcess, true);
1669
+ this.table.removedCrossFieldKeys.set({ target: CrossFieldTarget.Destination, ...newAttachId }, countToProcess, true);
1670
+ }
1671
+ if (countToProcess < count) {
1672
+ this.composeAttachDetach(offsetChangeAtomId(baseDetachId, countToProcess), offsetChangeAtomId(newAttachId, countToProcess), count - countToProcess);
1673
+ }
1345
1674
  }
1346
- get table() {
1347
- return this.crossFieldTable;
1675
+ invalidateBaseFields(fields) {
1676
+ if (this.allowInval) {
1677
+ for (const fieldId of fields) {
1678
+ this.table.pendingCompositions.affectedBaseFields.set(fieldIdKeyFromFieldId(fieldId), true);
1679
+ }
1680
+ }
1348
1681
  }
1349
1682
  }
1350
- function makeModularChangeset(props = {
1351
- maxId: -1,
1352
- }) {
1683
+ function makeModularChangeset(props) {
1684
+ const p = props ?? { maxId: -1 };
1353
1685
  const changeset = {
1354
- fieldChanges: props.fieldChanges ?? new Map(),
1355
- nodeChanges: props.nodeChanges ?? newTupleBTree(),
1356
- nodeToParent: props.nodeToParent ?? newTupleBTree(),
1357
- nodeAliases: props.nodeAliases ?? newTupleBTree(),
1358
- crossFieldKeys: props.crossFieldKeys ?? newCrossFieldKeyTable(),
1686
+ rebaseVersion: p.rebaseVersion ?? 1,
1687
+ fieldChanges: p.fieldChanges ?? new Map(),
1688
+ nodeChanges: p.nodeChanges ?? newTupleBTree(),
1689
+ rootNodes: p.rootNodes ?? newRootTable(),
1690
+ nodeToParent: p.nodeToParent ?? newTupleBTree(),
1691
+ nodeAliases: p.nodeAliases ?? newTupleBTree(),
1692
+ crossFieldKeys: p.crossFieldKeys ?? newCrossFieldRangeTable(),
1359
1693
  };
1360
- if (props.revisions !== undefined && props.revisions.length > 0) {
1361
- changeset.revisions = props.revisions;
1694
+ if (p.revisions !== undefined && p.revisions.length > 0) {
1695
+ changeset.revisions = p.revisions;
1362
1696
  }
1363
- if (props.maxId >= 0) {
1364
- changeset.maxId = brand(props.maxId);
1697
+ if (p.maxId >= 0) {
1698
+ changeset.maxId = brand(p.maxId);
1365
1699
  }
1366
- if (props.constraintViolationCount !== undefined && props.constraintViolationCount > 0) {
1367
- changeset.constraintViolationCount = props.constraintViolationCount;
1700
+ if (p.constraintViolationCount !== undefined && p.constraintViolationCount > 0) {
1701
+ changeset.constraintViolationCount = p.constraintViolationCount;
1368
1702
  }
1369
- if (props.constraintViolationCountOnRevert !== undefined &&
1370
- props.constraintViolationCountOnRevert > 0) {
1371
- changeset.constraintViolationCountOnRevert = props.constraintViolationCountOnRevert;
1703
+ if (p.constraintViolationCountOnRevert !== undefined &&
1704
+ p.constraintViolationCountOnRevert > 0) {
1705
+ changeset.constraintViolationCountOnRevert = p.constraintViolationCountOnRevert;
1372
1706
  }
1373
- if (props.noChangeConstraint !== undefined) {
1374
- changeset.noChangeConstraint = props.noChangeConstraint;
1707
+ if (p.noChangeConstraint !== undefined) {
1708
+ changeset.noChangeConstraint = p.noChangeConstraint;
1375
1709
  }
1376
- if (props.noChangeConstraintOnRevert !== undefined) {
1377
- changeset.noChangeConstraintOnRevert = props.noChangeConstraintOnRevert;
1710
+ if (p.noChangeConstraintOnRevert !== undefined) {
1711
+ changeset.noChangeConstraintOnRevert = p.noChangeConstraintOnRevert;
1378
1712
  }
1379
- if (props.builds !== undefined && props.builds.size > 0) {
1380
- changeset.builds = props.builds;
1713
+ if (p.builds !== undefined && p.builds.size > 0) {
1714
+ changeset.builds = p.builds;
1381
1715
  }
1382
- if (props.destroys !== undefined && props.destroys.size > 0) {
1383
- changeset.destroys = props.destroys;
1716
+ if (p.destroys !== undefined && p.destroys.size > 0) {
1717
+ changeset.destroys = p.destroys;
1384
1718
  }
1385
- if (props.refreshers !== undefined && props.refreshers.size > 0) {
1386
- changeset.refreshers = props.refreshers;
1719
+ if (p.refreshers !== undefined && p.refreshers.size > 0) {
1720
+ changeset.refreshers = p.refreshers;
1387
1721
  }
1388
1722
  return changeset;
1389
1723
  }
@@ -1395,6 +1729,9 @@ export class ModularEditBuilder extends EditBuilder {
1395
1729
  this.idAllocator = idAllocatorFromMaxId();
1396
1730
  this.codecOptions = codecOptions;
1397
1731
  }
1732
+ isInTransaction() {
1733
+ return this.transactionDepth > 0;
1734
+ }
1398
1735
  enterTransaction() {
1399
1736
  this.transactionDepth += 1;
1400
1737
  if (this.transactionDepth === 1) {
@@ -1445,7 +1782,8 @@ export class ModularEditBuilder extends EditBuilder {
1445
1782
  fieldChange: { fieldKind, change },
1446
1783
  nodeChanges: newTupleBTree(),
1447
1784
  nodeToParent: newTupleBTree(),
1448
- crossFieldKeys: newCrossFieldKeyTable(),
1785
+ crossFieldKeys: newCrossFieldRangeTable(),
1786
+ rootNodes: newRootTable(),
1449
1787
  idAllocator: this.idAllocator,
1450
1788
  localCrossFieldKeys,
1451
1789
  revision,
@@ -1464,6 +1802,7 @@ export class ModularEditBuilder extends EditBuilder {
1464
1802
  ? makeModularChangeset({
1465
1803
  maxId: this.idAllocator.getMaxId(),
1466
1804
  builds: change.builds,
1805
+ rootNodes: renameTableFromRenameDescriptions(change.renames ?? []),
1467
1806
  revisions: [{ revision: change.revision }],
1468
1807
  })
1469
1808
  : buildModularChangesetFromField({
@@ -1474,7 +1813,8 @@ export class ModularEditBuilder extends EditBuilder {
1474
1813
  },
1475
1814
  nodeChanges: newTupleBTree(),
1476
1815
  nodeToParent: newTupleBTree(),
1477
- crossFieldKeys: newCrossFieldKeyTable(),
1816
+ crossFieldKeys: newCrossFieldRangeTable(),
1817
+ rootNodes: newRootTable(),
1478
1818
  idAllocator: this.idAllocator,
1479
1819
  localCrossFieldKeys: getChangeHandler(this.fieldKinds, change.fieldKind).getCrossFieldKeys(change.change),
1480
1820
  revision: change.revision,
@@ -1503,7 +1843,8 @@ export class ModularEditBuilder extends EditBuilder {
1503
1843
  nodeChange,
1504
1844
  nodeChanges: newTupleBTree(),
1505
1845
  nodeToParent: newTupleBTree(),
1506
- crossFieldKeys: newCrossFieldKeyTable(),
1846
+ crossFieldKeys: newCrossFieldRangeTable(),
1847
+ rootNodes: newRootTable(),
1507
1848
  idAllocator: this.idAllocator,
1508
1849
  revision,
1509
1850
  }), revision));
@@ -1517,7 +1858,8 @@ export class ModularEditBuilder extends EditBuilder {
1517
1858
  nodeChange,
1518
1859
  nodeChanges: newTupleBTree(),
1519
1860
  nodeToParent: newTupleBTree(),
1520
- crossFieldKeys: newCrossFieldKeyTable(),
1861
+ crossFieldKeys: newCrossFieldRangeTable(),
1862
+ rootNodes: newRootTable(),
1521
1863
  idAllocator: this.idAllocator,
1522
1864
  revision,
1523
1865
  }), revision));
@@ -1543,17 +1885,20 @@ export class ModularEditBuilder extends EditBuilder {
1543
1885
  this.applyChange(tagChange(changeset, revision));
1544
1886
  }
1545
1887
  }
1546
- function buildModularChangesetFromField(props) {
1547
- const { path, fieldChange, nodeChanges, nodeToParent, crossFieldKeys, idAllocator = idAllocatorFromMaxId(), localCrossFieldKeys = [], childId, revision, } = props;
1888
+ export function buildModularChangesetFromField(props) {
1889
+ const { path, fieldChange, nodeChanges, nodeToParent, crossFieldKeys, rootNodes, idAllocator = idAllocatorFromMaxId(), localCrossFieldKeys = [], childId, revision, } = props;
1548
1890
  const fieldChanges = new Map([[path.field, fieldChange]]);
1549
1891
  if (path.parent === undefined) {
1892
+ const field = { nodeId: undefined, field: path.field };
1550
1893
  for (const { key, count } of localCrossFieldKeys) {
1551
- crossFieldKeys.set(key, count, { nodeId: undefined, field: path.field });
1894
+ crossFieldKeys.set(key, count, field);
1552
1895
  }
1553
1896
  if (childId !== undefined) {
1554
1897
  setInChangeAtomIdMap(nodeToParent, childId, {
1555
- nodeId: undefined,
1556
- field: path.field,
1898
+ field: {
1899
+ nodeId: undefined,
1900
+ field: path.field,
1901
+ },
1557
1902
  });
1558
1903
  }
1559
1904
  return makeModularChangeset({
@@ -1561,6 +1906,7 @@ function buildModularChangesetFromField(props) {
1561
1906
  nodeChanges,
1562
1907
  nodeToParent,
1563
1908
  crossFieldKeys,
1909
+ rootNodes,
1564
1910
  maxId: idAllocator.getMaxId(),
1565
1911
  revisions: [{ revision }],
1566
1912
  });
@@ -1569,13 +1915,13 @@ function buildModularChangesetFromField(props) {
1569
1915
  fieldChanges,
1570
1916
  };
1571
1917
  const parentId = { localId: brand(idAllocator.allocate()), revision };
1918
+ const fieldId = { nodeId: parentId, field: path.field };
1572
1919
  for (const { key, count } of localCrossFieldKeys) {
1573
1920
  crossFieldKeys.set(key, count, { nodeId: parentId, field: path.field });
1574
1921
  }
1575
1922
  if (childId !== undefined) {
1576
1923
  setInChangeAtomIdMap(nodeToParent, childId, {
1577
- nodeId: parentId,
1578
- field: path.field,
1924
+ field: fieldId,
1579
1925
  });
1580
1926
  }
1581
1927
  return buildModularChangesetFromNode({
@@ -1584,6 +1930,7 @@ function buildModularChangesetFromField(props) {
1584
1930
  nodeChanges,
1585
1931
  nodeToParent,
1586
1932
  crossFieldKeys,
1933
+ rootNodes,
1587
1934
  idAllocator,
1588
1935
  revision,
1589
1936
  nodeId: parentId,
@@ -1592,20 +1939,40 @@ function buildModularChangesetFromField(props) {
1592
1939
  function buildModularChangesetFromNode(props) {
1593
1940
  const { path, idAllocator, revision, nodeChanges, nodeChange, nodeId = { localId: brand(idAllocator.allocate()), revision }, } = props;
1594
1941
  setInChangeAtomIdMap(nodeChanges, nodeId, nodeChange);
1595
- const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
1596
- [path.parentIndex, nodeId],
1597
- ]);
1598
- const fieldChange = {
1599
- fieldKind: genericFieldKind.identifier,
1600
- change: fieldChangeset,
1601
- };
1602
- return buildModularChangesetFromField({
1603
- ...props,
1604
- path: { parent: path.parent, field: path.parentField },
1605
- fieldChange,
1606
- localCrossFieldKeys: [],
1607
- childId: nodeId,
1608
- });
1942
+ if (isDetachedUpPathRoot(path)) {
1943
+ props.rootNodes.nodeChanges.set([path.detachedNodeId.major, brand(path.detachedNodeId.minor)], nodeId);
1944
+ return makeModularChangeset({
1945
+ rootNodes: props.rootNodes,
1946
+ nodeChanges: props.nodeChanges,
1947
+ nodeToParent: props.nodeToParent,
1948
+ crossFieldKeys: props.crossFieldKeys,
1949
+ maxId: props.idAllocator.getMaxId(),
1950
+ revisions: [{ revision: props.revision }],
1951
+ });
1952
+ }
1953
+ else {
1954
+ const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
1955
+ [path.parentIndex, nodeId],
1956
+ ]);
1957
+ const fieldChange = {
1958
+ fieldKind: genericFieldKind.identifier,
1959
+ change: fieldChangeset,
1960
+ };
1961
+ return buildModularChangesetFromField({
1962
+ ...props,
1963
+ path: { parent: path.parent, field: path.parentField },
1964
+ fieldChange,
1965
+ localCrossFieldKeys: [],
1966
+ childId: nodeId,
1967
+ });
1968
+ }
1969
+ }
1970
+ function renameTableFromRenameDescriptions(renames) {
1971
+ const table = newRootTable();
1972
+ for (const rename of renames) {
1973
+ addNodeRename(table, rename.oldId, rename.newId, rename.count, rename.detachLocation);
1974
+ }
1975
+ return table;
1609
1976
  }
1610
1977
  function getRevInfoFromTaggedChanges(changes) {
1611
1978
  let maxId = -1;
@@ -1622,18 +1989,6 @@ function getRevInfoFromTaggedChanges(changes) {
1622
1989
  }
1623
1990
  }
1624
1991
  }
1625
- const rolledBackRevisions = [];
1626
- for (const info of revInfos) {
1627
- if (info.rollbackOf !== undefined) {
1628
- rolledBackRevisions.push(info.rollbackOf);
1629
- }
1630
- }
1631
- rolledBackRevisions.reverse();
1632
- for (const revision of rolledBackRevisions) {
1633
- if (!revisions.has(revision)) {
1634
- revInfos.push({ revision });
1635
- }
1636
- }
1637
1992
  return { maxId: brand(maxId), revInfos };
1638
1993
  }
1639
1994
  function revisionInfoFromTaggedChange(taggedChange) {
@@ -1650,15 +2005,16 @@ function revisionInfoFromTaggedChange(taggedChange) {
1650
2005
  }
1651
2006
  return revInfos;
1652
2007
  }
1653
- function fieldChangeFromId(fields, nodes, id) {
1654
- const fieldMap = fieldMapFromNodeId(fields, nodes, id.nodeId);
2008
+ function fieldChangeFromId(change, id) {
2009
+ const fieldId = normalizeFieldId(id, change.nodeAliases);
2010
+ const fieldMap = fieldMapFromNodeId(change.fieldChanges, change.nodeChanges, change.nodeAliases, fieldId.nodeId);
1655
2011
  return fieldMap.get(id.field) ?? fail(0xb25 /* No field exists for the given ID */);
1656
2012
  }
1657
- function fieldMapFromNodeId(rootFieldMap, nodes, nodeId) {
2013
+ function fieldMapFromNodeId(rootFieldMap, nodes, aliases, nodeId) {
1658
2014
  if (nodeId === undefined) {
1659
2015
  return rootFieldMap;
1660
2016
  }
1661
- const node = nodeChangeFromId(nodes, nodeId);
2017
+ const node = nodeChangeFromId(nodes, aliases, nodeId);
1662
2018
  assert(node.fieldChanges !== undefined, 0x9c9 /* Expected node to have field changes */);
1663
2019
  return node.fieldChanges;
1664
2020
  }
@@ -1671,8 +2027,9 @@ function rebasedFieldIdFromBaseId(table, baseId) {
1671
2027
  function rebasedNodeIdFromBaseNodeId(table, baseId) {
1672
2028
  return getFromChangeAtomIdMap(table.baseToRebasedNodeId, baseId) ?? baseId;
1673
2029
  }
1674
- function nodeChangeFromId(nodes, id) {
1675
- const node = getFromChangeAtomIdMap(nodes, id);
2030
+ function nodeChangeFromId(nodes, aliases, id) {
2031
+ const normalizedId = normalizeNodeId(id, aliases);
2032
+ const node = getFromChangeAtomIdMap(nodes, normalizedId);
1676
2033
  assert(node !== undefined, 0x9ca /* Unknown node ID */);
1677
2034
  return node;
1678
2035
  }
@@ -1680,12 +2037,20 @@ function fieldIdFromFieldIdKey([revision, localId, field]) {
1680
2037
  const nodeId = localId === undefined ? undefined : { revision, localId };
1681
2038
  return { nodeId, field };
1682
2039
  }
2040
+ function fieldIdKeyFromFieldId(fieldId) {
2041
+ return [fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field];
2042
+ }
1683
2043
  function cloneNodeChangeset(nodeChangeset) {
1684
2044
  if (nodeChangeset.fieldChanges !== undefined) {
1685
2045
  return { ...nodeChangeset, fieldChanges: new Map(nodeChangeset.fieldChanges) };
1686
2046
  }
1687
2047
  return { ...nodeChangeset };
1688
2048
  }
2049
+ function replaceNodeLocationRevision(location, replacer) {
2050
+ return location.field === undefined
2051
+ ? { root: replacer.getUpdatedAtomId(location.root) }
2052
+ : { field: replaceFieldIdRevision(location.field, replacer) };
2053
+ }
1689
2054
  function replaceFieldIdRevision(fieldId, replacer) {
1690
2055
  if (fieldId.nodeId === undefined) {
1691
2056
  return fieldId;
@@ -1695,16 +2060,33 @@ function replaceFieldIdRevision(fieldId, replacer) {
1695
2060
  nodeId: replacer.getUpdatedAtomId(fieldId.nodeId),
1696
2061
  };
1697
2062
  }
1698
- export function getParentFieldId(changeset, nodeId) {
1699
- const parentId = getFromChangeAtomIdMap(changeset.nodeToParent, nodeId);
1700
- assert(parentId !== undefined, 0x9cb /* Parent field should be defined */);
1701
- return normalizeFieldId(parentId, changeset.nodeAliases);
2063
+ export function getNodeParent(changeset, nodeId) {
2064
+ const normalizedNodeId = normalizeNodeId(nodeId, changeset.nodeAliases);
2065
+ const location = getFromChangeAtomIdMap(changeset.nodeToParent, normalizedNodeId);
2066
+ assert(location !== undefined, 0x9cb /* Parent field should be defined */);
2067
+ if (location.field !== undefined) {
2068
+ return { field: normalizeFieldId(location.field, changeset.nodeAliases) };
2069
+ }
2070
+ return location;
1702
2071
  }
1703
2072
  function getFieldsForCrossFieldKey(changeset, key, count) {
1704
2073
  return changeset.crossFieldKeys
1705
2074
  .getAll(key, count)
1706
2075
  .map(({ value: fieldId }) => normalizeFieldId(fieldId, changeset.nodeAliases));
1707
2076
  }
2077
+ function getFirstFieldForCrossFieldKey(changeset, key, count) {
2078
+ const result = changeset.crossFieldKeys.getFirst(key, count);
2079
+ if (result.value === undefined) {
2080
+ return result;
2081
+ }
2082
+ return { ...result, value: normalizeFieldId(result.value, changeset.nodeAliases) };
2083
+ }
2084
+ function normalizeNodeLocation(location, nodeAliases) {
2085
+ if (location.field !== undefined) {
2086
+ return { field: normalizeFieldId(location.field, nodeAliases) };
2087
+ }
2088
+ return location;
2089
+ }
1708
2090
  // This is only exported for use in test utilities.
1709
2091
  export function normalizeFieldId(fieldId, nodeAliases) {
1710
2092
  return fieldId.nodeId === undefined
@@ -1714,8 +2096,9 @@ export function normalizeFieldId(fieldId, nodeAliases) {
1714
2096
  /**
1715
2097
  * @returns The canonical form of nodeId, according to nodeAliases
1716
2098
  */
1717
- function normalizeNodeId(nodeId, nodeAliases) {
2099
+ export function normalizeNodeId(nodeId, nodeAliases) {
1718
2100
  let currentId = nodeId;
2101
+ let cycleProbeId = nodeId;
1719
2102
  // eslint-disable-next-line no-constant-condition
1720
2103
  while (true) {
1721
2104
  const dealiased = getFromChangeAtomIdMap(nodeAliases, currentId);
@@ -1723,6 +2106,13 @@ function normalizeNodeId(nodeId, nodeAliases) {
1723
2106
  return currentId;
1724
2107
  }
1725
2108
  currentId = dealiased;
2109
+ if (cycleProbeId !== undefined) {
2110
+ cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
2111
+ }
2112
+ if (cycleProbeId !== undefined) {
2113
+ cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
2114
+ }
2115
+ assert(!areEqualChangeAtomIdOpts(cycleProbeId, currentId), "Alias cycle detected");
1726
2116
  }
1727
2117
  }
1728
2118
  function hasConflicts(change) {
@@ -1731,4 +2121,479 @@ function hasConflicts(change) {
1731
2121
  function areEqualFieldIds(a, b) {
1732
2122
  return areEqualChangeAtomIdOpts(a.nodeId, b.nodeId) && a.field === b.field;
1733
2123
  }
2124
+ function firstAttachIdFromDetachId(roots, detachId, count) {
2125
+ const result = roots.oldToNewId.getFirst(detachId, count);
2126
+ return { ...result, value: result.value ?? detachId };
2127
+ }
2128
+ function firstDetachIdFromAttachId(roots, attachId, count) {
2129
+ const result = roots.newToOldId.getFirst(attachId, count);
2130
+ return { ...result, start: attachId, value: result.value ?? attachId };
2131
+ }
2132
+ function rebaseCrossFieldKeys(sourceTable, movedDetaches, newDetachLocations) {
2133
+ const rebasedTable = sourceTable.clone();
2134
+ for (const entry of movedDetaches.entries()) {
2135
+ rebasedTable.delete({ ...entry.start, target: CrossFieldTarget.Source }, entry.length);
2136
+ }
2137
+ for (const entry of newDetachLocations.entries()) {
2138
+ rebasedTable.set({ ...entry.start, target: CrossFieldTarget.Source }, entry.length, entry.value);
2139
+ }
2140
+ return rebasedTable;
2141
+ }
2142
+ export function newRootTable() {
2143
+ return {
2144
+ newToOldId: newChangeAtomIdTransform(),
2145
+ oldToNewId: newChangeAtomIdTransform(),
2146
+ nodeChanges: newTupleBTree(),
2147
+ detachLocations: newChangeAtomIdRangeMap(),
2148
+ outputDetachLocations: newChangeAtomIdRangeMap(),
2149
+ };
2150
+ }
2151
+ function rebaseRoots(change, base, affectedBaseFields, nodesToRebase, rebasedNodeToParent, rebaseVersion) {
2152
+ const rebasedRoots = newRootTable();
2153
+ for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
2154
+ rebaseRename(change.rootNodes, rebasedRoots, renameEntry, base, affectedBaseFields);
2155
+ }
2156
+ for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
2157
+ const changes = base.rootNodes.nodeChanges.get(detachIdKey);
2158
+ if (changes !== undefined) {
2159
+ nodesToRebase.push([nodeId, changes]);
2160
+ }
2161
+ const detachId = makeChangeAtomId(detachIdKey[1], detachIdKey[0]);
2162
+ const attachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
2163
+ const baseAttachEntry = base.crossFieldKeys.getFirst({ target: CrossFieldTarget.Destination, ...attachId }, 1);
2164
+ if (baseAttachEntry.value === undefined) {
2165
+ const renamedDetachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
2166
+ const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(renamedDetachId, 1).value;
2167
+ if (baseOutputDetachLocation !== undefined) {
2168
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
2169
+ }
2170
+ const detachLocation = baseOutputDetachLocation ??
2171
+ change.rootNodes.detachLocations.getFirst(detachId, 1).value;
2172
+ // Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
2173
+ // We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
2174
+ assignRootChange(rebasedRoots, rebasedNodeToParent, renamedDetachId, nodeId, detachLocation, rebaseVersion);
2175
+ }
2176
+ else {
2177
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseAttachEntry.value), true);
2178
+ rebasedNodeToParent.delete(detachIdKey);
2179
+ }
2180
+ }
2181
+ for (const entry of change.rootNodes.outputDetachLocations.entries()) {
2182
+ rebasedRoots.outputDetachLocations.set(entry.start, entry.length, entry.value);
2183
+ }
2184
+ return rebasedRoots;
2185
+ }
2186
+ function rebaseRename(newRoots, rebasedRoots, renameEntry, base, affectedBaseFields) {
2187
+ let count = renameEntry.length;
2188
+ const baseRenameEntry = firstAttachIdFromDetachId(base.rootNodes, renameEntry.start, count);
2189
+ count = baseRenameEntry.length;
2190
+ const baseAttachEntry = base.crossFieldKeys.getFirst({
2191
+ ...baseRenameEntry.value,
2192
+ target: CrossFieldTarget.Destination,
2193
+ }, count);
2194
+ count = baseAttachEntry.length;
2195
+ if (baseAttachEntry.value === undefined) {
2196
+ const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(baseRenameEntry.value, 1).value;
2197
+ if (baseOutputDetachLocation !== undefined) {
2198
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
2199
+ }
2200
+ const detachEntry = newRoots.detachLocations.getFirst(renameEntry.start, count);
2201
+ count = detachEntry.length;
2202
+ const detachLocation = baseOutputDetachLocation ?? detachEntry.value;
2203
+ // Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
2204
+ // We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
2205
+ addNodeRename(rebasedRoots, baseRenameEntry.value, renameEntry.value, count, detachLocation);
2206
+ }
2207
+ else {
2208
+ // This rename represents an intention to detach these nodes.
2209
+ // The rebased change should have a detach in the field where the base change attaches the nodes,
2210
+ // so we need to ensure that field is processed.
2211
+ affectedBaseFields.set(fieldIdKeyFromFieldId(normalizeFieldId(baseAttachEntry.value, base.nodeAliases)), true);
2212
+ }
2213
+ const countRemaining = renameEntry.length - count;
2214
+ if (countRemaining > 0) {
2215
+ rebaseRename(newRoots, rebasedRoots, {
2216
+ start: offsetChangeAtomId(renameEntry.start, count),
2217
+ value: offsetChangeAtomId(renameEntry.value, count),
2218
+ length: countRemaining,
2219
+ }, base, affectedBaseFields);
2220
+ }
2221
+ }
2222
+ /**
2223
+ * For each root detach location, replaces any node ID from the base changeset
2224
+ * with the corresponding ID in the new changeset.
2225
+ */
2226
+ function fixupRebasedDetachLocations(table) {
2227
+ for (const { start, length, value: detachLocation, } of table.rebasedRootNodes.detachLocations.entries()) {
2228
+ const normalizedDetachLocation = normalizeFieldId(detachLocation, table.baseChange.nodeAliases);
2229
+ if (normalizedDetachLocation.nodeId !== undefined) {
2230
+ const rebasedNodeId = getFromChangeAtomIdMap(table.baseToRebasedNodeId, normalizedDetachLocation.nodeId);
2231
+ if (rebasedNodeId !== undefined) {
2232
+ table.rebasedRootNodes.detachLocations.set(start, length, {
2233
+ ...normalizedDetachLocation,
2234
+ nodeId: rebasedNodeId,
2235
+ });
2236
+ }
2237
+ }
2238
+ }
2239
+ }
2240
+ function addNodesToCompose(table, id1, id2) {
2241
+ const normalizedId1 = normalizeNodeId(id1, table.baseChange.nodeAliases);
2242
+ const normalizedId2 = normalizeNodeId(id2, table.newChange.nodeAliases);
2243
+ if (getFromChangeAtomIdMap(table.newToBaseNodeId, normalizedId2) === undefined) {
2244
+ setInChangeAtomIdMap(table.newToBaseNodeId, normalizedId2, normalizedId1);
2245
+ table.pendingCompositions.nodeIdsToCompose.push([normalizedId1, normalizedId2]);
2246
+ }
2247
+ }
2248
+ function composeRevInfos(revisions1, revisions2) {
2249
+ if (revisions1?.length === 1 &&
2250
+ revisions2?.length === 1 &&
2251
+ revisions1[0]?.revision === revisions2[0]?.revision) {
2252
+ // This is a special case where we are composing two changesets from the same transaction.
2253
+ // We return one of the input arrays to avoid duplicating revision entries.
2254
+ return revisions1;
2255
+ }
2256
+ const result = [...(revisions1 ?? []), ...(revisions2 ?? [])];
2257
+ return result;
2258
+ }
2259
+ function composeCrossFieldKeyTables(table1, table2, movedCrossFieldKeys, removedCrossFieldKeys) {
2260
+ const composedTable = RangeMap.union(table1, table2);
2261
+ for (const entry of movedCrossFieldKeys.entries()) {
2262
+ composedTable.set(entry.start, entry.length, entry.value);
2263
+ }
2264
+ for (const entry of removedCrossFieldKeys.entries()) {
2265
+ composedTable.delete(entry.start, entry.length);
2266
+ }
2267
+ return composedTable;
2268
+ }
2269
+ function composeRootTables(change1, change2, composedNodeToParent, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions) {
2270
+ const composedTable = cloneRootTable(change1.rootNodes);
2271
+ for (const renameEntry of change2.rootNodes.oldToNewId.entries()) {
2272
+ composeRename(change1, change2, composedTable, renameEntry.start, renameEntry.value, renameEntry.length, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions);
2273
+ }
2274
+ for (const [[revision2, id2], nodeId2] of change2.rootNodes.nodeChanges.entries()) {
2275
+ const detachId2 = { revision: revision2, localId: id2 };
2276
+ const detachId1 = firstDetachIdFromAttachId(change1.rootNodes, detachId2, 1).value;
2277
+ const nodeId1 = getFromChangeAtomIdMap(change1.rootNodes.nodeChanges, detachId1);
2278
+ if (nodeId1 === undefined) {
2279
+ const fieldId = getFieldsForCrossFieldKey(change1, { ...detachId1, target: CrossFieldTarget.Source }, 1)[0];
2280
+ if (fieldId === undefined) {
2281
+ assignRootChange(composedTable, composedNodeToParent, detachId1, nodeId2, change1.rootNodes.detachLocations.getFirst(detachId1, 1).value ??
2282
+ change2.rootNodes.detachLocations.getFirst(detachId2, 1).value, Math.max(change1.rebaseVersion, change2.rebaseVersion));
2283
+ }
2284
+ else {
2285
+ // In this case, this node is attached in the input context of change1,
2286
+ // and is represented in detachFieldId.
2287
+ pendingCompositions.affectedBaseFields.set([fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field], true);
2288
+ }
2289
+ }
2290
+ else {
2291
+ pendingCompositions.nodeIdsToCompose.push([nodeId1, nodeId2]);
2292
+ }
2293
+ }
2294
+ for (const outputDetachEntry of change1.rootNodes.outputDetachLocations.entries()) {
2295
+ composeOutputDetachLocation(outputDetachEntry.start, outputDetachEntry.length, outputDetachEntry.value, change2, composedTable);
2296
+ }
2297
+ for (const entry of change2.rootNodes.outputDetachLocations.entries()) {
2298
+ composedTable.outputDetachLocations.set(entry.start, entry.length, entry.value);
2299
+ }
2300
+ return composedTable;
2301
+ }
2302
+ function composeOutputDetachLocation(outputDetachId1, count, detachLocation, change2, composedTable) {
2303
+ let countToProcess = count;
2304
+ const renameEntry = firstAttachIdFromDetachId(change2.rootNodes, outputDetachId1, countToProcess);
2305
+ countToProcess = renameEntry.length;
2306
+ const attachEntry = getFirstAttachField(change2.crossFieldKeys, renameEntry.value, countToProcess);
2307
+ countToProcess = attachEntry.length;
2308
+ composedTable.outputDetachLocations.delete(outputDetachId1, countToProcess);
2309
+ if (attachEntry.value === undefined) {
2310
+ // We update the key for the detach location to the renamed ID of the root in the composed output context.
2311
+ composedTable.outputDetachLocations.set(renameEntry.value, countToProcess, detachLocation);
2312
+ }
2313
+ else {
2314
+ // These nodes are attached by `change2` and thus attached in the composed output context,
2315
+ // so there should be no output detach location.
2316
+ }
2317
+ const countRemaining = count - countToProcess;
2318
+ if (countRemaining > 0) {
2319
+ composeOutputDetachLocation(offsetChangeAtomId(outputDetachId1, countToProcess), countRemaining, detachLocation, change2, composedTable);
2320
+ }
2321
+ }
2322
+ function composeRename(change1, change2, mergedTable, oldId, newId, count, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions) {
2323
+ let countToProcess = count;
2324
+ const detachEntry = getFirstDetachField(change1.crossFieldKeys, oldId, countToProcess);
2325
+ countToProcess = detachEntry.length;
2326
+ if (detachEntry.value === undefined) {
2327
+ // `change1` may also have a rename to `renameEntry.value`, in which case it must refer to a different node.
2328
+ // That node must have been attached by `change1` and detached by `change2`.
2329
+ // The final rename for that node will be created in `composeAttachDetach`.
2330
+ // We delete any such rename for now to avoid colliding with the rename currently being processed.
2331
+ deleteNodeRenameTo(mergedTable, newId, countToProcess);
2332
+ // The nodes were detached before `change`, so we append this rename.
2333
+ appendNodeRename(mergedTable, oldId, newId, countToProcess, change1.rootNodes, change2.rootNodes.detachLocations.getFirst(oldId, countToProcess).value);
2334
+ }
2335
+ else {
2336
+ // `change1` detached these nodes,
2337
+ // so we invalidate the detach location so that the detach's ID can be replaced with the new ID.
2338
+ pendingCompositions.affectedBaseFields.set(fieldIdKeyFromFieldId(detachEntry.value), true);
2339
+ if (!areEqualChangeAtomIds(oldId, newId)) {
2340
+ // `change1`'s detach will be replaced by `change2`'s detach, so we update the cross-field keys.
2341
+ removedCrossFieldKeys.set({ ...oldId, target: CrossFieldTarget.Source }, countToProcess, true);
2342
+ }
2343
+ movedCrossFieldKeys.set({ ...newId, target: CrossFieldTarget.Source }, countToProcess, detachEntry.value);
2344
+ }
2345
+ if (countToProcess < count) {
2346
+ composeRename(change1, change2, mergedTable, offsetChangeAtomId(oldId, countToProcess), offsetChangeAtomId(newId, countToProcess), count - countToProcess, movedCrossFieldKeys, removedCrossFieldKeys, pendingCompositions);
2347
+ }
2348
+ }
2349
+ export function cloneRootTable(table) {
2350
+ return {
2351
+ oldToNewId: table.oldToNewId.clone(),
2352
+ newToOldId: table.newToOldId.clone(),
2353
+ nodeChanges: brand(table.nodeChanges.clone()),
2354
+ detachLocations: table.detachLocations.clone(),
2355
+ outputDetachLocations: table.outputDetachLocations.clone(),
2356
+ };
2357
+ }
2358
+ function invertRootTable(change, isRollback) {
2359
+ const invertedRoots = newRootTable();
2360
+ for (const [[revision, localId], nodeId] of change.rootNodes.nodeChanges.entries()) {
2361
+ const detachId = { revision, localId };
2362
+ const renamedId = firstAttachIdFromDetachId(change.rootNodes, detachId, 1).value;
2363
+ // This checks whether `change` attaches this node.
2364
+ // If it does, the node is not detached in the input context of the inverse, and so should not be included in the root table.
2365
+ if (change.crossFieldKeys.getFirst({ ...renamedId, target: CrossFieldTarget.Destination }, 1)
2366
+ .value === undefined) {
2367
+ assignRootChange(invertedRoots, undefined, renamedId, nodeId, change.rootNodes.detachLocations.getFirst(detachId, 1).value, change.rebaseVersion);
2368
+ }
2369
+ }
2370
+ if (isRollback) {
2371
+ // We only invert renames of nodes which are not attached or detached by this changeset.
2372
+ // When we invert an attach we will create a detach which incorporates the rename.
2373
+ for (const { start: oldId, value: newId, length, } of change.rootNodes.oldToNewId.entries()) {
2374
+ invertRename(change, invertedRoots, oldId, newId, length);
2375
+ }
2376
+ }
2377
+ return invertedRoots;
2378
+ }
2379
+ function invertRename(change, invertedRoots, oldId, newId, length) {
2380
+ for (const detachEntry of doesChangeDetachNodes(change.crossFieldKeys, newId, length)) {
2381
+ assert(!detachEntry.value, "A changeset should not have a rename and detach for the same node.");
2382
+ }
2383
+ let countProcessed = length;
2384
+ const attachEntry = getFirstAttachField(change.crossFieldKeys, newId, countProcessed);
2385
+ countProcessed = attachEntry.length;
2386
+ if (attachEntry.value === undefined) {
2387
+ const outputDetachEntry = change.rootNodes.outputDetachLocations.getFirst(newId, countProcessed);
2388
+ countProcessed = outputDetachEntry.length;
2389
+ const inputDetachEntry = change.rootNodes.detachLocations.getFirst(oldId, countProcessed);
2390
+ countProcessed = inputDetachEntry.length;
2391
+ addNodeRename(invertedRoots, newId, oldId, countProcessed, outputDetachEntry.value ?? inputDetachEntry.value);
2392
+ }
2393
+ if (countProcessed < length) {
2394
+ invertRename(change, invertedRoots, offsetChangeAtomId(oldId, countProcessed), offsetChangeAtomId(newId, countProcessed), length - countProcessed);
2395
+ }
2396
+ }
2397
+ function doesChangeAttachNodes(table, id, count) {
2398
+ return table
2399
+ .getAll2({ ...id, target: CrossFieldTarget.Destination }, count)
2400
+ .map((entry) => ({ ...entry, value: entry.value !== undefined }));
2401
+ }
2402
+ function doesChangeDetachNodes(table, id, count) {
2403
+ return table
2404
+ .getAll2({ ...id, target: CrossFieldTarget.Source }, count)
2405
+ .map((entry) => ({ ...entry, value: entry.value !== undefined }));
2406
+ }
2407
+ export function getFirstDetachField(table, id, count) {
2408
+ return table.getFirst({ target: CrossFieldTarget.Source, ...id }, count);
2409
+ }
2410
+ export function getFirstAttachField(table, id, count) {
2411
+ return table.getFirst({ target: CrossFieldTarget.Destination, ...id }, count);
2412
+ }
2413
+ export function addNodeRename(table, oldId, newId, count, detachLocation) {
2414
+ if (areEqualChangeAtomIds(oldId, newId)) {
2415
+ return;
2416
+ }
2417
+ for (const entry of table.oldToNewId.getAll2(oldId, count)) {
2418
+ assert(entry.value === undefined ||
2419
+ areEqualChangeAtomIds(entry.value, offsetChangeAtomId(newId, entry.offset)), "Rename collision detected");
2420
+ }
2421
+ for (const entry of table.newToOldId.getAll2(newId, count)) {
2422
+ assert(entry.value === undefined ||
2423
+ areEqualChangeAtomIds(entry.value, offsetChangeAtomId(oldId, entry.offset)), "Rename collision detected");
2424
+ }
2425
+ table.oldToNewId.set(oldId, count, newId);
2426
+ table.newToOldId.set(newId, count, oldId);
2427
+ if (detachLocation !== undefined) {
2428
+ table.detachLocations.set(oldId, count, detachLocation);
2429
+ }
2430
+ }
2431
+ /**
2432
+ * Deletes any renames from `id`.
2433
+ */
2434
+ function deleteNodeRenameFrom(roots, id, count) {
2435
+ for (const entry of roots.oldToNewId.getAll(id, count)) {
2436
+ deleteNodeRenameEntry(roots, entry.start, entry.value, entry.length);
2437
+ }
2438
+ }
2439
+ /**
2440
+ * Deletes any renames to `id`.
2441
+ */
2442
+ function deleteNodeRenameTo(roots, id, count) {
2443
+ for (const entry of roots.newToOldId.getAll(id, count)) {
2444
+ deleteNodeRenameEntry(roots, entry.value, entry.start, entry.length);
2445
+ }
2446
+ }
2447
+ function appendNodeRename(composedTable, oldId, newId, count, change1Table, detachLocation) {
2448
+ let countToProcess = count;
2449
+ const rename1Entry = change1Table.newToOldId.getFirst(oldId, countToProcess);
2450
+ countToProcess = rename1Entry.length;
2451
+ if (rename1Entry.value !== undefined) {
2452
+ deleteNodeRenameFrom(composedTable, rename1Entry.value, countToProcess);
2453
+ }
2454
+ addNodeRename(composedTable, rename1Entry.value ?? oldId, newId, countToProcess, detachLocation);
2455
+ tryRemoveDetachLocation(composedTable, newId, countToProcess);
2456
+ if (countToProcess < count) {
2457
+ const countRemaining = count - countToProcess;
2458
+ appendNodeRename(composedTable, offsetChangeAtomId(oldId, countToProcess), offsetChangeAtomId(newId, countToProcess), countRemaining, change1Table, detachLocation);
2459
+ }
2460
+ }
2461
+ function tryRemoveDetachLocation(roots, rootId, count) {
2462
+ let countProcessed = count;
2463
+ const renameEntry = roots.oldToNewId.getFirst(rootId, countProcessed);
2464
+ countProcessed = renameEntry.length;
2465
+ const outputDetachEntry = roots.outputDetachLocations.getFirst(rootId, countProcessed);
2466
+ countProcessed = outputDetachEntry.length;
2467
+ const nodeChangeEntry = rangeQueryChangeAtomIdMap(roots.nodeChanges, rootId, countProcessed);
2468
+ countProcessed = nodeChangeEntry.length;
2469
+ if (nodeChangeEntry.value === undefined &&
2470
+ renameEntry.value === undefined &&
2471
+ outputDetachEntry.value === undefined) {
2472
+ roots.detachLocations.delete(rootId, countProcessed);
2473
+ }
2474
+ const countRemaining = count - countProcessed;
2475
+ if (countRemaining > 0) {
2476
+ tryRemoveDetachLocation(roots, offsetChangeAtomId(rootId, countProcessed), countRemaining);
2477
+ }
2478
+ }
2479
+ /**
2480
+ * Deletes the entry renaming the ID range of length `count` from `oldId` to `newId`.
2481
+ * This function assumes that such an entry exists.
2482
+ */
2483
+ function deleteNodeRenameEntry(roots, oldId, newId, count) {
2484
+ roots.oldToNewId.delete(oldId, count);
2485
+ roots.newToOldId.delete(newId, count);
2486
+ }
2487
+ function replaceRootTableRevision(table, replacer, nodeAliases) {
2488
+ const oldToNewId = table.oldToNewId.mapEntries((id) => replacer.getUpdatedAtomId(id), (id) => replacer.getUpdatedAtomId(id));
2489
+ const newToOldId = table.newToOldId.mapEntries((id) => replacer.getUpdatedAtomId(id), (id) => replacer.getUpdatedAtomId(id));
2490
+ const nodeChanges = replaceIdMapRevisions(table.nodeChanges, replacer, (nodeId) => replacer.getUpdatedAtomId(normalizeNodeId(nodeId, nodeAliases)));
2491
+ const detachLocations = table.detachLocations.mapEntries((id) => replacer.getUpdatedAtomId(id), (fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer));
2492
+ const outputDetachLocations = table.outputDetachLocations.mapEntries((id) => replacer.getUpdatedAtomId(id), (fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer));
2493
+ return { oldToNewId, newToOldId, nodeChanges, detachLocations, outputDetachLocations };
2494
+ }
2495
+ function newDetachedEntryMap() {
2496
+ return new RangeMap(offsetChangeAtomId, subtractChangeAtomIds, offsetDetachedNodeEntry);
2497
+ }
2498
+ function offsetDetachedNodeEntry(entry, count) {
2499
+ assert(count <= 1 || entry.nodeChange === undefined, "Cannot split an entry with a node change");
2500
+ return entry.detachId === undefined
2501
+ ? entry
2502
+ : { ...entry, detachId: offsetChangeAtomId(entry.detachId, count) };
2503
+ }
2504
+ function getFieldsWithRootMoves(roots, nodeAliases) {
2505
+ const fields = newTupleBTree();
2506
+ for (const { start: rootId, value: fieldId, length } of roots.detachLocations.entries()) {
2507
+ let isRootMoved = false;
2508
+ for (const renameEntry of roots.oldToNewId.getAll2(rootId, length)) {
2509
+ if (renameEntry.value !== undefined) {
2510
+ isRootMoved = true;
2511
+ }
2512
+ }
2513
+ for (const outputDetachEntry of roots.outputDetachLocations.getAll2(rootId, length)) {
2514
+ if (outputDetachEntry.value !== undefined) {
2515
+ isRootMoved = true;
2516
+ }
2517
+ }
2518
+ if (isRootMoved) {
2519
+ fields.set(fieldIdKeyFromFieldId(normalizeFieldId(fieldId, nodeAliases)), true);
2520
+ }
2521
+ }
2522
+ return fields;
2523
+ }
2524
+ function getFieldToRootChanges(roots, nodeAliases) {
2525
+ const fields = newTupleBTree();
2526
+ for (const rootIdKey of roots.nodeChanges.keys()) {
2527
+ const rootId = { revision: rootIdKey[0], localId: rootIdKey[1] };
2528
+ const detachLocation = roots.detachLocations.getFirst(rootId, 1).value;
2529
+ if (detachLocation !== undefined) {
2530
+ const fieldIdKey = fieldIdKeyFromFieldId(normalizeFieldId(detachLocation, nodeAliases));
2531
+ let rootsInField = fields.get(fieldIdKey);
2532
+ if (rootsInField === undefined) {
2533
+ rootsInField = [];
2534
+ fields.set(fieldIdKey, rootsInField);
2535
+ }
2536
+ rootsInField.push(rootId);
2537
+ }
2538
+ }
2539
+ return fields;
2540
+ }
2541
+ function muteRootChanges(roots) {
2542
+ return {
2543
+ oldToNewId: newChangeAtomIdTransform(),
2544
+ newToOldId: newChangeAtomIdTransform(),
2545
+ nodeChanges: brand(roots.nodeChanges.clone()),
2546
+ detachLocations: roots.detachLocations.clone(),
2547
+ outputDetachLocations: newChangeAtomIdRangeMap(),
2548
+ };
2549
+ }
2550
+ export function validateChangeset(change, fieldKinds) {
2551
+ const unreachableNodes = brand(change.nodeToParent.clone());
2552
+ const unreachableCFKs = change.crossFieldKeys.clone();
2553
+ validateFieldChanges(fieldKinds, change, change.fieldChanges, undefined, unreachableNodes, unreachableCFKs);
2554
+ for (const [[revision, localId], node] of change.nodeChanges.entries()) {
2555
+ if (node.fieldChanges === undefined) {
2556
+ continue;
2557
+ }
2558
+ const nodeId = normalizeNodeId({ revision, localId }, change.nodeAliases);
2559
+ validateFieldChanges(fieldKinds, change, node.fieldChanges, nodeId, unreachableNodes, unreachableCFKs);
2560
+ }
2561
+ for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
2562
+ const detachId = { revision: detachIdKey[0], localId: detachIdKey[1] };
2563
+ const location = getNodeParent(change, nodeId);
2564
+ assert(areEqualChangeAtomIdOpts(location.root, detachId), "Inconsistent node location");
2565
+ const normalizedNodeId = normalizeNodeId(nodeId, change.nodeAliases);
2566
+ unreachableNodes.delete([normalizedNodeId.revision, normalizedNodeId.localId]);
2567
+ const fieldChanges = nodeChangeFromId(change.nodeChanges, change.nodeAliases, nodeId).fieldChanges;
2568
+ if (fieldChanges !== undefined) {
2569
+ validateFieldChanges(fieldKinds, change, fieldChanges, normalizedNodeId, unreachableNodes, unreachableCFKs);
2570
+ }
2571
+ }
2572
+ assert(unreachableNodes.size === 0, "Unreachable nodes found");
2573
+ assert(unreachableCFKs.entries().length === 0, "Unreachable cross-field keys found");
2574
+ }
2575
+ /**
2576
+ * Asserts that each node has a correct entry in `change.nodeToParent`,
2577
+ * and each cross field key has a correct entry in `change.crossFieldKeys`.
2578
+ * @returns the number of children found.
2579
+ */
2580
+ function validateFieldChanges(fieldKinds, change, fieldChanges, nodeParent, unreachableNodes, unreachableCFKs) {
2581
+ for (const [field, fieldChange] of fieldChanges.entries()) {
2582
+ const fieldId = { nodeId: nodeParent, field };
2583
+ const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
2584
+ for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
2585
+ const parentFieldId = getNodeParent(change, child);
2586
+ assert(parentFieldId.field !== undefined && areEqualFieldIds(parentFieldId.field, fieldId), 0xa4e /* Inconsistent node parentage */);
2587
+ unreachableNodes.delete([child.revision, child.localId]);
2588
+ }
2589
+ for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
2590
+ const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
2591
+ assert(fields.length > 0, "Unregistered cross-field key");
2592
+ for (const fieldFromLookup of fields) {
2593
+ assert(areEqualFieldIds(fieldFromLookup, fieldId), 0xa4f /* Inconsistent cross field keys */);
2594
+ }
2595
+ unreachableCFKs.delete(keyRange.key, keyRange.count);
2596
+ }
2597
+ }
2598
+ }
1734
2599
  //# sourceMappingURL=modularChangeFamily.js.map