@fluidframework/tree 2.74.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 (1318) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/api-report/tree.alpha.api.md +63 -9
  3. package/dist/alpha.d.ts +9 -0
  4. package/dist/codec/codec.d.ts +31 -3
  5. package/dist/codec/codec.d.ts.map +1 -1
  6. package/dist/codec/codec.js +10 -0
  7. package/dist/codec/codec.js.map +1 -1
  8. package/dist/codec/index.d.ts +2 -2
  9. package/dist/codec/index.d.ts.map +1 -1
  10. package/dist/codec/index.js +2 -2
  11. package/dist/codec/index.js.map +1 -1
  12. package/dist/codec/versioned/codec.d.ts +80 -22
  13. package/dist/codec/versioned/codec.d.ts.map +1 -1
  14. package/dist/codec/versioned/codec.js +137 -15
  15. package/dist/codec/versioned/codec.js.map +1 -1
  16. package/dist/codec/versioned/index.d.ts +1 -1
  17. package/dist/codec/versioned/index.d.ts.map +1 -1
  18. package/dist/codec/versioned/index.js +2 -2
  19. package/dist/codec/versioned/index.js.map +1 -1
  20. package/dist/core/change-family/changeFamily.d.ts +4 -1
  21. package/dist/core/change-family/changeFamily.d.ts.map +1 -1
  22. package/dist/core/change-family/changeFamily.js.map +1 -1
  23. package/dist/core/change-family/index.d.ts +1 -1
  24. package/dist/core/change-family/index.d.ts.map +1 -1
  25. package/dist/core/change-family/index.js.map +1 -1
  26. package/dist/core/index.d.ts +3 -3
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +7 -6
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/rebase/changeRebaser.d.ts +42 -3
  31. package/dist/core/rebase/changeRebaser.d.ts.map +1 -1
  32. package/dist/core/rebase/changeRebaser.js.map +1 -1
  33. package/dist/core/rebase/index.d.ts +2 -2
  34. package/dist/core/rebase/index.d.ts.map +1 -1
  35. package/dist/core/rebase/index.js +2 -2
  36. package/dist/core/rebase/index.js.map +1 -1
  37. package/dist/core/rebase/types.d.ts +52 -10
  38. package/dist/core/rebase/types.d.ts.map +1 -1
  39. package/dist/core/rebase/types.js +5 -12
  40. package/dist/core/rebase/types.js.map +1 -1
  41. package/dist/core/rebase/utils.d.ts.map +1 -1
  42. package/dist/core/rebase/utils.js +33 -11
  43. package/dist/core/rebase/utils.js.map +1 -1
  44. package/dist/core/schema-stored/schema.js +3 -3
  45. package/dist/core/schema-stored/schema.js.map +1 -1
  46. package/dist/core/tree/anchorSet.d.ts.map +1 -1
  47. package/dist/core/tree/anchorSet.js +4 -4
  48. package/dist/core/tree/anchorSet.js.map +1 -1
  49. package/dist/core/tree/detachedFieldIndex.d.ts +7 -2
  50. package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
  51. package/dist/core/tree/detachedFieldIndex.js +21 -56
  52. package/dist/core/tree/detachedFieldIndex.js.map +1 -1
  53. package/dist/core/tree/detachedFieldIndexCodecCommon.d.ts +18 -10
  54. package/dist/core/tree/detachedFieldIndexCodecCommon.d.ts.map +1 -1
  55. package/dist/core/tree/detachedFieldIndexCodecCommon.js +6 -6
  56. package/dist/core/tree/detachedFieldIndexCodecCommon.js.map +1 -1
  57. package/dist/core/tree/detachedFieldIndexCodecV1.d.ts +2 -3
  58. package/dist/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
  59. package/dist/core/tree/detachedFieldIndexCodecV1.js +4 -5
  60. package/dist/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
  61. package/dist/core/tree/detachedFieldIndexCodecV2.d.ts +2 -3
  62. package/dist/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
  63. package/dist/core/tree/detachedFieldIndexCodecV2.js +4 -6
  64. package/dist/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
  65. package/dist/core/tree/detachedFieldIndexCodecs.d.ts +5 -6
  66. package/dist/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
  67. package/dist/core/tree/detachedFieldIndexCodecs.js +11 -41
  68. package/dist/core/tree/detachedFieldIndexCodecs.js.map +1 -1
  69. package/dist/core/tree/index.d.ts +2 -2
  70. package/dist/core/tree/index.d.ts.map +1 -1
  71. package/dist/core/tree/index.js +5 -4
  72. package/dist/core/tree/index.js.map +1 -1
  73. package/dist/core/tree/mapTree.js +1 -1
  74. package/dist/core/tree/mapTree.js.map +1 -1
  75. package/dist/core/tree/pathTree.d.ts +11 -3
  76. package/dist/core/tree/pathTree.d.ts.map +1 -1
  77. package/dist/core/tree/pathTree.js +14 -2
  78. package/dist/core/tree/pathTree.js.map +1 -1
  79. package/dist/core/tree/sparseTree.d.ts.map +1 -1
  80. package/dist/core/tree/sparseTree.js +1 -0
  81. package/dist/core/tree/sparseTree.js.map +1 -1
  82. package/dist/core/tree/treeTextFormat.d.ts.map +1 -1
  83. package/dist/core/tree/treeTextFormat.js +5 -9
  84. package/dist/core/tree/treeTextFormat.js.map +1 -1
  85. package/dist/core/tree/visitDelta.d.ts.map +1 -1
  86. package/dist/core/tree/visitDelta.js +3 -2
  87. package/dist/core/tree/visitDelta.js.map +1 -1
  88. package/dist/core/tree/visitorUtils.d.ts.map +1 -1
  89. package/dist/core/tree/visitorUtils.js +58 -18
  90. package/dist/core/tree/visitorUtils.js.map +1 -1
  91. package/dist/feature-libraries/changeAtomIdBTree.d.ts +16 -0
  92. package/dist/feature-libraries/changeAtomIdBTree.d.ts.map +1 -0
  93. package/dist/feature-libraries/changeAtomIdBTree.js +30 -0
  94. package/dist/feature-libraries/changeAtomIdBTree.js.map +1 -0
  95. package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  96. package/dist/feature-libraries/chunked-forest/basicChunk.js +8 -1
  97. package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  98. package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
  99. package/dist/feature-libraries/chunked-forest/chunkTree.js +4 -1
  100. package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  101. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  102. package/dist/feature-libraries/chunked-forest/chunkedForest.js +9 -7
  103. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  104. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
  105. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js +7 -4
  106. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
  107. package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
  108. package/dist/feature-libraries/chunked-forest/codec/codecs.js +19 -11
  109. package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
  110. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js +4 -3
  111. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  112. package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +9 -5
  113. package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
  114. package/dist/feature-libraries/chunked-forest/uniformChunk.js +1 -1
  115. package/dist/feature-libraries/chunked-forest/uniformChunk.js.map +1 -1
  116. package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts +103 -44
  117. package/dist/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
  118. package/dist/feature-libraries/default-schema/defaultEditBuilder.js +251 -77
  119. package/dist/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  120. package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
  121. package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  122. package/dist/feature-libraries/default-schema/defaultFieldKinds.js +22 -3
  123. package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  124. package/dist/feature-libraries/default-schema/index.d.ts +2 -1
  125. package/dist/feature-libraries/default-schema/index.d.ts.map +1 -1
  126. package/dist/feature-libraries/default-schema/index.js +5 -2
  127. package/dist/feature-libraries/default-schema/index.js.map +1 -1
  128. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
  129. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
  130. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js +132 -0
  131. package/dist/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
  132. package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts +9 -6
  133. package/dist/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
  134. package/dist/feature-libraries/default-schema/mappedEditBuilder.js +21 -0
  135. package/dist/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
  136. package/dist/feature-libraries/deltaUtils.d.ts +1 -0
  137. package/dist/feature-libraries/deltaUtils.d.ts.map +1 -1
  138. package/dist/feature-libraries/deltaUtils.js +6 -1
  139. package/dist/feature-libraries/deltaUtils.js.map +1 -1
  140. package/dist/feature-libraries/flex-tree/context.d.ts +9 -0
  141. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  142. package/dist/feature-libraries/flex-tree/context.js +6 -0
  143. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  144. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
  145. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  146. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  147. package/dist/feature-libraries/flex-tree/lazyField.d.ts +8 -7
  148. package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  149. package/dist/feature-libraries/flex-tree/lazyField.js +44 -15
  150. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  151. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  152. package/dist/feature-libraries/forest-summary/forestSummarizer.js +3 -1
  153. package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  154. package/dist/feature-libraries/index.d.ts +5 -4
  155. package/dist/feature-libraries/index.d.ts.map +1 -1
  156. package/dist/feature-libraries/index.js +15 -8
  157. package/dist/feature-libraries/index.js.map +1 -1
  158. package/dist/feature-libraries/indexing/anchorTreeIndex.js +6 -6
  159. package/dist/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  160. package/dist/feature-libraries/mapTreeCursor.d.ts.map +1 -1
  161. package/dist/feature-libraries/mapTreeCursor.js +1 -0
  162. package/dist/feature-libraries/mapTreeCursor.js.map +1 -1
  163. package/dist/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
  164. package/dist/feature-libraries/mitigatedChangeFamily.js +12 -3
  165. package/dist/feature-libraries/mitigatedChangeFamily.js.map +1 -1
  166. package/dist/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
  167. package/dist/feature-libraries/modular-schema/comparison.js +12 -9
  168. package/dist/feature-libraries/modular-schema/comparison.js.map +1 -1
  169. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
  170. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  171. package/dist/feature-libraries/modular-schema/crossFieldQueries.js +4 -7
  172. package/dist/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  173. package/dist/feature-libraries/modular-schema/defaultRevisionReplacer.d.ts +25 -0
  174. package/dist/feature-libraries/modular-schema/defaultRevisionReplacer.d.ts.map +1 -0
  175. package/dist/feature-libraries/modular-schema/defaultRevisionReplacer.js +59 -0
  176. package/dist/feature-libraries/modular-schema/defaultRevisionReplacer.js.map +1 -0
  177. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +22 -53
  178. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  179. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  180. package/dist/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  181. package/dist/feature-libraries/modular-schema/genericFieldKind.js +7 -13
  182. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  183. package/dist/feature-libraries/modular-schema/genericFieldKindCodecs.js +2 -2
  184. package/dist/feature-libraries/modular-schema/genericFieldKindCodecs.js.map +1 -1
  185. package/dist/feature-libraries/modular-schema/index.d.ts +7 -5
  186. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  187. package/dist/feature-libraries/modular-schema/index.js +12 -8
  188. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  189. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +32 -0
  190. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -0
  191. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +409 -0
  192. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -0
  193. package/dist/feature-libraries/modular-schema/modularChangeCodecV2.d.ts +15 -0
  194. package/dist/feature-libraries/modular-schema/modularChangeCodecV2.d.ts.map +1 -0
  195. package/dist/feature-libraries/modular-schema/modularChangeCodecV2.js +31 -0
  196. package/dist/feature-libraries/modular-schema/modularChangeCodecV2.js.map +1 -0
  197. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
  198. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
  199. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js +393 -0
  200. package/dist/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
  201. package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts +2 -2
  202. package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  203. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +32 -288
  204. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  205. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +60 -26
  206. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  207. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +1519 -615
  208. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  209. package/{lib/feature-libraries/modular-schema/modularChangeFormat.d.ts → dist/feature-libraries/modular-schema/modularChangeFormatV1.d.ts} +5 -6
  210. package/dist/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -0
  211. package/dist/feature-libraries/modular-schema/{modularChangeFormat.js → modularChangeFormatV1.js} +7 -7
  212. package/dist/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -0
  213. package/dist/feature-libraries/modular-schema/modularChangeFormatV2.d.ts +112 -0
  214. package/dist/feature-libraries/modular-schema/modularChangeFormatV2.d.ts.map +1 -0
  215. package/dist/feature-libraries/modular-schema/modularChangeFormatV2.js +21 -0
  216. package/dist/feature-libraries/modular-schema/modularChangeFormatV2.js.map +1 -0
  217. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
  218. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
  219. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js +32 -0
  220. package/dist/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
  221. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +59 -13
  222. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  223. package/dist/feature-libraries/modular-schema/modularChangeTypes.js +3 -3
  224. package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  225. package/dist/feature-libraries/node-identifier/mockNodeIdentifierManager.js +1 -1
  226. package/dist/feature-libraries/node-identifier/mockNodeIdentifierManager.js.map +1 -1
  227. package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  228. package/dist/feature-libraries/object-forest/objectForest.js +7 -5
  229. package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
  230. package/dist/feature-libraries/optional-field/index.d.ts +2 -2
  231. package/dist/feature-libraries/optional-field/index.d.ts.map +1 -1
  232. package/dist/feature-libraries/optional-field/index.js +1 -2
  233. package/dist/feature-libraries/optional-field/index.js.map +1 -1
  234. package/dist/feature-libraries/optional-field/optionalField.d.ts +5 -26
  235. package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  236. package/dist/feature-libraries/optional-field/optionalField.js +223 -450
  237. package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
  238. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
  239. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
  240. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +31 -0
  241. package/dist/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
  242. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
  243. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
  244. package/dist/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
  245. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
  246. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
  247. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js +57 -28
  248. package/dist/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
  249. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
  250. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
  251. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js +57 -0
  252. package/dist/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
  253. package/dist/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
  254. package/dist/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
  255. package/dist/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
  256. package/dist/feature-libraries/schema-edits/schemaChangeCodecs.d.ts.map +1 -1
  257. package/dist/feature-libraries/schema-edits/schemaChangeCodecs.js +14 -2
  258. package/dist/feature-libraries/schema-edits/schemaChangeCodecs.js.map +1 -1
  259. package/dist/feature-libraries/schema-index/codec.d.ts +7 -21
  260. package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
  261. package/dist/feature-libraries/schema-index/codec.js +28 -70
  262. package/dist/feature-libraries/schema-index/codec.js.map +1 -1
  263. package/dist/feature-libraries/schema-index/index.d.ts +2 -2
  264. package/dist/feature-libraries/schema-index/index.d.ts.map +1 -1
  265. package/dist/feature-libraries/schema-index/index.js +2 -5
  266. package/dist/feature-libraries/schema-index/index.js.map +1 -1
  267. package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts +1 -9
  268. package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
  269. package/dist/feature-libraries/schema-index/schemaSummarizer.js +1 -12
  270. package/dist/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  271. package/dist/feature-libraries/schemaChecker.d.ts.map +1 -1
  272. package/dist/feature-libraries/schemaChecker.js +11 -6
  273. package/dist/feature-libraries/schemaChecker.js.map +1 -1
  274. package/dist/feature-libraries/sequence-field/compose.d.ts +6 -7
  275. package/dist/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  276. package/dist/feature-libraries/sequence-field/compose.js +91 -263
  277. package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
  278. package/dist/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
  279. package/dist/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
  280. package/dist/feature-libraries/sequence-field/helperTypes.js.map +1 -1
  281. package/dist/feature-libraries/sequence-field/index.d.ts +2 -3
  282. package/dist/feature-libraries/sequence-field/index.d.ts.map +1 -1
  283. package/dist/feature-libraries/sequence-field/index.js +1 -3
  284. package/dist/feature-libraries/sequence-field/index.js.map +1 -1
  285. package/dist/feature-libraries/sequence-field/invert.d.ts +3 -3
  286. package/dist/feature-libraries/sequence-field/invert.d.ts.map +1 -1
  287. package/dist/feature-libraries/sequence-field/invert.js +67 -168
  288. package/dist/feature-libraries/sequence-field/invert.js.map +1 -1
  289. package/dist/feature-libraries/sequence-field/markQueue.d.ts +2 -2
  290. package/dist/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  291. package/dist/feature-libraries/sequence-field/markQueue.js.map +1 -1
  292. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
  293. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  294. package/dist/feature-libraries/sequence-field/moveEffectTable.js +10 -87
  295. package/dist/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  296. package/dist/feature-libraries/sequence-field/rebase.d.ts +3 -3
  297. package/dist/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
  298. package/dist/feature-libraries/sequence-field/rebase.js +120 -119
  299. package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
  300. package/dist/feature-libraries/sequence-field/replaceRevisions.d.ts +2 -2
  301. package/dist/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
  302. package/dist/feature-libraries/sequence-field/replaceRevisions.js +35 -43
  303. package/dist/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
  304. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  305. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
  306. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  307. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
  308. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
  309. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js +365 -179
  310. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
  311. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
  312. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js +20 -60
  313. package/dist/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
  314. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
  315. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
  316. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
  317. package/dist/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
  318. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
  319. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
  320. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js +20 -112
  321. package/dist/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
  322. package/dist/feature-libraries/sequence-field/types.d.ts +30 -59
  323. package/dist/feature-libraries/sequence-field/types.d.ts.map +1 -1
  324. package/dist/feature-libraries/sequence-field/types.js.map +1 -1
  325. package/dist/feature-libraries/sequence-field/utils.d.ts +15 -24
  326. package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  327. package/dist/feature-libraries/sequence-field/utils.js +148 -318
  328. package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
  329. package/dist/feature-libraries/treeCursorUtils.js +7 -7
  330. package/dist/feature-libraries/treeCursorUtils.js.map +1 -1
  331. package/dist/feature-libraries/treeTextCursor.js +2 -2
  332. package/dist/feature-libraries/treeTextCursor.js.map +1 -1
  333. package/dist/feature-libraries/valueUtilities.d.ts.map +1 -1
  334. package/dist/feature-libraries/valueUtilities.js +16 -8
  335. package/dist/feature-libraries/valueUtilities.js.map +1 -1
  336. package/dist/index.d.ts +4 -3
  337. package/dist/index.d.ts.map +1 -1
  338. package/dist/index.js +4 -1
  339. package/dist/index.js.map +1 -1
  340. package/dist/packageVersion.d.ts +1 -1
  341. package/dist/packageVersion.d.ts.map +1 -1
  342. package/dist/packageVersion.js +1 -1
  343. package/dist/packageVersion.js.map +1 -1
  344. package/dist/shared-tree/index.d.ts +1 -1
  345. package/dist/shared-tree/index.d.ts.map +1 -1
  346. package/dist/shared-tree/index.js.map +1 -1
  347. package/dist/shared-tree/schematizeTree.d.ts +4 -4
  348. package/dist/shared-tree/schematizeTree.d.ts.map +1 -1
  349. package/dist/shared-tree/schematizeTree.js +2 -1
  350. package/dist/shared-tree/schematizeTree.js.map +1 -1
  351. package/dist/shared-tree/schematizingTreeView.d.ts +3 -8
  352. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  353. package/dist/shared-tree/schematizingTreeView.js +58 -44
  354. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  355. package/dist/shared-tree/sharedTree.d.ts +9 -3
  356. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  357. package/dist/shared-tree/sharedTree.js +72 -44
  358. package/dist/shared-tree/sharedTree.js.map +1 -1
  359. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
  360. package/dist/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  361. package/dist/shared-tree/sharedTreeChangeCodecs.js +10 -8
  362. package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  363. package/dist/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  364. package/dist/shared-tree/sharedTreeChangeEnricher.js +5 -3
  365. package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  366. package/dist/shared-tree/sharedTreeChangeFamily.d.ts +7 -6
  367. package/dist/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
  368. package/dist/shared-tree/sharedTreeChangeFamily.js +29 -18
  369. package/dist/shared-tree/sharedTreeChangeFamily.js.map +1 -1
  370. package/dist/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
  371. package/dist/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
  372. package/dist/shared-tree/sharedTreeEditBuilder.js +14 -7
  373. package/dist/shared-tree/sharedTreeEditBuilder.js.map +1 -1
  374. package/dist/shared-tree/treeAlpha.d.ts +35 -29
  375. package/dist/shared-tree/treeAlpha.d.ts.map +1 -1
  376. package/dist/shared-tree/treeAlpha.js +21 -23
  377. package/dist/shared-tree/treeAlpha.js.map +1 -1
  378. package/dist/shared-tree/treeCheckout.d.ts +11 -10
  379. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  380. package/dist/shared-tree/treeCheckout.js +93 -25
  381. package/dist/shared-tree/treeCheckout.js.map +1 -1
  382. package/dist/shared-tree-core/branch.d.ts +6 -3
  383. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  384. package/dist/shared-tree-core/branch.js +13 -6
  385. package/dist/shared-tree-core/branch.js.map +1 -1
  386. package/dist/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
  387. package/dist/shared-tree-core/branchCommitEnricher.js +1 -1
  388. package/dist/shared-tree-core/branchCommitEnricher.js.map +1 -1
  389. package/dist/shared-tree-core/editManager.d.ts +2 -2
  390. package/dist/shared-tree-core/editManager.d.ts.map +1 -1
  391. package/dist/shared-tree-core/editManager.js +21 -15
  392. package/dist/shared-tree-core/editManager.js.map +1 -1
  393. package/dist/shared-tree-core/editManagerCodecs.d.ts +4 -0
  394. package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  395. package/dist/shared-tree-core/editManagerCodecs.js +18 -5
  396. package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
  397. package/dist/shared-tree-core/editManagerFormatCommons.d.ts +2 -0
  398. package/dist/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
  399. package/dist/shared-tree-core/editManagerFormatCommons.js +12 -0
  400. package/dist/shared-tree-core/editManagerFormatCommons.js.map +1 -1
  401. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
  402. package/dist/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
  403. package/dist/shared-tree-core/editManagerFormatV1toV4.js +2 -0
  404. package/dist/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
  405. package/dist/shared-tree-core/editManagerSummarizer.js +3 -3
  406. package/dist/shared-tree-core/editManagerSummarizer.js.map +1 -1
  407. package/dist/shared-tree-core/index.d.ts +2 -2
  408. package/dist/shared-tree-core/index.d.ts.map +1 -1
  409. package/dist/shared-tree-core/index.js +3 -1
  410. package/dist/shared-tree-core/index.js.map +1 -1
  411. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
  412. package/dist/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
  413. package/dist/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
  414. package/dist/shared-tree-core/messageCodecVSharedBranches.d.ts.map +1 -1
  415. package/dist/shared-tree-core/messageCodecVSharedBranches.js +2 -1
  416. package/dist/shared-tree-core/messageCodecVSharedBranches.js.map +1 -1
  417. package/dist/shared-tree-core/messageCodecs.d.ts +4 -0
  418. package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
  419. package/dist/shared-tree-core/messageCodecs.js +17 -5
  420. package/dist/shared-tree-core/messageCodecs.js.map +1 -1
  421. package/dist/shared-tree-core/messageFormat.d.ts +2 -0
  422. package/dist/shared-tree-core/messageFormat.d.ts.map +1 -1
  423. package/dist/shared-tree-core/messageFormat.js +12 -0
  424. package/dist/shared-tree-core/messageFormat.js.map +1 -1
  425. package/dist/shared-tree-core/messageFormatV1ToV4.d.ts +3 -2
  426. package/dist/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
  427. package/dist/shared-tree-core/messageFormatV1ToV4.js +9 -1
  428. package/dist/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
  429. package/dist/shared-tree-core/sequenceIdUtils.d.ts.map +1 -1
  430. package/dist/shared-tree-core/sequenceIdUtils.js +4 -4
  431. package/dist/shared-tree-core/sequenceIdUtils.js.map +1 -1
  432. package/dist/shared-tree-core/sharedTreeCore.d.ts +1 -0
  433. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  434. package/dist/shared-tree-core/sharedTreeCore.js +13 -9
  435. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  436. package/dist/shared-tree-core/transaction.d.ts +25 -8
  437. package/dist/shared-tree-core/transaction.d.ts.map +1 -1
  438. package/dist/shared-tree-core/transaction.js +65 -30
  439. package/dist/shared-tree-core/transaction.js.map +1 -1
  440. package/dist/shared-tree-core/transactionEnricher.d.ts +2 -2
  441. package/dist/shared-tree-core/transactionEnricher.d.ts.map +1 -1
  442. package/dist/shared-tree-core/transactionEnricher.js +3 -3
  443. package/dist/shared-tree-core/transactionEnricher.js.map +1 -1
  444. package/dist/simple-tree/api/configuration.js +1 -1
  445. package/dist/simple-tree/api/configuration.js.map +1 -1
  446. package/dist/simple-tree/api/customTree.d.ts.map +1 -1
  447. package/dist/simple-tree/api/customTree.js +13 -9
  448. package/dist/simple-tree/api/customTree.js.map +1 -1
  449. package/dist/simple-tree/api/discrepancies.d.ts.map +1 -1
  450. package/dist/simple-tree/api/discrepancies.js +21 -17
  451. package/dist/simple-tree/api/discrepancies.js.map +1 -1
  452. package/dist/simple-tree/api/index.d.ts +2 -2
  453. package/dist/simple-tree/api/index.d.ts.map +1 -1
  454. package/dist/simple-tree/api/index.js +2 -1
  455. package/dist/simple-tree/api/index.js.map +1 -1
  456. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  457. package/dist/simple-tree/api/schemaFactory.js +12 -8
  458. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  459. package/dist/simple-tree/api/schemaFactoryAlpha.js +1 -1
  460. package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  461. package/dist/simple-tree/api/schemaFactoryBeta.js +1 -1
  462. package/dist/simple-tree/api/schemaFactoryBeta.js.map +1 -1
  463. package/dist/simple-tree/api/schemaFromSimple.js +18 -9
  464. package/dist/simple-tree/api/schemaFromSimple.js.map +1 -1
  465. package/dist/simple-tree/api/simpleSchemaCodec.js +10 -5
  466. package/dist/simple-tree/api/simpleSchemaCodec.js.map +1 -1
  467. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  468. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +19 -15
  469. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  470. package/dist/simple-tree/api/simpleTreeIndex.js +10 -10
  471. package/dist/simple-tree/api/simpleTreeIndex.js.map +1 -1
  472. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
  473. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
  474. package/dist/simple-tree/api/snapshotCompatibilityChecker.js +297 -1
  475. package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  476. package/dist/simple-tree/api/storedSchema.d.ts.map +1 -1
  477. package/dist/simple-tree/api/storedSchema.js +2 -3
  478. package/dist/simple-tree/api/storedSchema.js.map +1 -1
  479. package/dist/simple-tree/api/transactionTypes.d.ts +17 -4
  480. package/dist/simple-tree/api/transactionTypes.d.ts.map +1 -1
  481. package/dist/simple-tree/api/transactionTypes.js.map +1 -1
  482. package/dist/simple-tree/api/tree.d.ts +3 -1
  483. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  484. package/dist/simple-tree/api/tree.js.map +1 -1
  485. package/dist/simple-tree/api/treeNodeApi.d.ts.map +1 -1
  486. package/dist/simple-tree/api/treeNodeApi.js +21 -13
  487. package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
  488. package/dist/simple-tree/api/verboseTree.d.ts.map +1 -1
  489. package/dist/simple-tree/api/verboseTree.js +14 -9
  490. package/dist/simple-tree/api/verboseTree.js.map +1 -1
  491. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  492. package/dist/simple-tree/core/treeNodeKernel.js +12 -5
  493. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  494. package/dist/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
  495. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  496. package/dist/simple-tree/core/unhydratedFlexTree.js +79 -23
  497. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  498. package/dist/simple-tree/fieldSchema.d.ts +4 -4
  499. package/dist/simple-tree/fieldSchema.d.ts.map +1 -1
  500. package/dist/simple-tree/fieldSchema.js.map +1 -1
  501. package/dist/simple-tree/getTreeNodeForField.d.ts.map +1 -1
  502. package/dist/simple-tree/getTreeNodeForField.js +2 -1
  503. package/dist/simple-tree/getTreeNodeForField.js.map +1 -1
  504. package/dist/simple-tree/index.d.ts +3 -3
  505. package/dist/simple-tree/index.d.ts.map +1 -1
  506. package/dist/simple-tree/index.js +4 -3
  507. package/dist/simple-tree/index.js.map +1 -1
  508. package/dist/simple-tree/leafNodeSchema.js +9 -6
  509. package/dist/simple-tree/leafNodeSchema.js.map +1 -1
  510. package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  511. package/dist/simple-tree/node-kinds/array/arrayNode.js +22 -20
  512. package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  513. package/dist/simple-tree/node-kinds/common.d.ts.map +1 -1
  514. package/dist/simple-tree/node-kinds/common.js +1 -1
  515. package/dist/simple-tree/node-kinds/common.js.map +1 -1
  516. package/dist/simple-tree/node-kinds/map/mapNode.js +2 -2
  517. package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  518. package/dist/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  519. package/dist/simple-tree/node-kinds/object/objectNode.js +21 -22
  520. package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  521. package/dist/simple-tree/node-kinds/record/recordNode.js +6 -5
  522. package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  523. package/dist/simple-tree/prepareForInsertion.d.ts +54 -47
  524. package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
  525. package/dist/simple-tree/prepareForInsertion.js +184 -126
  526. package/dist/simple-tree/prepareForInsertion.js.map +1 -1
  527. package/dist/simple-tree/toStoredSchema.d.ts.map +1 -1
  528. package/dist/simple-tree/toStoredSchema.js +9 -5
  529. package/dist/simple-tree/toStoredSchema.js.map +1 -1
  530. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
  531. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
  532. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +31 -13
  533. package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  534. package/dist/text/index.d.ts +6 -0
  535. package/dist/text/index.d.ts.map +1 -0
  536. package/dist/text/index.js +10 -0
  537. package/dist/text/index.js.map +1 -0
  538. package/dist/text/textDomain.d.ts +138 -0
  539. package/dist/text/textDomain.d.ts.map +1 -0
  540. package/dist/text/textDomain.js +121 -0
  541. package/dist/text/textDomain.js.map +1 -0
  542. package/dist/treeFactory.d.ts.map +1 -1
  543. package/dist/treeFactory.js +17 -3
  544. package/dist/treeFactory.js.map +1 -1
  545. package/dist/util/bTreeUtils.d.ts +12 -4
  546. package/dist/util/bTreeUtils.d.ts.map +1 -1
  547. package/dist/util/bTreeUtils.js +15 -19
  548. package/dist/util/bTreeUtils.js.map +1 -1
  549. package/dist/util/breakable.js +7 -9
  550. package/dist/util/breakable.js.map +1 -1
  551. package/dist/util/idAllocator.d.ts.map +1 -1
  552. package/dist/util/idAllocator.js +1 -2
  553. package/dist/util/idAllocator.js.map +1 -1
  554. package/dist/util/index.d.ts +1 -1
  555. package/dist/util/index.d.ts.map +1 -1
  556. package/dist/util/index.js +2 -1
  557. package/dist/util/index.js.map +1 -1
  558. package/dist/util/nestedMap.d.ts.map +1 -1
  559. package/dist/util/nestedMap.js +13 -13
  560. package/dist/util/nestedMap.js.map +1 -1
  561. package/dist/util/rangeMap.d.ts +24 -12
  562. package/dist/util/rangeMap.d.ts.map +1 -1
  563. package/dist/util/rangeMap.js +46 -6
  564. package/dist/util/rangeMap.js.map +1 -1
  565. package/dist/util/utils.d.ts.map +1 -1
  566. package/dist/util/utils.js +16 -15
  567. package/dist/util/utils.js.map +1 -1
  568. package/docs/user-facing/merge-semantics.md +3 -2
  569. package/eslint.config.mts +5 -48
  570. package/lib/alpha.d.ts +9 -0
  571. package/lib/codec/codec.d.ts +31 -3
  572. package/lib/codec/codec.d.ts.map +1 -1
  573. package/lib/codec/codec.js +10 -0
  574. package/lib/codec/codec.js.map +1 -1
  575. package/lib/codec/index.d.ts +2 -2
  576. package/lib/codec/index.d.ts.map +1 -1
  577. package/lib/codec/index.js +1 -1
  578. package/lib/codec/index.js.map +1 -1
  579. package/lib/codec/versioned/codec.d.ts +80 -22
  580. package/lib/codec/versioned/codec.d.ts.map +1 -1
  581. package/lib/codec/versioned/codec.js +138 -15
  582. package/lib/codec/versioned/codec.js.map +1 -1
  583. package/lib/codec/versioned/index.d.ts +1 -1
  584. package/lib/codec/versioned/index.d.ts.map +1 -1
  585. package/lib/codec/versioned/index.js +1 -1
  586. package/lib/codec/versioned/index.js.map +1 -1
  587. package/lib/core/change-family/changeFamily.d.ts +4 -1
  588. package/lib/core/change-family/changeFamily.d.ts.map +1 -1
  589. package/lib/core/change-family/changeFamily.js.map +1 -1
  590. package/lib/core/change-family/index.d.ts +1 -1
  591. package/lib/core/change-family/index.d.ts.map +1 -1
  592. package/lib/core/change-family/index.js.map +1 -1
  593. package/lib/core/index.d.ts +3 -3
  594. package/lib/core/index.d.ts.map +1 -1
  595. package/lib/core/index.js +2 -2
  596. package/lib/core/index.js.map +1 -1
  597. package/lib/core/rebase/changeRebaser.d.ts +42 -3
  598. package/lib/core/rebase/changeRebaser.d.ts.map +1 -1
  599. package/lib/core/rebase/changeRebaser.js.map +1 -1
  600. package/lib/core/rebase/index.d.ts +2 -2
  601. package/lib/core/rebase/index.d.ts.map +1 -1
  602. package/lib/core/rebase/index.js +1 -1
  603. package/lib/core/rebase/index.js.map +1 -1
  604. package/lib/core/rebase/types.d.ts +52 -10
  605. package/lib/core/rebase/types.d.ts.map +1 -1
  606. package/lib/core/rebase/types.js +3 -10
  607. package/lib/core/rebase/types.js.map +1 -1
  608. package/lib/core/rebase/utils.d.ts.map +1 -1
  609. package/lib/core/rebase/utils.js +33 -11
  610. package/lib/core/rebase/utils.js.map +1 -1
  611. package/lib/core/schema-stored/schema.js +3 -3
  612. package/lib/core/schema-stored/schema.js.map +1 -1
  613. package/lib/core/tree/anchorSet.d.ts.map +1 -1
  614. package/lib/core/tree/anchorSet.js +4 -4
  615. package/lib/core/tree/anchorSet.js.map +1 -1
  616. package/lib/core/tree/detachedFieldIndex.d.ts +7 -2
  617. package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
  618. package/lib/core/tree/detachedFieldIndex.js +23 -58
  619. package/lib/core/tree/detachedFieldIndex.js.map +1 -1
  620. package/lib/core/tree/detachedFieldIndexCodecCommon.d.ts +18 -10
  621. package/lib/core/tree/detachedFieldIndexCodecCommon.d.ts.map +1 -1
  622. package/lib/core/tree/detachedFieldIndexCodecCommon.js +4 -4
  623. package/lib/core/tree/detachedFieldIndexCodecCommon.js.map +1 -1
  624. package/lib/core/tree/detachedFieldIndexCodecV1.d.ts +2 -3
  625. package/lib/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
  626. package/lib/core/tree/detachedFieldIndexCodecV1.js +4 -5
  627. package/lib/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
  628. package/lib/core/tree/detachedFieldIndexCodecV2.d.ts +2 -3
  629. package/lib/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
  630. package/lib/core/tree/detachedFieldIndexCodecV2.js +4 -6
  631. package/lib/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
  632. package/lib/core/tree/detachedFieldIndexCodecs.d.ts +5 -6
  633. package/lib/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
  634. package/lib/core/tree/detachedFieldIndexCodecs.js +12 -39
  635. package/lib/core/tree/detachedFieldIndexCodecs.js.map +1 -1
  636. package/lib/core/tree/index.d.ts +2 -2
  637. package/lib/core/tree/index.d.ts.map +1 -1
  638. package/lib/core/tree/index.js +2 -2
  639. package/lib/core/tree/index.js.map +1 -1
  640. package/lib/core/tree/mapTree.js +1 -1
  641. package/lib/core/tree/mapTree.js.map +1 -1
  642. package/lib/core/tree/pathTree.d.ts +11 -3
  643. package/lib/core/tree/pathTree.d.ts.map +1 -1
  644. package/lib/core/tree/pathTree.js +12 -1
  645. package/lib/core/tree/pathTree.js.map +1 -1
  646. package/lib/core/tree/sparseTree.d.ts.map +1 -1
  647. package/lib/core/tree/sparseTree.js +1 -0
  648. package/lib/core/tree/sparseTree.js.map +1 -1
  649. package/lib/core/tree/treeTextFormat.d.ts.map +1 -1
  650. package/lib/core/tree/treeTextFormat.js +5 -9
  651. package/lib/core/tree/treeTextFormat.js.map +1 -1
  652. package/lib/core/tree/visitDelta.d.ts.map +1 -1
  653. package/lib/core/tree/visitDelta.js +3 -2
  654. package/lib/core/tree/visitDelta.js.map +1 -1
  655. package/lib/core/tree/visitorUtils.d.ts.map +1 -1
  656. package/lib/core/tree/visitorUtils.js +58 -18
  657. package/lib/core/tree/visitorUtils.js.map +1 -1
  658. package/lib/feature-libraries/changeAtomIdBTree.d.ts +16 -0
  659. package/lib/feature-libraries/changeAtomIdBTree.d.ts.map +1 -0
  660. package/lib/feature-libraries/changeAtomIdBTree.js +24 -0
  661. package/lib/feature-libraries/changeAtomIdBTree.js.map +1 -0
  662. package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  663. package/lib/feature-libraries/chunked-forest/basicChunk.js +9 -2
  664. package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  665. package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
  666. package/lib/feature-libraries/chunked-forest/chunkTree.js +4 -1
  667. package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  668. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  669. package/lib/feature-libraries/chunked-forest/chunkedForest.js +9 -7
  670. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  671. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
  672. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js +7 -4
  673. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
  674. package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
  675. package/lib/feature-libraries/chunked-forest/codec/codecs.js +19 -11
  676. package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
  677. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js +4 -3
  678. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  679. package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +9 -5
  680. package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
  681. package/lib/feature-libraries/chunked-forest/uniformChunk.js +1 -1
  682. package/lib/feature-libraries/chunked-forest/uniformChunk.js.map +1 -1
  683. package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts +103 -44
  684. package/lib/feature-libraries/default-schema/defaultEditBuilder.d.ts.map +1 -1
  685. package/lib/feature-libraries/default-schema/defaultEditBuilder.js +249 -78
  686. package/lib/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  687. package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts +1 -1
  688. package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  689. package/lib/feature-libraries/default-schema/defaultFieldKinds.js +22 -3
  690. package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  691. package/lib/feature-libraries/default-schema/index.d.ts +2 -1
  692. package/lib/feature-libraries/default-schema/index.d.ts.map +1 -1
  693. package/lib/feature-libraries/default-schema/index.js +2 -1
  694. package/lib/feature-libraries/default-schema/index.js.map +1 -1
  695. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts +38 -0
  696. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.d.ts.map +1 -0
  697. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js +128 -0
  698. package/lib/feature-libraries/default-schema/locationBasedEditBuilder.js.map +1 -0
  699. package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts +9 -6
  700. package/lib/feature-libraries/default-schema/mappedEditBuilder.d.ts.map +1 -1
  701. package/lib/feature-libraries/default-schema/mappedEditBuilder.js +21 -0
  702. package/lib/feature-libraries/default-schema/mappedEditBuilder.js.map +1 -1
  703. package/lib/feature-libraries/deltaUtils.d.ts +1 -0
  704. package/lib/feature-libraries/deltaUtils.d.ts.map +1 -1
  705. package/lib/feature-libraries/deltaUtils.js +5 -1
  706. package/lib/feature-libraries/deltaUtils.js.map +1 -1
  707. package/lib/feature-libraries/flex-tree/context.d.ts +9 -0
  708. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  709. package/lib/feature-libraries/flex-tree/context.js +6 -0
  710. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  711. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +6 -6
  712. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  713. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  714. package/lib/feature-libraries/flex-tree/lazyField.d.ts +8 -7
  715. package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  716. package/lib/feature-libraries/flex-tree/lazyField.js +45 -16
  717. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  718. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  719. package/lib/feature-libraries/forest-summary/forestSummarizer.js +3 -1
  720. package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  721. package/lib/feature-libraries/index.d.ts +5 -4
  722. package/lib/feature-libraries/index.d.ts.map +1 -1
  723. package/lib/feature-libraries/index.js +5 -4
  724. package/lib/feature-libraries/index.js.map +1 -1
  725. package/lib/feature-libraries/indexing/anchorTreeIndex.js +6 -6
  726. package/lib/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
  727. package/lib/feature-libraries/mapTreeCursor.d.ts.map +1 -1
  728. package/lib/feature-libraries/mapTreeCursor.js +2 -1
  729. package/lib/feature-libraries/mapTreeCursor.js.map +1 -1
  730. package/lib/feature-libraries/mitigatedChangeFamily.d.ts.map +1 -1
  731. package/lib/feature-libraries/mitigatedChangeFamily.js +12 -3
  732. package/lib/feature-libraries/mitigatedChangeFamily.js.map +1 -1
  733. package/lib/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
  734. package/lib/feature-libraries/modular-schema/comparison.js +12 -9
  735. package/lib/feature-libraries/modular-schema/comparison.js.map +1 -1
  736. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts +97 -21
  737. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  738. package/lib/feature-libraries/modular-schema/crossFieldQueries.js +3 -5
  739. package/lib/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  740. package/lib/feature-libraries/modular-schema/defaultRevisionReplacer.d.ts +25 -0
  741. package/lib/feature-libraries/modular-schema/defaultRevisionReplacer.d.ts.map +1 -0
  742. package/lib/feature-libraries/modular-schema/defaultRevisionReplacer.js +55 -0
  743. package/lib/feature-libraries/modular-schema/defaultRevisionReplacer.js.map +1 -0
  744. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +22 -53
  745. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  746. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  747. package/lib/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  748. package/lib/feature-libraries/modular-schema/genericFieldKind.js +8 -14
  749. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  750. package/lib/feature-libraries/modular-schema/genericFieldKindCodecs.js +1 -1
  751. package/lib/feature-libraries/modular-schema/genericFieldKindCodecs.js.map +1 -1
  752. package/lib/feature-libraries/modular-schema/index.d.ts +7 -5
  753. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  754. package/lib/feature-libraries/modular-schema/index.js +5 -3
  755. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  756. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +32 -0
  757. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -0
  758. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +402 -0
  759. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -0
  760. package/lib/feature-libraries/modular-schema/modularChangeCodecV2.d.ts +15 -0
  761. package/lib/feature-libraries/modular-schema/modularChangeCodecV2.d.ts.map +1 -0
  762. package/lib/feature-libraries/modular-schema/modularChangeCodecV2.js +27 -0
  763. package/lib/feature-libraries/modular-schema/modularChangeCodecV2.js.map +1 -0
  764. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts +15 -0
  765. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.d.ts.map +1 -0
  766. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js +389 -0
  767. package/lib/feature-libraries/modular-schema/modularChangeCodecV3.js.map +1 -0
  768. package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts +2 -2
  769. package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  770. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +34 -290
  771. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  772. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +60 -26
  773. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  774. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1441 -546
  775. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  776. package/{dist/feature-libraries/modular-schema/modularChangeFormat.d.ts → lib/feature-libraries/modular-schema/modularChangeFormatV1.d.ts} +5 -6
  777. package/lib/feature-libraries/modular-schema/modularChangeFormatV1.d.ts.map +1 -0
  778. package/lib/feature-libraries/modular-schema/{modularChangeFormat.js → modularChangeFormatV1.js} +4 -4
  779. package/lib/feature-libraries/modular-schema/modularChangeFormatV1.js.map +1 -0
  780. package/lib/feature-libraries/modular-schema/modularChangeFormatV2.d.ts +112 -0
  781. package/lib/feature-libraries/modular-schema/modularChangeFormatV2.d.ts.map +1 -0
  782. package/lib/feature-libraries/modular-schema/modularChangeFormatV2.js +18 -0
  783. package/lib/feature-libraries/modular-schema/modularChangeFormatV2.js.map +1 -0
  784. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts +146 -0
  785. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.d.ts.map +1 -0
  786. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js +29 -0
  787. package/lib/feature-libraries/modular-schema/modularChangeFormatV3.js.map +1 -0
  788. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +59 -13
  789. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  790. package/lib/feature-libraries/modular-schema/modularChangeTypes.js +1 -1
  791. package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  792. package/lib/feature-libraries/node-identifier/mockNodeIdentifierManager.js +1 -1
  793. package/lib/feature-libraries/node-identifier/mockNodeIdentifierManager.js.map +1 -1
  794. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  795. package/lib/feature-libraries/object-forest/objectForest.js +7 -5
  796. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  797. package/lib/feature-libraries/optional-field/index.d.ts +2 -2
  798. package/lib/feature-libraries/optional-field/index.d.ts.map +1 -1
  799. package/lib/feature-libraries/optional-field/index.js +1 -1
  800. package/lib/feature-libraries/optional-field/index.js.map +1 -1
  801. package/lib/feature-libraries/optional-field/optionalField.d.ts +5 -26
  802. package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  803. package/lib/feature-libraries/optional-field/optionalField.js +223 -448
  804. package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
  805. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts +23 -0
  806. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.d.ts.map +1 -0
  807. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js +27 -0
  808. package/lib/feature-libraries/optional-field/optionalFieldChangeFormatV3.js.map +1 -0
  809. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts +24 -33
  810. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.d.ts.map +1 -1
  811. package/lib/feature-libraries/optional-field/optionalFieldChangeTypes.js.map +1 -1
  812. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts +1 -1
  813. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.d.ts.map +1 -1
  814. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js +55 -26
  815. package/lib/feature-libraries/optional-field/optionalFieldCodecV2.js.map +1 -1
  816. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts +12 -0
  817. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.d.ts.map +1 -0
  818. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js +53 -0
  819. package/lib/feature-libraries/optional-field/optionalFieldCodecV3.js.map +1 -0
  820. package/lib/feature-libraries/optional-field/optionalFieldCodecs.d.ts.map +1 -1
  821. package/lib/feature-libraries/optional-field/optionalFieldCodecs.js +5 -1
  822. package/lib/feature-libraries/optional-field/optionalFieldCodecs.js.map +1 -1
  823. package/lib/feature-libraries/schema-edits/schemaChangeCodecs.d.ts.map +1 -1
  824. package/lib/feature-libraries/schema-edits/schemaChangeCodecs.js +15 -3
  825. package/lib/feature-libraries/schema-edits/schemaChangeCodecs.js.map +1 -1
  826. package/lib/feature-libraries/schema-index/codec.d.ts +7 -21
  827. package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
  828. package/lib/feature-libraries/schema-index/codec.js +30 -68
  829. package/lib/feature-libraries/schema-index/codec.js.map +1 -1
  830. package/lib/feature-libraries/schema-index/index.d.ts +2 -2
  831. package/lib/feature-libraries/schema-index/index.d.ts.map +1 -1
  832. package/lib/feature-libraries/schema-index/index.js +2 -2
  833. package/lib/feature-libraries/schema-index/index.js.map +1 -1
  834. package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts +1 -9
  835. package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
  836. package/lib/feature-libraries/schema-index/schemaSummarizer.js +0 -10
  837. package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  838. package/lib/feature-libraries/schemaChecker.d.ts.map +1 -1
  839. package/lib/feature-libraries/schemaChecker.js +11 -6
  840. package/lib/feature-libraries/schemaChecker.js.map +1 -1
  841. package/lib/feature-libraries/sequence-field/compose.d.ts +6 -7
  842. package/lib/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  843. package/lib/feature-libraries/sequence-field/compose.js +93 -265
  844. package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
  845. package/lib/feature-libraries/sequence-field/helperTypes.d.ts +14 -10
  846. package/lib/feature-libraries/sequence-field/helperTypes.d.ts.map +1 -1
  847. package/lib/feature-libraries/sequence-field/helperTypes.js.map +1 -1
  848. package/lib/feature-libraries/sequence-field/index.d.ts +2 -3
  849. package/lib/feature-libraries/sequence-field/index.d.ts.map +1 -1
  850. package/lib/feature-libraries/sequence-field/index.js +0 -1
  851. package/lib/feature-libraries/sequence-field/index.js.map +1 -1
  852. package/lib/feature-libraries/sequence-field/invert.d.ts +3 -3
  853. package/lib/feature-libraries/sequence-field/invert.d.ts.map +1 -1
  854. package/lib/feature-libraries/sequence-field/invert.js +69 -170
  855. package/lib/feature-libraries/sequence-field/invert.js.map +1 -1
  856. package/lib/feature-libraries/sequence-field/markQueue.d.ts +2 -2
  857. package/lib/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  858. package/lib/feature-libraries/sequence-field/markQueue.js.map +1 -1
  859. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts +4 -56
  860. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  861. package/lib/feature-libraries/sequence-field/moveEffectTable.js +9 -81
  862. package/lib/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  863. package/lib/feature-libraries/sequence-field/rebase.d.ts +3 -3
  864. package/lib/feature-libraries/sequence-field/rebase.d.ts.map +1 -1
  865. package/lib/feature-libraries/sequence-field/rebase.js +122 -121
  866. package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
  867. package/lib/feature-libraries/sequence-field/replaceRevisions.d.ts +2 -2
  868. package/lib/feature-libraries/sequence-field/replaceRevisions.d.ts.map +1 -1
  869. package/lib/feature-libraries/sequence-field/replaceRevisions.js +36 -44
  870. package/lib/feature-libraries/sequence-field/replaceRevisions.js.map +1 -1
  871. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  872. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +0 -2
  873. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  874. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts +22 -4
  875. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.d.ts.map +1 -1
  876. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js +356 -174
  877. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV2.js.map +1 -1
  878. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.d.ts.map +1 -1
  879. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js +21 -61
  880. package/lib/feature-libraries/sequence-field/sequenceFieldCodecV3.js.map +1 -1
  881. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts +2 -2
  882. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.d.ts.map +1 -1
  883. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js +10 -10
  884. package/lib/feature-libraries/sequence-field/sequenceFieldEditor.js.map +1 -1
  885. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts +3 -2
  886. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.d.ts.map +1 -1
  887. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js +20 -112
  888. package/lib/feature-libraries/sequence-field/sequenceFieldToDelta.js.map +1 -1
  889. package/lib/feature-libraries/sequence-field/types.d.ts +30 -59
  890. package/lib/feature-libraries/sequence-field/types.d.ts.map +1 -1
  891. package/lib/feature-libraries/sequence-field/types.js.map +1 -1
  892. package/lib/feature-libraries/sequence-field/utils.d.ts +15 -24
  893. package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  894. package/lib/feature-libraries/sequence-field/utils.js +144 -311
  895. package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
  896. package/lib/feature-libraries/treeCursorUtils.js +7 -7
  897. package/lib/feature-libraries/treeCursorUtils.js.map +1 -1
  898. package/lib/feature-libraries/treeTextCursor.js +2 -2
  899. package/lib/feature-libraries/treeTextCursor.js.map +1 -1
  900. package/lib/feature-libraries/valueUtilities.d.ts.map +1 -1
  901. package/lib/feature-libraries/valueUtilities.js +16 -8
  902. package/lib/feature-libraries/valueUtilities.js.map +1 -1
  903. package/lib/index.d.ts +4 -3
  904. package/lib/index.d.ts.map +1 -1
  905. package/lib/index.js +2 -1
  906. package/lib/index.js.map +1 -1
  907. package/lib/packageVersion.d.ts +1 -1
  908. package/lib/packageVersion.d.ts.map +1 -1
  909. package/lib/packageVersion.js +1 -1
  910. package/lib/packageVersion.js.map +1 -1
  911. package/lib/shared-tree/index.d.ts +1 -1
  912. package/lib/shared-tree/index.d.ts.map +1 -1
  913. package/lib/shared-tree/index.js.map +1 -1
  914. package/lib/shared-tree/schematizeTree.d.ts +4 -4
  915. package/lib/shared-tree/schematizeTree.d.ts.map +1 -1
  916. package/lib/shared-tree/schematizeTree.js +3 -2
  917. package/lib/shared-tree/schematizeTree.js.map +1 -1
  918. package/lib/shared-tree/schematizingTreeView.d.ts +3 -8
  919. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  920. package/lib/shared-tree/schematizingTreeView.js +61 -47
  921. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  922. package/lib/shared-tree/sharedTree.d.ts +9 -3
  923. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  924. package/lib/shared-tree/sharedTree.js +41 -13
  925. package/lib/shared-tree/sharedTree.js.map +1 -1
  926. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts +1 -1
  927. package/lib/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
  928. package/lib/shared-tree/sharedTreeChangeCodecs.js +10 -8
  929. package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
  930. package/lib/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
  931. package/lib/shared-tree/sharedTreeChangeEnricher.js +6 -4
  932. package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
  933. package/lib/shared-tree/sharedTreeChangeFamily.d.ts +7 -6
  934. package/lib/shared-tree/sharedTreeChangeFamily.d.ts.map +1 -1
  935. package/lib/shared-tree/sharedTreeChangeFamily.js +30 -19
  936. package/lib/shared-tree/sharedTreeChangeFamily.js.map +1 -1
  937. package/lib/shared-tree/sharedTreeEditBuilder.d.ts +16 -6
  938. package/lib/shared-tree/sharedTreeEditBuilder.d.ts.map +1 -1
  939. package/lib/shared-tree/sharedTreeEditBuilder.js +12 -6
  940. package/lib/shared-tree/sharedTreeEditBuilder.js.map +1 -1
  941. package/lib/shared-tree/treeAlpha.d.ts +35 -29
  942. package/lib/shared-tree/treeAlpha.d.ts.map +1 -1
  943. package/lib/shared-tree/treeAlpha.js +21 -23
  944. package/lib/shared-tree/treeAlpha.js.map +1 -1
  945. package/lib/shared-tree/treeCheckout.d.ts +11 -10
  946. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  947. package/lib/shared-tree/treeCheckout.js +97 -29
  948. package/lib/shared-tree/treeCheckout.js.map +1 -1
  949. package/lib/shared-tree-core/branch.d.ts +6 -3
  950. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  951. package/lib/shared-tree-core/branch.js +13 -6
  952. package/lib/shared-tree-core/branch.js.map +1 -1
  953. package/lib/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
  954. package/lib/shared-tree-core/branchCommitEnricher.js +2 -2
  955. package/lib/shared-tree-core/branchCommitEnricher.js.map +1 -1
  956. package/lib/shared-tree-core/editManager.d.ts +2 -2
  957. package/lib/shared-tree-core/editManager.d.ts.map +1 -1
  958. package/lib/shared-tree-core/editManager.js +21 -15
  959. package/lib/shared-tree-core/editManager.js.map +1 -1
  960. package/lib/shared-tree-core/editManagerCodecs.d.ts +4 -0
  961. package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  962. package/lib/shared-tree-core/editManagerCodecs.js +16 -4
  963. package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
  964. package/lib/shared-tree-core/editManagerFormatCommons.d.ts +2 -0
  965. package/lib/shared-tree-core/editManagerFormatCommons.d.ts.map +1 -1
  966. package/lib/shared-tree-core/editManagerFormatCommons.js +12 -0
  967. package/lib/shared-tree-core/editManagerFormatCommons.js.map +1 -1
  968. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts +2 -2
  969. package/lib/shared-tree-core/editManagerFormatV1toV4.d.ts.map +1 -1
  970. package/lib/shared-tree-core/editManagerFormatV1toV4.js +2 -0
  971. package/lib/shared-tree-core/editManagerFormatV1toV4.js.map +1 -1
  972. package/lib/shared-tree-core/editManagerSummarizer.js +3 -3
  973. package/lib/shared-tree-core/editManagerSummarizer.js.map +1 -1
  974. package/lib/shared-tree-core/index.d.ts +2 -2
  975. package/lib/shared-tree-core/index.d.ts.map +1 -1
  976. package/lib/shared-tree-core/index.js +2 -2
  977. package/lib/shared-tree-core/index.js.map +1 -1
  978. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts +1 -1
  979. package/lib/shared-tree-core/messageCodecV1ToV4.d.ts.map +1 -1
  980. package/lib/shared-tree-core/messageCodecV1ToV4.js.map +1 -1
  981. package/lib/shared-tree-core/messageCodecVSharedBranches.d.ts.map +1 -1
  982. package/lib/shared-tree-core/messageCodecVSharedBranches.js +2 -1
  983. package/lib/shared-tree-core/messageCodecVSharedBranches.js.map +1 -1
  984. package/lib/shared-tree-core/messageCodecs.d.ts +4 -0
  985. package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
  986. package/lib/shared-tree-core/messageCodecs.js +15 -4
  987. package/lib/shared-tree-core/messageCodecs.js.map +1 -1
  988. package/lib/shared-tree-core/messageFormat.d.ts +2 -0
  989. package/lib/shared-tree-core/messageFormat.d.ts.map +1 -1
  990. package/lib/shared-tree-core/messageFormat.js +12 -0
  991. package/lib/shared-tree-core/messageFormat.js.map +1 -1
  992. package/lib/shared-tree-core/messageFormatV1ToV4.d.ts +3 -2
  993. package/lib/shared-tree-core/messageFormatV1ToV4.d.ts.map +1 -1
  994. package/lib/shared-tree-core/messageFormatV1ToV4.js +9 -1
  995. package/lib/shared-tree-core/messageFormatV1ToV4.js.map +1 -1
  996. package/lib/shared-tree-core/sequenceIdUtils.d.ts.map +1 -1
  997. package/lib/shared-tree-core/sequenceIdUtils.js +4 -4
  998. package/lib/shared-tree-core/sequenceIdUtils.js.map +1 -1
  999. package/lib/shared-tree-core/sharedTreeCore.d.ts +1 -0
  1000. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  1001. package/lib/shared-tree-core/sharedTreeCore.js +13 -9
  1002. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  1003. package/lib/shared-tree-core/transaction.d.ts +25 -8
  1004. package/lib/shared-tree-core/transaction.d.ts.map +1 -1
  1005. package/lib/shared-tree-core/transaction.js +67 -32
  1006. package/lib/shared-tree-core/transaction.js.map +1 -1
  1007. package/lib/shared-tree-core/transactionEnricher.d.ts +2 -2
  1008. package/lib/shared-tree-core/transactionEnricher.d.ts.map +1 -1
  1009. package/lib/shared-tree-core/transactionEnricher.js +3 -3
  1010. package/lib/shared-tree-core/transactionEnricher.js.map +1 -1
  1011. package/lib/simple-tree/api/configuration.js +1 -1
  1012. package/lib/simple-tree/api/configuration.js.map +1 -1
  1013. package/lib/simple-tree/api/customTree.d.ts.map +1 -1
  1014. package/lib/simple-tree/api/customTree.js +13 -9
  1015. package/lib/simple-tree/api/customTree.js.map +1 -1
  1016. package/lib/simple-tree/api/discrepancies.d.ts.map +1 -1
  1017. package/lib/simple-tree/api/discrepancies.js +21 -17
  1018. package/lib/simple-tree/api/discrepancies.js.map +1 -1
  1019. package/lib/simple-tree/api/index.d.ts +2 -2
  1020. package/lib/simple-tree/api/index.d.ts.map +1 -1
  1021. package/lib/simple-tree/api/index.js +1 -1
  1022. package/lib/simple-tree/api/index.js.map +1 -1
  1023. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  1024. package/lib/simple-tree/api/schemaFactory.js +12 -8
  1025. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  1026. package/lib/simple-tree/api/schemaFactoryAlpha.js +1 -1
  1027. package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  1028. package/lib/simple-tree/api/schemaFactoryBeta.js +1 -1
  1029. package/lib/simple-tree/api/schemaFactoryBeta.js.map +1 -1
  1030. package/lib/simple-tree/api/schemaFromSimple.js +18 -9
  1031. package/lib/simple-tree/api/schemaFromSimple.js.map +1 -1
  1032. package/lib/simple-tree/api/simpleSchemaCodec.js +10 -5
  1033. package/lib/simple-tree/api/simpleSchemaCodec.js.map +1 -1
  1034. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  1035. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +19 -15
  1036. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  1037. package/lib/simple-tree/api/simpleTreeIndex.js +10 -10
  1038. package/lib/simple-tree/api/simpleTreeIndex.js.map +1 -1
  1039. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +244 -0
  1040. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
  1041. package/lib/simple-tree/api/snapshotCompatibilityChecker.js +270 -0
  1042. package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  1043. package/lib/simple-tree/api/storedSchema.d.ts.map +1 -1
  1044. package/lib/simple-tree/api/storedSchema.js +4 -7
  1045. package/lib/simple-tree/api/storedSchema.js.map +1 -1
  1046. package/lib/simple-tree/api/transactionTypes.d.ts +17 -4
  1047. package/lib/simple-tree/api/transactionTypes.d.ts.map +1 -1
  1048. package/lib/simple-tree/api/transactionTypes.js.map +1 -1
  1049. package/lib/simple-tree/api/tree.d.ts +3 -1
  1050. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  1051. package/lib/simple-tree/api/tree.js.map +1 -1
  1052. package/lib/simple-tree/api/treeNodeApi.d.ts.map +1 -1
  1053. package/lib/simple-tree/api/treeNodeApi.js +21 -13
  1054. package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
  1055. package/lib/simple-tree/api/verboseTree.d.ts.map +1 -1
  1056. package/lib/simple-tree/api/verboseTree.js +14 -9
  1057. package/lib/simple-tree/api/verboseTree.js.map +1 -1
  1058. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  1059. package/lib/simple-tree/core/treeNodeKernel.js +12 -5
  1060. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  1061. package/lib/simple-tree/core/unhydratedFlexTree.d.ts +15 -15
  1062. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  1063. package/lib/simple-tree/core/unhydratedFlexTree.js +78 -23
  1064. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  1065. package/lib/simple-tree/fieldSchema.d.ts +4 -4
  1066. package/lib/simple-tree/fieldSchema.d.ts.map +1 -1
  1067. package/lib/simple-tree/fieldSchema.js.map +1 -1
  1068. package/lib/simple-tree/getTreeNodeForField.d.ts.map +1 -1
  1069. package/lib/simple-tree/getTreeNodeForField.js +2 -1
  1070. package/lib/simple-tree/getTreeNodeForField.js.map +1 -1
  1071. package/lib/simple-tree/index.d.ts +3 -3
  1072. package/lib/simple-tree/index.d.ts.map +1 -1
  1073. package/lib/simple-tree/index.js +2 -2
  1074. package/lib/simple-tree/index.js.map +1 -1
  1075. package/lib/simple-tree/leafNodeSchema.js +9 -6
  1076. package/lib/simple-tree/leafNodeSchema.js.map +1 -1
  1077. package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  1078. package/lib/simple-tree/node-kinds/array/arrayNode.js +23 -21
  1079. package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  1080. package/lib/simple-tree/node-kinds/common.d.ts.map +1 -1
  1081. package/lib/simple-tree/node-kinds/common.js +2 -2
  1082. package/lib/simple-tree/node-kinds/common.js.map +1 -1
  1083. package/lib/simple-tree/node-kinds/map/mapNode.js +2 -2
  1084. package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  1085. package/lib/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  1086. package/lib/simple-tree/node-kinds/object/objectNode.js +22 -23
  1087. package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  1088. package/lib/simple-tree/node-kinds/record/recordNode.js +6 -5
  1089. package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  1090. package/lib/simple-tree/prepareForInsertion.d.ts +54 -47
  1091. package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
  1092. package/lib/simple-tree/prepareForInsertion.js +184 -125
  1093. package/lib/simple-tree/prepareForInsertion.js.map +1 -1
  1094. package/lib/simple-tree/toStoredSchema.d.ts.map +1 -1
  1095. package/lib/simple-tree/toStoredSchema.js +9 -5
  1096. package/lib/simple-tree/toStoredSchema.js.map +1 -1
  1097. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts +13 -4
  1098. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
  1099. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +28 -11
  1100. package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
  1101. package/lib/text/index.d.ts +6 -0
  1102. package/lib/text/index.d.ts.map +1 -0
  1103. package/lib/text/index.js +6 -0
  1104. package/lib/text/index.js.map +1 -0
  1105. package/lib/text/textDomain.d.ts +138 -0
  1106. package/lib/text/textDomain.d.ts.map +1 -0
  1107. package/lib/text/textDomain.js +118 -0
  1108. package/lib/text/textDomain.js.map +1 -0
  1109. package/lib/treeFactory.d.ts.map +1 -1
  1110. package/lib/treeFactory.js +18 -4
  1111. package/lib/treeFactory.js.map +1 -1
  1112. package/lib/util/bTreeUtils.d.ts +12 -4
  1113. package/lib/util/bTreeUtils.d.ts.map +1 -1
  1114. package/lib/util/bTreeUtils.js +16 -20
  1115. package/lib/util/bTreeUtils.js.map +1 -1
  1116. package/lib/util/breakable.js +7 -9
  1117. package/lib/util/breakable.js.map +1 -1
  1118. package/lib/util/idAllocator.d.ts.map +1 -1
  1119. package/lib/util/idAllocator.js +1 -2
  1120. package/lib/util/idAllocator.js.map +1 -1
  1121. package/lib/util/index.d.ts +1 -1
  1122. package/lib/util/index.d.ts.map +1 -1
  1123. package/lib/util/index.js +1 -1
  1124. package/lib/util/index.js.map +1 -1
  1125. package/lib/util/nestedMap.d.ts.map +1 -1
  1126. package/lib/util/nestedMap.js +13 -13
  1127. package/lib/util/nestedMap.js.map +1 -1
  1128. package/lib/util/rangeMap.d.ts +24 -12
  1129. package/lib/util/rangeMap.d.ts.map +1 -1
  1130. package/lib/util/rangeMap.js +44 -5
  1131. package/lib/util/rangeMap.js.map +1 -1
  1132. package/lib/util/utils.d.ts.map +1 -1
  1133. package/lib/util/utils.js +16 -15
  1134. package/lib/util/utils.js.map +1 -1
  1135. package/package.json +33 -33
  1136. package/src/codec/codec.ts +48 -8
  1137. package/src/codec/index.ts +4 -1
  1138. package/src/codec/versioned/codec.ts +340 -22
  1139. package/src/codec/versioned/index.ts +3 -1
  1140. package/src/core/change-family/changeFamily.ts +5 -0
  1141. package/src/core/change-family/index.ts +1 -0
  1142. package/src/core/index.ts +8 -3
  1143. package/src/core/rebase/changeRebaser.ts +46 -7
  1144. package/src/core/rebase/index.ts +4 -1
  1145. package/src/core/rebase/types.ts +71 -39
  1146. package/src/core/rebase/utils.ts +42 -14
  1147. package/src/core/schema-stored/schema.ts +3 -3
  1148. package/src/core/tree/anchorSet.ts +4 -4
  1149. package/src/core/tree/detachedFieldIndex.ts +29 -74
  1150. package/src/core/tree/detachedFieldIndexCodecCommon.ts +4 -8
  1151. package/src/core/tree/detachedFieldIndexCodecV1.ts +3 -7
  1152. package/src/core/tree/detachedFieldIndexCodecV2.ts +5 -9
  1153. package/src/core/tree/detachedFieldIndexCodecs.ts +21 -64
  1154. package/src/core/tree/index.ts +3 -2
  1155. package/src/core/tree/mapTree.ts +1 -1
  1156. package/src/core/tree/pathTree.ts +16 -4
  1157. package/src/core/tree/sparseTree.ts +1 -0
  1158. package/src/core/tree/treeTextFormat.ts +5 -9
  1159. package/src/core/tree/visitDelta.ts +6 -2
  1160. package/src/core/tree/visitorUtils.ts +55 -19
  1161. package/src/feature-libraries/changeAtomIdBTree.ts +56 -0
  1162. package/src/feature-libraries/chunked-forest/basicChunk.ts +8 -2
  1163. package/src/feature-libraries/chunked-forest/chunkTree.ts +6 -1
  1164. package/src/feature-libraries/chunked-forest/chunkedForest.ts +8 -6
  1165. package/src/feature-libraries/chunked-forest/codec/chunkDecoding.ts +7 -4
  1166. package/src/feature-libraries/chunked-forest/codec/codecs.ts +19 -11
  1167. package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +4 -3
  1168. package/src/feature-libraries/chunked-forest/codec/schemaBasedEncode.ts +9 -5
  1169. package/src/feature-libraries/chunked-forest/uniformChunk.ts +1 -1
  1170. package/src/feature-libraries/default-schema/defaultEditBuilder.ts +442 -139
  1171. package/src/feature-libraries/default-schema/defaultFieldKinds.ts +24 -6
  1172. package/src/feature-libraries/default-schema/index.ts +17 -5
  1173. package/src/feature-libraries/default-schema/locationBasedEditBuilder.ts +188 -0
  1174. package/src/feature-libraries/default-schema/mappedEditBuilder.ts +41 -9
  1175. package/src/feature-libraries/deltaUtils.ts +6 -1
  1176. package/src/feature-libraries/flex-tree/context.ts +17 -0
  1177. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +7 -8
  1178. package/src/feature-libraries/flex-tree/lazyField.ts +72 -30
  1179. package/src/feature-libraries/forest-summary/forestSummarizer.ts +3 -1
  1180. package/src/feature-libraries/index.ts +32 -13
  1181. package/src/feature-libraries/indexing/anchorTreeIndex.ts +5 -5
  1182. package/src/feature-libraries/mapTreeCursor.ts +2 -1
  1183. package/src/feature-libraries/mitigatedChangeFamily.ts +14 -7
  1184. package/src/feature-libraries/modular-schema/comparison.ts +12 -9
  1185. package/src/feature-libraries/modular-schema/crossFieldQueries.ts +142 -44
  1186. package/src/feature-libraries/modular-schema/defaultRevisionReplacer.ts +70 -0
  1187. package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +35 -64
  1188. package/src/feature-libraries/modular-schema/genericFieldKind.ts +11 -25
  1189. package/src/feature-libraries/modular-schema/genericFieldKindCodecs.ts +1 -1
  1190. package/src/feature-libraries/modular-schema/index.ts +20 -16
  1191. package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +912 -0
  1192. package/src/feature-libraries/modular-schema/modularChangeCodecV2.ts +89 -0
  1193. package/src/feature-libraries/modular-schema/modularChangeCodecV3.ts +760 -0
  1194. package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +52 -523
  1195. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +2756 -884
  1196. package/src/feature-libraries/modular-schema/{modularChangeFormat.ts → modularChangeFormatV1.ts} +5 -4
  1197. package/src/feature-libraries/modular-schema/modularChangeFormatV2.ts +34 -0
  1198. package/src/feature-libraries/modular-schema/modularChangeFormatV3.ts +62 -0
  1199. package/src/feature-libraries/modular-schema/modularChangeTypes.ts +70 -14
  1200. package/src/feature-libraries/node-identifier/mockNodeIdentifierManager.ts +1 -1
  1201. package/src/feature-libraries/object-forest/objectForest.ts +7 -5
  1202. package/src/feature-libraries/optional-field/index.ts +1 -3
  1203. package/src/feature-libraries/optional-field/optionalField.ts +320 -576
  1204. package/src/feature-libraries/optional-field/optionalFieldChangeFormatV3.ts +45 -0
  1205. package/src/feature-libraries/optional-field/optionalFieldChangeTypes.ts +24 -38
  1206. package/src/feature-libraries/optional-field/optionalFieldCodecV2.ts +89 -35
  1207. package/src/feature-libraries/optional-field/optionalFieldCodecV3.ts +94 -0
  1208. package/src/feature-libraries/optional-field/optionalFieldCodecs.ts +5 -1
  1209. package/src/feature-libraries/schema-edits/schemaChangeCodecs.ts +18 -3
  1210. package/src/feature-libraries/schema-index/codec.ts +30 -90
  1211. package/src/feature-libraries/schema-index/index.ts +2 -4
  1212. package/src/feature-libraries/schema-index/schemaSummarizer.ts +0 -17
  1213. package/src/feature-libraries/schemaChecker.ts +11 -6
  1214. package/src/feature-libraries/sequence-field/compose.ts +147 -526
  1215. package/src/feature-libraries/sequence-field/helperTypes.ts +34 -19
  1216. package/src/feature-libraries/sequence-field/index.ts +0 -9
  1217. package/src/feature-libraries/sequence-field/invert.ts +103 -227
  1218. package/src/feature-libraries/sequence-field/markQueue.ts +2 -2
  1219. package/src/feature-libraries/sequence-field/moveEffectTable.ts +11 -192
  1220. package/src/feature-libraries/sequence-field/rebase.ts +182 -210
  1221. package/src/feature-libraries/sequence-field/replaceRevisions.ts +54 -80
  1222. package/src/feature-libraries/sequence-field/sequenceFieldChangeHandler.ts +0 -2
  1223. package/src/feature-libraries/sequence-field/sequenceFieldCodecV2.ts +648 -220
  1224. package/src/feature-libraries/sequence-field/sequenceFieldCodecV3.ts +56 -68
  1225. package/src/feature-libraries/sequence-field/sequenceFieldEditor.ts +25 -27
  1226. package/src/feature-libraries/sequence-field/sequenceFieldToDelta.ts +25 -132
  1227. package/src/feature-libraries/sequence-field/types.ts +34 -64
  1228. package/src/feature-libraries/sequence-field/utils.ts +171 -366
  1229. package/src/feature-libraries/treeCursorUtils.ts +7 -7
  1230. package/src/feature-libraries/treeTextCursor.ts +2 -2
  1231. package/src/feature-libraries/valueUtilities.ts +16 -8
  1232. package/src/index.ts +11 -0
  1233. package/src/packageVersion.ts +1 -1
  1234. package/src/shared-tree/index.ts +3 -2
  1235. package/src/shared-tree/schematizeTree.ts +21 -8
  1236. package/src/shared-tree/schematizingTreeView.ts +79 -83
  1237. package/src/shared-tree/sharedTree.ts +50 -17
  1238. package/src/shared-tree/sharedTreeChangeCodecs.ts +14 -9
  1239. package/src/shared-tree/sharedTreeChangeEnricher.ts +6 -2
  1240. package/src/shared-tree/sharedTreeChangeFamily.ts +44 -24
  1241. package/src/shared-tree/sharedTreeEditBuilder.ts +48 -13
  1242. package/src/shared-tree/treeAlpha.ts +60 -51
  1243. package/src/shared-tree/treeCheckout.ts +160 -73
  1244. package/src/shared-tree-core/branch.ts +21 -6
  1245. package/src/shared-tree-core/branchCommitEnricher.ts +3 -8
  1246. package/src/shared-tree-core/editManager.ts +43 -29
  1247. package/src/shared-tree-core/editManagerCodecs.ts +19 -4
  1248. package/src/shared-tree-core/editManagerFormatCommons.ts +12 -0
  1249. package/src/shared-tree-core/editManagerFormatV1toV4.ts +5 -1
  1250. package/src/shared-tree-core/editManagerSummarizer.ts +3 -3
  1251. package/src/shared-tree-core/index.ts +2 -0
  1252. package/src/shared-tree-core/messageCodecV1ToV4.ts +3 -1
  1253. package/src/shared-tree-core/messageCodecVSharedBranches.ts +2 -1
  1254. package/src/shared-tree-core/messageCodecs.ts +18 -4
  1255. package/src/shared-tree-core/messageFormat.ts +12 -1
  1256. package/src/shared-tree-core/messageFormatV1ToV4.ts +18 -2
  1257. package/src/shared-tree-core/sequenceIdUtils.ts +4 -4
  1258. package/src/shared-tree-core/sharedTreeCore.ts +15 -8
  1259. package/src/shared-tree-core/transaction.ts +115 -56
  1260. package/src/shared-tree-core/transactionEnricher.ts +5 -6
  1261. package/src/simple-tree/api/configuration.ts +1 -1
  1262. package/src/simple-tree/api/customTree.ts +14 -10
  1263. package/src/simple-tree/api/discrepancies.ts +23 -17
  1264. package/src/simple-tree/api/index.ts +5 -0
  1265. package/src/simple-tree/api/schemaFactory.ts +11 -7
  1266. package/src/simple-tree/api/schemaFactoryAlpha.ts +1 -1
  1267. package/src/simple-tree/api/schemaFactoryBeta.ts +1 -1
  1268. package/src/simple-tree/api/schemaFromSimple.ts +18 -9
  1269. package/src/simple-tree/api/simpleSchemaCodec.ts +10 -5
  1270. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +21 -17
  1271. package/src/simple-tree/api/simpleTreeIndex.ts +8 -8
  1272. package/src/simple-tree/api/snapshotCompatibilityChecker.ts +501 -0
  1273. package/src/simple-tree/api/storedSchema.ts +10 -7
  1274. package/src/simple-tree/api/transactionTypes.ts +19 -4
  1275. package/src/simple-tree/api/tree.ts +3 -1
  1276. package/src/simple-tree/api/treeNodeApi.ts +21 -13
  1277. package/src/simple-tree/api/verboseTree.ts +14 -9
  1278. package/src/simple-tree/core/treeNodeKernel.ts +12 -5
  1279. package/src/simple-tree/core/unhydratedFlexTree.ts +109 -53
  1280. package/src/simple-tree/fieldSchema.ts +6 -4
  1281. package/src/simple-tree/getTreeNodeForField.ts +2 -1
  1282. package/src/simple-tree/index.ts +7 -1
  1283. package/src/simple-tree/leafNodeSchema.ts +8 -5
  1284. package/src/simple-tree/node-kinds/array/arrayNode.ts +32 -30
  1285. package/src/simple-tree/node-kinds/common.ts +2 -5
  1286. package/src/simple-tree/node-kinds/map/mapNode.ts +4 -4
  1287. package/src/simple-tree/node-kinds/object/objectNode.ts +29 -30
  1288. package/src/simple-tree/node-kinds/record/recordNode.ts +12 -12
  1289. package/src/simple-tree/prepareForInsertion.ts +343 -201
  1290. package/src/simple-tree/toStoredSchema.ts +9 -5
  1291. package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +43 -15
  1292. package/src/text/README.md +8 -0
  1293. package/src/text/index.ts +6 -0
  1294. package/src/text/textDomain.ts +199 -0
  1295. package/src/treeFactory.ts +20 -5
  1296. package/src/util/bTreeUtils.ts +34 -23
  1297. package/src/util/breakable.ts +9 -9
  1298. package/src/util/idAllocator.ts +1 -2
  1299. package/src/util/index.ts +3 -0
  1300. package/src/util/nestedMap.ts +13 -15
  1301. package/src/util/rangeMap.ts +72 -18
  1302. package/src/util/utils.ts +14 -13
  1303. package/.eslintrc.cjs +0 -140
  1304. package/assertTagging.config.mjs +0 -14
  1305. package/dist/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +0 -1
  1306. package/dist/feature-libraries/modular-schema/modularChangeFormat.js.map +0 -1
  1307. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
  1308. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
  1309. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -50
  1310. package/dist/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
  1311. package/docs/main/sequence-field/move-composition.md +0 -46
  1312. package/lib/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +0 -1
  1313. package/lib/feature-libraries/modular-schema/modularChangeFormat.js.map +0 -1
  1314. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts +0 -9
  1315. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.d.ts.map +0 -1
  1316. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js +0 -46
  1317. package/lib/feature-libraries/sequence-field/relevantRemovedRoots.js.map +0 -1
  1318. package/src/feature-libraries/sequence-field/relevantRemovedRoots.ts +0 -57
@@ -5,8 +5,13 @@
5
5
 
6
6
  import { assert, fail } from "@fluidframework/core-utils/internal";
7
7
  import { BTree } from "@tylerbu/sorted-btree-es6";
8
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
9
 
9
- import type { ICodecFamily } from "../../codec/index.js";
10
+ import {
11
+ FluidClientVersion,
12
+ type CodecWriteOptions,
13
+ type ICodecFamily,
14
+ } from "../../codec/index.js";
10
15
  import {
11
16
  type ChangeEncodingContext,
12
17
  type ChangeFamily,
@@ -22,24 +27,30 @@ import {
22
27
  EditBuilder,
23
28
  type FieldKey,
24
29
  type FieldKindIdentifier,
25
- type FieldUpPath,
26
30
  type RevisionInfo,
27
31
  type RevisionMetadataSource,
28
32
  type RevisionTag,
29
33
  type TaggedChange,
30
- type UpPath,
31
34
  makeDetachedNodeId,
32
- replaceAtomRevisions,
33
35
  revisionMetadataSourceFromInfo,
34
36
  areEqualChangeAtomIds,
35
37
  type ChangeAtomId,
36
38
  areEqualChangeAtomIdOpts,
37
39
  tagChange,
38
40
  makeAnonChange,
39
- newChangeAtomIdRangeMap,
40
41
  type DeltaDetachedNodeChanges,
41
42
  type DeltaDetachedNodeRename,
42
43
  mapTaggedChange,
44
+ newChangeAtomIdRangeMap,
45
+ newChangeAtomIdTransform,
46
+ type ChangeAtomIdRangeMap,
47
+ offsetChangeAtomId,
48
+ type NormalizedUpPath,
49
+ type NormalizedFieldUpPath,
50
+ isDetachedUpPathRoot,
51
+ subtractChangeAtomIds,
52
+ makeChangeAtomId,
53
+ type RevisionReplacer,
43
54
  } from "../../core/index.js";
44
55
  import {
45
56
  type IdAllocationState,
@@ -55,14 +66,19 @@ import {
55
66
  type TupleBTree,
56
67
  RangeMap,
57
68
  balancedReduce,
69
+ type RangeQueryEntry,
70
+ type RangeQueryResultFragment,
58
71
  } from "../../util/index.js";
59
72
  import type { TreeChunk } from "../chunked-forest/index.js";
60
73
 
61
74
  import {
62
- type CrossFieldManager,
75
+ type ComposeNodeManager,
63
76
  type CrossFieldMap,
64
77
  CrossFieldTarget,
65
- getFirstFromCrossFieldMap,
78
+ type DetachedNodeEntry,
79
+ type InvertNodeManager,
80
+ type RebaseDetachedNodeEntry,
81
+ type RebaseNodeManager,
66
82
  setInCrossFieldMap,
67
83
  } from "./crossFieldQueries.js";
68
84
  import {
@@ -73,20 +89,31 @@ import {
73
89
  import { convertGenericChange, genericFieldKind } from "./genericFieldKind.js";
74
90
  import type { GenericChangeset } from "./genericFieldKindTypes.js";
75
91
  import {
76
- type ChangeAtomIdBTree,
77
92
  type CrossFieldKey,
78
93
  type CrossFieldKeyRange,
79
94
  type CrossFieldKeyTable,
95
+ type CrossFieldRangeTable,
80
96
  type FieldChange,
81
97
  type FieldChangeMap,
82
98
  type FieldChangeset,
83
99
  type FieldId,
84
100
  type ModularChangeset,
85
- newCrossFieldKeyTable,
101
+ newCrossFieldRangeTable,
102
+ type NoChangeConstraint,
86
103
  type NodeChangeset,
87
104
  type NodeId,
105
+ type NodeLocation,
106
+ type RebaseVersion,
107
+ type RootNodeTable,
88
108
  } from "./modularChangeTypes.js";
89
109
  import type { FlexFieldKind } from "./fieldKind.js";
110
+ import { lt } from "semver-ts";
111
+ import {
112
+ getFromChangeAtomIdMap,
113
+ rangeQueryChangeAtomIdMap,
114
+ setInChangeAtomIdMap,
115
+ type ChangeAtomIdBTree,
116
+ } from "../changeAtomIdBTree.js";
90
117
 
91
118
  /**
92
119
  * Implementation of ChangeFamily which delegates work in a given field to the appropriate FieldKind
@@ -104,6 +131,7 @@ export class ModularChangeFamily
104
131
  public constructor(
105
132
  fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
106
133
  public readonly codecs: ICodecFamily<ModularChangeset, ChangeEncodingContext>,
134
+ public readonly codecOptions: CodecWriteOptions,
107
135
  ) {
108
136
  this.fieldKinds = fieldKinds;
109
137
  }
@@ -130,9 +158,9 @@ export class ModularChangeFamily
130
158
  } {
131
159
  // TODO: Handle the case where changes have conflicting field kinds
132
160
  const kind =
133
- change1.fieldKind !== genericFieldKind.identifier
134
- ? change1.fieldKind
135
- : change2.fieldKind;
161
+ change1.fieldKind === genericFieldKind.identifier
162
+ ? change2.fieldKind
163
+ : change1.fieldKind;
136
164
 
137
165
  if (kind === genericFieldKind.identifier) {
138
166
  // Both changes are generic
@@ -171,14 +199,14 @@ export class ModularChangeFamily
171
199
  }
172
200
 
173
201
  public compose(changes: TaggedChange<ModularChangeset>[]): ModularChangeset {
174
- const { revInfos, maxId } = getRevInfoFromTaggedChanges(changes);
202
+ const { maxId } = getRevInfoFromTaggedChanges(changes);
175
203
  const idState: IdAllocationState = { maxId };
176
204
 
177
205
  const pairwiseDelegate = (
178
206
  left: ModularChangeset,
179
207
  right: ModularChangeset,
180
208
  ): ModularChangeset => {
181
- return this.composePair(left, right, revInfos, idState);
209
+ return this.composePair(left, right, idState);
182
210
  };
183
211
 
184
212
  const innerChanges = changes.map((change) => change.change);
@@ -188,10 +216,11 @@ export class ModularChangeFamily
188
216
  private composePair(
189
217
  change1: ModularChangeset,
190
218
  change2: ModularChangeset,
191
- revInfos: RevisionInfo[],
192
219
  idState: IdAllocationState,
193
220
  ): ModularChangeset {
194
- const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys } =
221
+ const revInfos = composeRevInfos(change1.revisions, change2.revisions);
222
+
223
+ const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, rootNodes } =
195
224
  this.composeAllFields(change1, change2, revInfos, idState);
196
225
 
197
226
  const { allBuilds, allDestroys, allRefreshers } = composeBuildsDestroysAndRefreshers(
@@ -199,7 +228,12 @@ export class ModularChangeFamily
199
228
  change2,
200
229
  );
201
230
 
202
- return makeModularChangeset({
231
+ // The composed changeset has a "no change" constraint if either change has one
232
+ const noChangeConstraint = change1.noChangeConstraint ?? change2.noChangeConstraint;
233
+ const noChangeConstraintOnRevert =
234
+ change1.noChangeConstraintOnRevert ?? change2.noChangeConstraintOnRevert;
235
+
236
+ const composed = makeModularChangeset({
203
237
  fieldChanges,
204
238
  nodeChanges,
205
239
  nodeToParent,
@@ -207,16 +241,23 @@ export class ModularChangeFamily
207
241
  crossFieldKeys,
208
242
  maxId: idState.maxId,
209
243
  revisions: revInfos,
244
+ rootNodes,
210
245
  builds: allBuilds,
211
246
  destroys: allDestroys,
212
247
  refreshers: allRefreshers,
248
+ noChangeConstraint,
249
+ noChangeConstraintOnRevert,
213
250
  });
251
+
252
+ // XXX: This is an expensive assert which should be disabled before merging.
253
+ validateChangeset(composed, this.fieldKinds);
254
+ return composed;
214
255
  }
215
256
 
216
257
  private composeAllFields(
217
258
  potentiallyConflictedChange1: ModularChangeset,
218
259
  potentiallyConflictedChange2: ModularChangeset,
219
- revInfos: RevisionInfo[],
260
+ revInfos: readonly RevisionInfo[],
220
261
  idState: IdAllocationState,
221
262
  ): ModularChangesetContent {
222
263
  // Our current cell ordering scheme in sequences depends on being able to rebase over a change with conflicts.
@@ -240,14 +281,38 @@ export class ModularChangeFamily
240
281
  mergeTupleBTrees(change1.nodeChanges, change2.nodeChanges),
241
282
  );
242
283
 
243
- const composedNodeToParent: ChangeAtomIdBTree<FieldId> = brand(
284
+ const composedNodeToParent: ChangeAtomIdBTree<NodeLocation> = brand(
244
285
  mergeTupleBTrees(change1.nodeToParent, change2.nodeToParent),
245
286
  );
246
287
  const composedNodeAliases: ChangeAtomIdBTree<NodeId> = brand(
247
288
  mergeTupleBTrees(change1.nodeAliases, change2.nodeAliases),
248
289
  );
249
290
 
250
- const crossFieldTable = newComposeTable(change1, change2, composedNodeToParent);
291
+ const pendingCompositions: PendingCompositions = {
292
+ nodeIdsToCompose: [],
293
+ affectedBaseFields: newTupleBTree(),
294
+ };
295
+
296
+ const movedCrossFieldKeys: CrossFieldKeyTable = newCrossFieldRangeTable();
297
+ const removedCrossFieldKeys: CrossFieldRangeTable<boolean> = newCrossFieldRangeTable();
298
+
299
+ const composedRoots = composeRootTables(
300
+ change1,
301
+ change2,
302
+ composedNodeToParent,
303
+ movedCrossFieldKeys,
304
+ removedCrossFieldKeys,
305
+ pendingCompositions,
306
+ );
307
+
308
+ const crossFieldTable = newComposeTable(
309
+ change1,
310
+ change2,
311
+ composedRoots,
312
+ movedCrossFieldKeys,
313
+ removedCrossFieldKeys,
314
+ pendingCompositions,
315
+ );
251
316
 
252
317
  const composedFields = this.composeFieldMaps(
253
318
  change1.fieldChanges,
@@ -268,17 +333,32 @@ export class ModularChangeFamily
268
333
  revisionMetadata,
269
334
  );
270
335
 
271
- // Currently no field kinds require making changes to cross-field keys during composition, so we can just merge the two tables.
272
- const composedCrossFieldKeys = RangeMap.union(
273
- change1.crossFieldKeys,
274
- change2.crossFieldKeys,
275
- );
336
+ for (const entry of crossFieldTable.renamesToDelete.entries()) {
337
+ deleteNodeRenameFrom(crossFieldTable.composedRootNodes, entry.start, entry.length);
338
+ }
339
+
340
+ for (const [nodeId, location] of crossFieldTable.movedNodeToParent.entries()) {
341
+ // Moved nodes are from change2.
342
+ // If there is a corresponding node in change1, then composedNodeToParent will already have the correct entry,
343
+ // because the location of the node is the same in change1 and the composed change
344
+ // (since they have the same input context).
345
+ if (crossFieldTable.newToBaseNodeId.get(nodeId) === undefined) {
346
+ composedNodeToParent.set(nodeId, location);
347
+ }
348
+ }
349
+
276
350
  return {
277
351
  fieldChanges: composedFields,
278
352
  nodeChanges: composedNodeChanges,
279
353
  nodeToParent: composedNodeToParent,
280
354
  nodeAliases: composedNodeAliases,
281
- crossFieldKeys: composedCrossFieldKeys,
355
+ crossFieldKeys: composeCrossFieldKeyTables(
356
+ change1.crossFieldKeys,
357
+ change2.crossFieldKeys,
358
+ crossFieldTable.movedCrossFieldKeys,
359
+ crossFieldTable.removedCrossFieldKeys,
360
+ ),
361
+ rootNodes: composedRoots,
282
362
  };
283
363
  }
284
364
 
@@ -290,17 +370,16 @@ export class ModularChangeFamily
290
370
  ): void {
291
371
  const context = crossFieldTable.fieldToContext.get(fieldChange);
292
372
  assert(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
293
- const { fieldId, change1: fieldChange1, change2: fieldChange2, composedChange } = context;
373
+ const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
374
+
375
+ crossFieldTable.pendingCompositions.affectedBaseFields.delete(
376
+ fieldIdKeyFromFieldId(context.fieldId),
377
+ );
294
378
 
295
379
  const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
296
380
  const composeNodes = (child1: NodeId | undefined, child2: NodeId | undefined): NodeId => {
297
- if (
298
- child1 !== undefined &&
299
- child2 !== undefined &&
300
- getFromChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2) === undefined
301
- ) {
302
- setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
303
- crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
381
+ if (child1 !== undefined && child2 !== undefined) {
382
+ addNodesToCompose(crossFieldTable, child1, child2);
304
383
  }
305
384
 
306
385
  return child1 ?? child2 ?? fail(0xb22 /* Should not compose two undefined nodes */);
@@ -311,7 +390,7 @@ export class ModularChangeFamily
311
390
  fieldChange2,
312
391
  composeNodes,
313
392
  genId,
314
- new ComposeManager(crossFieldTable, fieldChange, fieldId, false),
393
+ new ComposeNodeManagerI(crossFieldTable, context.fieldId, false),
315
394
  revisionMetadata,
316
395
  );
317
396
  composedChange.change = brand(amendedChange);
@@ -323,7 +402,7 @@ export class ModularChangeFamily
323
402
  * - discovering that two node changesets refer to the same node (`nodeIdsToCompose`)
324
403
  * - a previously composed field being invalidated by a cross field effect (`invalidatedFields`)
325
404
  * - a field which was copied directly from an input changeset being invalidated by a cross field effect
326
- * (`affectedBaseFields` and `affectedNewFields`)
405
+ * (`affectedBaseFields`)
327
406
  *
328
407
  * Updating an element may invalidate further elements. This function runs until there is no more invalidation.
329
408
  */
@@ -331,72 +410,59 @@ export class ModularChangeFamily
331
410
  table: ComposeTable,
332
411
  composedFields: FieldChangeMap,
333
412
  composedNodes: ChangeAtomIdBTree<NodeChangeset>,
334
- composedNodeToParent: ChangeAtomIdBTree<FieldId>,
413
+ composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
335
414
  nodeAliases: ChangeAtomIdBTree<NodeId>,
336
415
  genId: IdAllocator,
337
416
  metadata: RevisionMetadataSource,
338
417
  ): void {
339
418
  const pending = table.pendingCompositions;
340
- while (
341
- table.invalidatedFields.size > 0 ||
342
- pending.nodeIdsToCompose.length > 0 ||
343
- pending.affectedBaseFields.length > 0 ||
344
- pending.affectedNewFields.length > 0
345
- ) {
346
- // Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
347
- for (const [id1, id2] of pending.nodeIdsToCompose) {
348
- this.composeNodesById(
349
- table.baseChange.nodeChanges,
350
- table.newChange.nodeChanges,
351
- composedNodes,
352
- composedNodeToParent,
353
- nodeAliases,
354
- id1,
355
- id2,
356
- genId,
357
- table,
358
- metadata,
359
- );
360
- }
361
-
362
- pending.nodeIdsToCompose.length = 0;
363
-
364
- this.composeAffectedFields(
419
+ while (pending.nodeIdsToCompose.length > 0 || pending.affectedBaseFields.length > 0) {
420
+ this.processPendingNodeCompositions(
365
421
  table,
366
- table.baseChange,
367
- true,
368
- pending.affectedBaseFields,
369
- composedFields,
370
422
  composedNodes,
423
+ composedNodeToParent,
424
+ nodeAliases,
371
425
  genId,
372
426
  metadata,
373
427
  );
374
428
 
375
429
  this.composeAffectedFields(
376
430
  table,
377
- table.newChange,
378
- false,
379
- pending.affectedNewFields,
431
+ table.baseChange,
432
+ pending.affectedBaseFields,
380
433
  composedFields,
381
434
  composedNodes,
382
435
  genId,
383
436
  metadata,
384
437
  );
385
-
386
- this.processInvalidatedCompositions(table, genId, metadata);
387
438
  }
388
439
  }
389
440
 
390
- private processInvalidatedCompositions(
441
+ private processPendingNodeCompositions(
391
442
  table: ComposeTable,
443
+ composedNodes: ChangeAtomIdBTree<NodeChangeset>,
444
+ composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
445
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
392
446
  genId: IdAllocator,
393
447
  metadata: RevisionMetadataSource,
394
448
  ): void {
395
- const fieldsToUpdate = table.invalidatedFields;
396
- table.invalidatedFields = new Set();
397
- for (const fieldChange of fieldsToUpdate) {
398
- this.composeInvalidatedField(fieldChange, table, genId, metadata);
449
+ // Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
450
+ for (const [id1, id2] of table.pendingCompositions.nodeIdsToCompose) {
451
+ this.composeNodesById(
452
+ table.baseChange,
453
+ table.newChange,
454
+ composedNodes,
455
+ composedNodeToParent,
456
+ nodeAliases,
457
+ id1,
458
+ id2,
459
+ genId,
460
+ table,
461
+ metadata,
462
+ );
399
463
  }
464
+
465
+ table.pendingCompositions.nodeIdsToCompose.length = 0;
400
466
  }
401
467
 
402
468
  /**
@@ -413,63 +479,79 @@ export class ModularChangeFamily
413
479
  private composeAffectedFields(
414
480
  table: ComposeTable,
415
481
  change: ModularChangeset,
416
- areBaseFields: boolean,
417
482
  affectedFields: BTree<FieldIdKey, true>,
418
483
  composedFields: FieldChangeMap,
419
484
  composedNodes: ChangeAtomIdBTree<NodeChangeset>,
420
485
  genId: IdAllocator,
421
486
  metadata: RevisionMetadataSource,
422
487
  ): void {
423
- for (const fieldIdKey of affectedFields.keys()) {
424
- const fieldId = normalizeFieldId(fieldIdFromFieldIdKey(fieldIdKey), change.nodeAliases);
425
- const fieldChange = fieldChangeFromId(change.fieldChanges, change.nodeChanges, fieldId);
488
+ const fieldsToProcess = affectedFields.clone();
489
+ affectedFields.clear();
490
+
491
+ for (const fieldIdKey of fieldsToProcess.keys()) {
492
+ const fieldId = fieldIdFromFieldIdKey(fieldIdKey);
493
+ const fieldChange = fieldChangeFromId(change, fieldId);
426
494
 
427
495
  if (
428
496
  table.fieldToContext.has(fieldChange) ||
429
497
  table.newFieldToBaseField.has(fieldChange)
430
498
  ) {
431
- // This function handles fields which were not part of the intersection of the two changesets but which need to be updated anyway.
432
- // If we've already processed this field then either it is up to date
433
- // or there is pending inval which will be handled in processInvalidatedCompositions.
434
- continue;
499
+ this.composeInvalidatedField(fieldChange, table, genId, metadata);
500
+ } else {
501
+ this.composeFieldWithNoNewChange(
502
+ table,
503
+ fieldChange,
504
+ fieldId,
505
+ composedFields,
506
+ composedNodes,
507
+ genId,
508
+ metadata,
509
+ );
435
510
  }
511
+ }
512
+ }
436
513
 
437
- const emptyChange = this.createEmptyFieldChange(fieldChange.fieldKind);
438
- const [change1, change2] = areBaseFields
439
- ? [fieldChange, emptyChange]
440
- : [emptyChange, fieldChange];
441
-
442
- const composedField = this.composeFieldChanges(
443
- fieldId,
444
- change1,
445
- change2,
446
- genId,
447
- table,
448
- metadata,
449
- );
514
+ private composeFieldWithNoNewChange(
515
+ table: ComposeTable,
516
+ baseFieldChange: FieldChange,
517
+ fieldId: FieldId,
518
+ composedFields: FieldChangeMap,
519
+ composedNodes: ChangeAtomIdBTree<NodeChangeset>,
520
+ genId: IdAllocator,
521
+ metadata: RevisionMetadataSource,
522
+ ): void {
523
+ const emptyChange = this.createEmptyFieldChange(baseFieldChange.fieldKind);
450
524
 
451
- if (fieldId.nodeId === undefined) {
452
- composedFields.set(fieldId.field, composedField);
453
- continue;
454
- }
525
+ const composedField = this.composeFieldChanges(
526
+ fieldId,
527
+ baseFieldChange,
528
+ emptyChange,
529
+ genId,
530
+ table,
531
+ metadata,
532
+ );
455
533
 
456
- const nodeId =
457
- getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId;
534
+ if (fieldId.nodeId === undefined) {
535
+ composedFields.set(fieldId.field, composedField);
536
+ return;
537
+ }
458
538
 
459
- let nodeChangeset = nodeChangeFromId(composedNodes, nodeId);
460
- if (!table.composedNodes.has(nodeChangeset)) {
461
- nodeChangeset = cloneNodeChangeset(nodeChangeset);
462
- setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
463
- }
539
+ const nodeId = normalizeNodeId(
540
+ getFromChangeAtomIdMap(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId,
541
+ table.baseChange.nodeAliases,
542
+ );
464
543
 
465
- if (nodeChangeset.fieldChanges === undefined) {
466
- nodeChangeset.fieldChanges = new Map();
467
- }
544
+ // We clone the node changeset before mutating it, as it may be from one of the input changesets.
545
+ const nodeChangeset: Mutable<NodeChangeset> = cloneNodeChangeset(
546
+ nodeChangeFromId(composedNodes, table.baseChange.nodeAliases, nodeId),
547
+ );
548
+ setInChangeAtomIdMap(composedNodes, nodeId, nodeChangeset);
468
549
 
469
- nodeChangeset.fieldChanges.set(fieldId.field, composedField);
550
+ if (nodeChangeset.fieldChanges === undefined) {
551
+ nodeChangeset.fieldChanges = new Map();
470
552
  }
471
553
 
472
- affectedFields.clear();
554
+ nodeChangeset.fieldChanges.set(fieldId.field, composedField);
473
555
  }
474
556
 
475
557
  private composeFieldMaps(
@@ -488,17 +570,29 @@ export class ModularChangeFamily
488
570
  for (const [field, fieldChange1] of change1) {
489
571
  const fieldId: FieldId = { nodeId: parentId, field };
490
572
  const fieldChange2 = change2.get(field);
573
+
574
+ const cachedComposedFieldChange =
575
+ crossFieldTable.fieldToContext.get(fieldChange1)?.composedChange;
576
+
577
+ if (fieldChange2 === undefined && cachedComposedFieldChange !== undefined) {
578
+ // This can happen if the field was previous processed in `composeFieldWithNoNewChange`.
579
+ // If `change2` does not have a change for this field, then without this check we would
580
+ // lose the composed field change and instead simply have `change1`'s change.
581
+ composedFields.set(field, cachedComposedFieldChange);
582
+ continue;
583
+ }
584
+
491
585
  const composedField =
492
- fieldChange2 !== undefined
493
- ? this.composeFieldChanges(
586
+ fieldChange2 === undefined
587
+ ? fieldChange1
588
+ : this.composeFieldChanges(
494
589
  fieldId,
495
590
  fieldChange1,
496
591
  fieldChange2,
497
592
  genId,
498
593
  crossFieldTable,
499
594
  revisionMetadata,
500
- )
501
- : fieldChange1;
595
+ );
502
596
 
503
597
  composedFields.set(field, composedField);
504
598
  }
@@ -519,7 +613,7 @@ export class ModularChangeFamily
519
613
  * will be added to `crossFieldTable.pendingCompositions.nodeIdsToCompose`.
520
614
  *
521
615
  * Any fields which had cross-field information sent to them as part of this field composition
522
- * will be added to either `affectedBaseFields` or `affectedNewFields` in `crossFieldTable.pendingCompositions`.
616
+ * will be added to `affectedBaseFields` in `crossFieldTable.pendingCompositions`.
523
617
  *
524
618
  * Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
525
619
  */
@@ -538,15 +632,14 @@ export class ModularChangeFamily
538
632
  change2: change2Normalized,
539
633
  } = this.normalizeFieldChanges(change1, change2);
540
634
 
541
- const manager = new ComposeManager(crossFieldTable, change1, fieldId);
635
+ const manager = new ComposeNodeManagerI(crossFieldTable, fieldId);
542
636
 
543
637
  const composedChange = changeHandler.rebaser.compose(
544
638
  change1Normalized,
545
639
  change2Normalized,
546
640
  (child1, child2) => {
547
641
  if (child1 !== undefined && child2 !== undefined) {
548
- setInChangeAtomIdMap(crossFieldTable.newToBaseNodeId, child2, child1);
549
- crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
642
+ addNodesToCompose(crossFieldTable, child1, child2);
550
643
  }
551
644
  return child1 ?? child2 ?? fail(0xb23 /* Should not compose two undefined nodes */);
552
645
  },
@@ -572,19 +665,19 @@ export class ModularChangeFamily
572
665
  }
573
666
 
574
667
  private composeNodesById(
575
- nodeChanges1: ChangeAtomIdBTree<NodeChangeset>,
576
- nodeChanges2: ChangeAtomIdBTree<NodeChangeset>,
668
+ change1: ModularChangeset,
669
+ change2: ModularChangeset,
577
670
  composedNodes: ChangeAtomIdBTree<NodeChangeset>,
578
- composedNodeToParent: ChangeAtomIdBTree<FieldId>,
579
- nodeAliases: ChangeAtomIdBTree<NodeId>,
671
+ composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
672
+ composedAliases: ChangeAtomIdBTree<NodeId>,
580
673
  id1: NodeId,
581
674
  id2: NodeId,
582
675
  idAllocator: IdAllocator,
583
676
  crossFieldTable: ComposeTable,
584
677
  revisionMetadata: RevisionMetadataSource,
585
678
  ): void {
586
- const nodeChangeset1 = nodeChangeFromId(nodeChanges1, id1);
587
- const nodeChangeset2 = nodeChangeFromId(nodeChanges2, id2);
679
+ const nodeChangeset1 = nodeChangeFromId(change1.nodeChanges, change1.nodeAliases, id1);
680
+ const nodeChangeset2 = nodeChangeFromId(change2.nodeChanges, change2.nodeAliases, id2);
588
681
  const composedNodeChangeset = this.composeNodeChanges(
589
682
  id1,
590
683
  nodeChangeset1,
@@ -599,13 +692,11 @@ export class ModularChangeFamily
599
692
  if (!areEqualChangeAtomIds(id1, id2)) {
600
693
  composedNodes.delete([id2.revision, id2.localId]);
601
694
  composedNodeToParent.delete([id2.revision, id2.localId]);
602
- setInChangeAtomIdMap(nodeAliases, id2, id1);
695
+ setInChangeAtomIdMap(composedAliases, id2, id1);
603
696
 
604
697
  // We need to delete id1 to avoid forming a cycle in case id1 already had an alias.
605
- nodeAliases.delete([id1.revision, id1.localId]);
698
+ composedAliases.delete([id1.revision, id1.localId]);
606
699
  }
607
-
608
- crossFieldTable.composedNodes.add(composedNodeChangeset);
609
700
  }
610
701
 
611
702
  private composeNodeChanges(
@@ -640,7 +731,7 @@ export class ModularChangeFamily
640
731
  revisionMetadata,
641
732
  );
642
733
 
643
- const composedNodeChange: NodeChangeset = {};
734
+ const composedNodeChange: Mutable<NodeChangeset> = {};
644
735
 
645
736
  if (composedFieldChanges.size > 0) {
646
737
  composedNodeChange.fieldChanges = composedFieldChanges;
@@ -660,8 +751,8 @@ export class ModularChangeFamily
660
751
  /**
661
752
  * @param change - The change to invert.
662
753
  * @param isRollback - Whether the inverted change is meant to rollback a change on a branch as is the case when
663
- * @param revisionForInvert - The revision for the invert changeset.
664
754
  * performing a sandwich rebase.
755
+ * @param revisionForInvert - The revision for the invert changeset.
665
756
  */
666
757
  public invert(
667
758
  change: TaggedChange<ModularChangeset>,
@@ -681,6 +772,9 @@ export class ModularChangeFamily
681
772
  ? [{ revision: revisionForInvert, rollbackOf: change.revision }]
682
773
  : [{ revision: revisionForInvert }];
683
774
 
775
+ const noChangeConstraint = change.change.noChangeConstraintOnRevert;
776
+ const noChangeConstraintOnRevert = change.change.noChangeConstraint;
777
+
684
778
  if (hasConflicts(change.change)) {
685
779
  return makeModularChangeset({
686
780
  maxId: change.change.maxId as number,
@@ -692,9 +786,14 @@ export class ModularChangeFamily
692
786
  const genId: IdAllocator = idAllocatorFromMaxId(change.change.maxId ?? -1);
693
787
 
694
788
  const crossFieldTable: InvertTable = {
695
- ...newCrossFieldTable<FieldChange>(),
789
+ change: change.change,
790
+ entries: newChangeAtomIdRangeMap(),
696
791
  originalFieldToContext: new Map(),
792
+ invertRevision: revisionForInvert,
697
793
  invertedNodeToParent: brand(change.change.nodeToParent.clone()),
794
+ invalidatedFields: new Set(),
795
+ invertedRoots: invertRootTable(change.change, isRollback),
796
+ attachToDetachId: newChangeAtomIdTransform(),
698
797
  };
699
798
  const { revInfos: oldRevInfos } = getRevInfoFromTaggedChanges([change]);
700
799
  const revisionMetadata = revisionMetadataSourceFromInfo(oldRevInfos);
@@ -735,7 +834,7 @@ export class ModularChangeFamily
735
834
  context !== undefined,
736
835
  0x851 /* Should have context for every invalidated field */,
737
836
  );
738
- const { invertedField, fieldId } = context;
837
+ const { invertedField } = context;
739
838
 
740
839
  const amendedChange = getChangeHandler(
741
840
  this.fieldKinds,
@@ -745,7 +844,7 @@ export class ModularChangeFamily
745
844
  isRollback,
746
845
  genId,
747
846
  revisionForInvert,
748
- new InvertManager(crossFieldTable, fieldChange, fieldId),
847
+ new InvertNodeManagerI(crossFieldTable, context.fieldId),
749
848
  revisionMetadata,
750
849
  );
751
850
  invertedField.change = brand(amendedChange);
@@ -754,16 +853,21 @@ export class ModularChangeFamily
754
853
 
755
854
  const crossFieldKeys = this.makeCrossFieldKeyTable(invertedFields, invertedNodes);
756
855
 
856
+ this.processInvertRenames(crossFieldTable);
857
+
757
858
  return makeModularChangeset({
758
859
  fieldChanges: invertedFields,
759
860
  nodeChanges: invertedNodes,
760
861
  nodeToParent: crossFieldTable.invertedNodeToParent,
862
+ rootNodes: crossFieldTable.invertedRoots,
761
863
  nodeAliases: change.change.nodeAliases,
762
864
  crossFieldKeys,
763
865
  maxId: genId.getMaxId(),
764
866
  revisions: revInfos,
765
867
  constraintViolationCount: change.change.constraintViolationCountOnRevert,
766
868
  constraintViolationCountOnRevert: change.change.constraintViolationCount,
869
+ noChangeConstraint,
870
+ noChangeConstraintOnRevert,
767
871
  destroys,
768
872
  });
769
873
  }
@@ -781,7 +885,7 @@ export class ModularChangeFamily
781
885
 
782
886
  for (const [field, fieldChange] of changes) {
783
887
  const fieldId = { nodeId: parentId, field };
784
- const manager = new InvertManager(crossFieldTable, fieldChange, fieldId);
888
+ const manager = new InvertNodeManagerI(crossFieldTable, fieldId);
785
889
  const invertedChange = getChangeHandler(
786
890
  this.fieldKinds,
787
891
  fieldChange.fieldKind,
@@ -818,7 +922,7 @@ export class ModularChangeFamily
818
922
  revisionMetadata: RevisionMetadataSource,
819
923
  revisionForInvert: RevisionTag,
820
924
  ): NodeChangeset {
821
- const inverse: NodeChangeset = {};
925
+ const inverse: Mutable<NodeChangeset> = {};
822
926
 
823
927
  // If the node has a constraint, it should be inverted to a node-exist-on-revert constraint. This ensure that if
824
928
  // the inverse is inverted again, the original input constraint will be restored.
@@ -848,6 +952,17 @@ export class ModularChangeFamily
848
952
  return inverse;
849
953
  }
850
954
 
955
+ private processInvertRenames(table: InvertTable): void {
956
+ for (const {
957
+ start: newAttachId,
958
+ value: originalDetachId,
959
+ length,
960
+ } of table.attachToDetachId.entries()) {
961
+ // Note that the detach location is already set in `invertDetach`.
962
+ addNodeRename(table.invertedRoots, originalDetachId, newAttachId, length, undefined);
963
+ }
964
+ }
965
+
851
966
  public rebase(
852
967
  taggedChange: TaggedChange<ModularChangeset>,
853
968
  potentiallyConflictedOver: TaggedChange<ModularChangeset>,
@@ -867,17 +982,41 @@ export class ModularChangeFamily
867
982
  const idState: IdAllocationState = { maxId };
868
983
  const genId: IdAllocator = idAllocatorFromState(idState);
869
984
 
985
+ const affectedBaseFields: TupleBTree<FieldIdKey, boolean> = newTupleBTree();
986
+ const nodesToRebase: [newChangeset: NodeId, baseChangeset: NodeId][] = [];
987
+
988
+ const rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation> = brand(
989
+ change.nodeToParent.clone(),
990
+ );
991
+
992
+ const rebaseVersion = Math.max(
993
+ change.rebaseVersion,
994
+ over.change.rebaseVersion,
995
+ ) as RebaseVersion;
996
+
997
+ const rebasedRootNodes = rebaseRoots(
998
+ change,
999
+ over.change,
1000
+ affectedBaseFields,
1001
+ nodesToRebase,
1002
+ rebasedNodeToParent,
1003
+ rebaseVersion,
1004
+ );
870
1005
  const crossFieldTable: RebaseTable = {
871
- ...newCrossFieldTable<FieldChange>(),
1006
+ rebaseVersion,
1007
+ entries: newDetachedEntryMap(),
872
1008
  newChange: change,
873
1009
  baseChange: over.change,
874
1010
  baseFieldToContext: new Map(),
1011
+ baseRoots: over.change.rootNodes,
1012
+ rebasedRootNodes,
875
1013
  baseToRebasedNodeId: newTupleBTree(),
876
1014
  rebasedFields: new Set(),
877
- rebasedNodeToParent: brand(change.nodeToParent.clone()),
878
- rebasedCrossFieldKeys: change.crossFieldKeys.clone(),
1015
+ rebasedNodeToParent,
1016
+ rebasedDetachLocations: newChangeAtomIdRangeMap(),
1017
+ movedDetaches: newChangeAtomIdRangeMap(),
879
1018
  nodeIdPairs: [],
880
- affectedBaseFields: newTupleBTree(),
1019
+ affectedBaseFields,
881
1020
  fieldsWithUnattachedChild: new Set(),
882
1021
  };
883
1022
 
@@ -893,13 +1032,14 @@ export class ModularChangeFamily
893
1032
  const rebasedNodes: ChangeAtomIdBTree<NodeChangeset> = brand(change.nodeChanges.clone());
894
1033
 
895
1034
  const rebasedFields = this.rebaseIntersectingFields(
1035
+ nodesToRebase,
896
1036
  crossFieldTable,
897
1037
  rebasedNodes,
898
1038
  genId,
899
1039
  rebaseMetadata,
900
1040
  );
901
1041
 
902
- this.rebaseInvalidatedElements(
1042
+ this.rebaseInvalidatedFields(
903
1043
  rebasedFields,
904
1044
  rebasedNodes,
905
1045
  crossFieldTable,
@@ -907,40 +1047,86 @@ export class ModularChangeFamily
907
1047
  genId,
908
1048
  );
909
1049
 
1050
+ fixupRebasedDetachLocations(crossFieldTable);
1051
+
910
1052
  const constraintState = newConstraintState(change.constraintViolationCount ?? 0);
911
1053
  const revertConstraintState = newConstraintState(
912
1054
  change.constraintViolationCountOnRevert ?? 0,
913
1055
  );
914
- this.updateConstraintsForFields(
1056
+
1057
+ let noChangeConstraint = change.noChangeConstraint;
1058
+ if (noChangeConstraint !== undefined && !noChangeConstraint.violated) {
1059
+ noChangeConstraint = { violated: true };
1060
+ constraintState.violationCount += 1;
1061
+ }
1062
+
1063
+ this.updateConstraints(
915
1064
  rebasedFields,
916
- NodeAttachState.Attached,
917
- NodeAttachState.Attached,
1065
+ rebasedNodes,
1066
+ rebasedRootNodes,
918
1067
  constraintState,
919
1068
  revertConstraintState,
920
- rebasedNodes,
1069
+ );
1070
+
1071
+ const fieldsWithRootMoves = getFieldsWithRootMoves(
1072
+ crossFieldTable.rebasedRootNodes,
1073
+ change.nodeAliases,
1074
+ );
1075
+
1076
+ const fieldToRootChanges = getFieldToRootChanges(
1077
+ crossFieldTable.rebasedRootNodes,
1078
+ change.nodeAliases,
921
1079
  );
922
1080
 
923
1081
  const rebased = makeModularChangeset({
924
- fieldChanges: this.pruneFieldMap(rebasedFields, rebasedNodes),
1082
+ fieldChanges: this.pruneFieldMap(
1083
+ rebasedFields,
1084
+ undefined,
1085
+ rebasedNodes,
1086
+ crossFieldTable.rebasedNodeToParent,
1087
+ change.nodeAliases,
1088
+ crossFieldTable.rebasedRootNodes,
1089
+ fieldsWithRootMoves,
1090
+ fieldToRootChanges,
1091
+ ),
925
1092
  nodeChanges: rebasedNodes,
926
1093
  nodeToParent: crossFieldTable.rebasedNodeToParent,
1094
+ rootNodes: this.pruneRoots(
1095
+ crossFieldTable.rebasedRootNodes,
1096
+ rebasedNodes,
1097
+ crossFieldTable.rebasedNodeToParent,
1098
+ change.nodeAliases,
1099
+ fieldsWithRootMoves,
1100
+ fieldToRootChanges,
1101
+ ),
1102
+ // TODO: Do we need to include aliases for node changesets added during rebasing?
927
1103
  nodeAliases: change.nodeAliases,
928
- crossFieldKeys: crossFieldTable.rebasedCrossFieldKeys,
1104
+ crossFieldKeys: rebaseCrossFieldKeys(
1105
+ change.crossFieldKeys,
1106
+ crossFieldTable.movedDetaches,
1107
+ crossFieldTable.rebasedDetachLocations,
1108
+ ),
929
1109
  maxId: idState.maxId,
930
1110
  revisions: change.revisions,
931
1111
  constraintViolationCount: constraintState.violationCount,
932
1112
  constraintViolationCountOnRevert: revertConstraintState.violationCount,
1113
+ noChangeConstraint,
1114
+ noChangeConstraintOnRevert: change.noChangeConstraintOnRevert,
933
1115
  builds: change.builds,
934
1116
  destroys: change.destroys,
935
1117
  refreshers: change.refreshers,
1118
+ rebaseVersion,
936
1119
  });
937
1120
 
1121
+ // XXX: This is an expensive assert which should be disabled before merging.
1122
+ validateChangeset(rebased, this.fieldKinds);
938
1123
  return rebased;
939
1124
  }
940
1125
 
941
1126
  // This performs a first pass on all fields which have both new and base changes.
942
1127
  // TODO: Can we also handle additional passes in this method?
943
1128
  private rebaseIntersectingFields(
1129
+ rootChanges: [newChangeset: NodeId, baseChangeset: NodeId][],
944
1130
  crossFieldTable: RebaseTable,
945
1131
  rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
946
1132
  genId: IdAllocator,
@@ -957,6 +1143,18 @@ export class ModularChangeFamily
957
1143
  metadata,
958
1144
  );
959
1145
 
1146
+ for (const [newChildChange, baseChildChange] of rootChanges) {
1147
+ const rebasedNode = this.rebaseNodeChange(
1148
+ newChildChange,
1149
+ baseChildChange,
1150
+ genId,
1151
+ crossFieldTable,
1152
+ metadata,
1153
+ );
1154
+
1155
+ setInChangeAtomIdMap(rebasedNodes, newChildChange, rebasedNode);
1156
+ }
1157
+
960
1158
  // This loop processes all fields which have both base and new changes.
961
1159
  // Note that the call to `rebaseNodeChange` can add entries to `crossFieldTable.nodeIdPairs`.
962
1160
  for (const [newId, baseId, _attachState] of crossFieldTable.nodeIdPairs) {
@@ -974,146 +1172,150 @@ export class ModularChangeFamily
974
1172
  return rebasedFields;
975
1173
  }
976
1174
 
977
- // This processes fields which have no new changes but have been invalidated by another field.
978
- private rebaseFieldsWithoutNewChanges(
1175
+ private rebaseFieldWithoutNewChanges(
1176
+ baseFieldChange: FieldChange,
1177
+ baseFieldId: FieldId,
1178
+ crossFieldTable: RebaseTable,
979
1179
  rebasedFields: FieldChangeMap,
980
1180
  rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
981
- crossFieldTable: RebaseTable,
982
1181
  genId: IdAllocator,
983
1182
  metadata: RebaseRevisionMetadata,
984
- ): void {
985
- const baseChange = crossFieldTable.baseChange;
986
- for (const [revision, localId, fieldKey] of crossFieldTable.affectedBaseFields.keys()) {
987
- const baseNodeId =
988
- localId !== undefined
989
- ? normalizeNodeId({ revision, localId }, baseChange.nodeAliases)
990
- : undefined;
991
-
992
- const baseFieldChange = fieldMapFromNodeId(
993
- baseChange.fieldChanges,
994
- baseChange.nodeChanges,
995
- baseNodeId,
996
- ).get(fieldKey);
997
1183
 
998
- assert(
999
- baseFieldChange !== undefined,
1000
- 0x9c2 /* Cross field key registered for empty field */,
1001
- );
1002
- if (crossFieldTable.baseFieldToContext.has(baseFieldChange)) {
1003
- // This field has already been processed because there were changes to rebase.
1004
- continue;
1184
+ /**
1185
+ * The ID of a node in `baseFieldChange` which should be included in the rebased field change.
1186
+ */
1187
+ baseNodeId?: NodeId,
1188
+ ): void {
1189
+ // This field has no changes in the new changeset, otherwise it would have been added to
1190
+ // `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
1191
+ const rebaseChild = (
1192
+ child: NodeId | undefined,
1193
+ baseChild: NodeId | undefined,
1194
+ stateChange: NodeAttachState | undefined,
1195
+ ): NodeId | undefined => {
1196
+ assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
1197
+ if (baseChild === undefined || baseNodeId === undefined) {
1198
+ return undefined;
1005
1199
  }
1006
1200
 
1007
- // This field has no changes in the new changeset, otherwise it would have been added to
1008
- // `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
1009
- const rebaseChild = (
1010
- child: NodeId | undefined,
1011
- baseChild: NodeId | undefined,
1012
- stateChange: NodeAttachState | undefined,
1013
- ): NodeId | undefined => {
1014
- assert(child === undefined, 0x9c3 /* There should be no new changes in this field */);
1015
- return undefined;
1016
- };
1201
+ return areEqualChangeAtomIds(
1202
+ normalizeNodeId(baseChild, crossFieldTable.baseChange.nodeAliases),
1203
+ baseNodeId,
1204
+ )
1205
+ ? baseNodeId
1206
+ : undefined;
1207
+ };
1017
1208
 
1018
- const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
1019
- const fieldChange: FieldChange = {
1020
- ...baseFieldChange,
1021
- change: brand(handler.createEmpty()),
1022
- };
1209
+ const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
1210
+ const fieldChange: FieldChange = {
1211
+ ...baseFieldChange,
1212
+ change: brand(handler.createEmpty()),
1213
+ };
1023
1214
 
1024
- const rebasedNodeId =
1025
- baseNodeId !== undefined
1026
- ? rebasedNodeIdFromBaseNodeId(crossFieldTable, baseNodeId)
1027
- : undefined;
1215
+ const rebasedNodeId =
1216
+ baseFieldId.nodeId === undefined
1217
+ ? undefined
1218
+ : rebasedNodeIdFromBaseNodeId(crossFieldTable, baseFieldId.nodeId);
1028
1219
 
1029
- const fieldId: FieldId = { nodeId: rebasedNodeId, field: fieldKey };
1030
- const rebasedField: unknown = handler.rebaser.rebase(
1031
- fieldChange.change,
1032
- baseFieldChange.change,
1033
- rebaseChild,
1034
- genId,
1035
- new RebaseManager(crossFieldTable, baseFieldChange, fieldId),
1036
- metadata,
1037
- );
1220
+ const fieldId: FieldId = { nodeId: rebasedNodeId, field: baseFieldId.field };
1038
1221
 
1039
- const rebasedFieldChange: FieldChange = {
1040
- ...baseFieldChange,
1041
- change: brand(rebasedField),
1042
- };
1222
+ const rebasedField: unknown = handler.rebaser.rebase(
1223
+ fieldChange.change,
1224
+ baseFieldChange.change,
1225
+ rebaseChild,
1226
+ genId,
1227
+ new RebaseNodeManagerI(crossFieldTable, fieldId),
1228
+ metadata,
1229
+ crossFieldTable.rebaseVersion,
1230
+ );
1043
1231
 
1044
- // TODO: Deduplicate
1045
- crossFieldTable.baseFieldToContext.set(baseFieldChange, {
1046
- newChange: fieldChange,
1047
- baseChange: baseFieldChange,
1048
- rebasedChange: rebasedFieldChange,
1049
- fieldId,
1050
- baseNodeIds: [],
1051
- });
1052
- crossFieldTable.rebasedFields.add(rebasedFieldChange);
1232
+ const rebasedFieldChange: FieldChange = {
1233
+ ...baseFieldChange,
1234
+ change: brand(rebasedField),
1235
+ };
1053
1236
 
1054
- this.attachRebasedField(
1055
- rebasedFields,
1056
- rebasedNodes,
1057
- crossFieldTable,
1058
- rebasedFieldChange,
1059
- fieldId,
1060
- genId,
1061
- metadata,
1062
- );
1237
+ const context: RebaseFieldContext = {
1238
+ newChange: fieldChange,
1239
+ baseChange: baseFieldChange,
1240
+ rebasedChange: rebasedFieldChange,
1241
+ fieldId,
1242
+ baseNodeIds: newTupleBTree(),
1243
+ };
1244
+
1245
+ if (baseNodeId !== undefined) {
1246
+ setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true);
1063
1247
  }
1064
- }
1065
1248
 
1066
- private rebaseInvalidatedElements(
1067
- rebasedFields: FieldChangeMap,
1068
- rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
1069
- table: RebaseTable,
1070
- metadata: RebaseRevisionMetadata,
1071
- idAllocator: IdAllocator,
1072
- ): void {
1073
- this.rebaseFieldsWithoutNewChanges(
1249
+ crossFieldTable.baseFieldToContext.set(baseFieldChange, context);
1250
+
1251
+ crossFieldTable.rebasedFields.add(rebasedFieldChange);
1252
+
1253
+ this.attachRebasedField(
1074
1254
  rebasedFields,
1075
1255
  rebasedNodes,
1076
- table,
1077
- idAllocator,
1256
+ crossFieldTable,
1257
+ rebasedFieldChange,
1258
+ fieldId,
1259
+ genId,
1078
1260
  metadata,
1079
1261
  );
1080
-
1081
- this.rebaseFieldsWithUnattachedChild(table, metadata, idAllocator);
1082
- this.rebaseInvalidatedFields(table, metadata, idAllocator);
1083
1262
  }
1084
1263
 
1085
1264
  private rebaseInvalidatedFields(
1265
+ rebasedFields: FieldChangeMap,
1266
+ rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
1086
1267
  crossFieldTable: RebaseTable,
1087
1268
  rebaseMetadata: RebaseRevisionMetadata,
1088
1269
  genId: IdAllocator,
1089
1270
  ): void {
1090
- const fieldsToUpdate = crossFieldTable.invalidatedFields;
1091
- crossFieldTable.invalidatedFields = new Set();
1092
- for (const field of fieldsToUpdate) {
1093
- this.rebaseInvalidatedField(field, crossFieldTable, rebaseMetadata, genId);
1094
- }
1095
- }
1271
+ while (crossFieldTable.affectedBaseFields.size > 0) {
1272
+ const baseFields = crossFieldTable.affectedBaseFields.clone();
1273
+ crossFieldTable.affectedBaseFields.clear();
1274
+
1275
+ for (const baseFieldIdKey of baseFields.keys()) {
1276
+ const baseFieldId = normalizeFieldId(
1277
+ fieldIdFromFieldIdKey(baseFieldIdKey),
1278
+ crossFieldTable.baseChange.nodeAliases,
1279
+ );
1096
1280
 
1097
- private rebaseFieldsWithUnattachedChild(
1098
- table: RebaseTable,
1099
- metadata: RebaseRevisionMetadata,
1100
- idAllocator: IdAllocator,
1101
- ): void {
1102
- for (const field of table.fieldsWithUnattachedChild) {
1103
- table.invalidatedFields.delete(field);
1104
- this.rebaseInvalidatedField(field, table, metadata, idAllocator, true);
1281
+ const baseField = fieldChangeFromId(crossFieldTable.baseChange, baseFieldId);
1282
+
1283
+ assert(
1284
+ baseField !== undefined,
1285
+ 0x9c2 /* Cross field key registered for empty field */,
1286
+ );
1287
+
1288
+ const context = crossFieldTable.baseFieldToContext.get(baseField);
1289
+ if (context === undefined) {
1290
+ this.rebaseFieldWithoutNewChanges(
1291
+ baseField,
1292
+ baseFieldId,
1293
+ crossFieldTable,
1294
+ rebasedFields,
1295
+ rebasedNodes,
1296
+ genId,
1297
+ rebaseMetadata,
1298
+ );
1299
+ } else {
1300
+ this.rebaseInvalidatedField(
1301
+ baseField,
1302
+ crossFieldTable,
1303
+ context,
1304
+ rebaseMetadata,
1305
+ genId,
1306
+ );
1307
+ }
1308
+ }
1105
1309
  }
1106
1310
  }
1107
1311
 
1108
1312
  private rebaseInvalidatedField(
1109
1313
  baseField: FieldChange,
1110
1314
  crossFieldTable: RebaseTable,
1315
+ context: RebaseFieldContext,
1111
1316
  rebaseMetadata: RebaseRevisionMetadata,
1112
1317
  genId: IdAllocator,
1113
- allowInval = false,
1114
1318
  ): void {
1115
- const context = crossFieldTable.baseFieldToContext.get(baseField);
1116
- assert(context !== undefined, 0x852 /* Every field should have a context */);
1117
1319
  const {
1118
1320
  changeHandler,
1119
1321
  change1: fieldChangeset,
@@ -1128,25 +1330,28 @@ export class ModularChangeFamily
1128
1330
  return curr;
1129
1331
  }
1130
1332
 
1131
- if (base !== undefined) {
1132
- for (const id of context.baseNodeIds) {
1133
- if (areEqualChangeAtomIds(base, id)) {
1134
- return base;
1135
- }
1136
- }
1333
+ if (base !== undefined && getFromChangeAtomIdMap(context.baseNodeIds, base) === true) {
1334
+ return base;
1137
1335
  }
1138
1336
 
1139
1337
  return undefined;
1140
1338
  };
1141
1339
 
1340
+ let allowInval = false;
1341
+ if (crossFieldTable.fieldsWithUnattachedChild.has(baseField)) {
1342
+ crossFieldTable.fieldsWithUnattachedChild.delete(baseField);
1343
+ allowInval = true;
1344
+ }
1345
+
1142
1346
  context.rebasedChange.change = brand(
1143
1347
  changeHandler.rebaser.rebase(
1144
1348
  fieldChangeset,
1145
1349
  baseChangeset,
1146
1350
  rebaseChild,
1147
1351
  genId,
1148
- new RebaseManager(crossFieldTable, baseField, context.fieldId, allowInval),
1352
+ new RebaseNodeManagerI(crossFieldTable, context.fieldId, allowInval),
1149
1353
  rebaseMetadata,
1354
+ crossFieldTable.rebaseVersion,
1150
1355
  ),
1151
1356
  );
1152
1357
  }
@@ -1166,14 +1371,20 @@ export class ModularChangeFamily
1166
1371
  }
1167
1372
  const rebasedNode = getFromChangeAtomIdMap(rebasedNodes, nodeId);
1168
1373
  if (rebasedNode !== undefined) {
1169
- if (rebasedNode.fieldChanges === undefined) {
1170
- rebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
1374
+ const updatedRebasedNode: Mutable<NodeChangeset> = cloneNodeChangeset(rebasedNode);
1375
+ setInChangeAtomIdMap(rebasedNodes, nodeId, updatedRebasedNode);
1376
+
1377
+ if (updatedRebasedNode.fieldChanges === undefined) {
1378
+ updatedRebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
1171
1379
  return;
1172
1380
  }
1173
1381
 
1174
- assert(!rebasedNode.fieldChanges.has(fieldKey), 0x9c4 /* Expected an empty field */);
1175
- rebasedNode.fieldChanges.set(fieldKey, rebasedField);
1176
- return;
1382
+ assert(
1383
+ !updatedRebasedNode.fieldChanges.has(fieldKey),
1384
+ 0x9c4 /* Expected an empty field */,
1385
+ );
1386
+ updatedRebasedNode.fieldChanges.set(fieldKey, rebasedField);
1387
+ return;
1177
1388
  }
1178
1389
 
1179
1390
  const newNode: NodeChangeset = {
@@ -1183,14 +1394,14 @@ export class ModularChangeFamily
1183
1394
  setInChangeAtomIdMap(rebasedNodes, nodeId, newNode);
1184
1395
  setInChangeAtomIdMap(table.baseToRebasedNodeId, nodeId, nodeId);
1185
1396
 
1186
- const parentFieldId = getParentFieldId(table.baseChange, nodeId);
1397
+ const parentBase = getNodeParent(table.baseChange, nodeId);
1187
1398
 
1188
1399
  this.attachRebasedNode(
1189
1400
  rebasedFields,
1190
1401
  rebasedNodes,
1191
1402
  table,
1192
1403
  nodeId,
1193
- parentFieldId,
1404
+ parentBase,
1194
1405
  idAllocator,
1195
1406
  metadata,
1196
1407
  );
@@ -1201,65 +1412,100 @@ export class ModularChangeFamily
1201
1412
  rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
1202
1413
  table: RebaseTable,
1203
1414
  baseNodeId: NodeId,
1204
- parentFieldIdBase: FieldId,
1415
+ parentBase: NodeLocation,
1205
1416
  idAllocator: IdAllocator,
1206
1417
  metadata: RebaseRevisionMetadata,
1207
1418
  ): void {
1208
- const baseFieldChange = fieldChangeFromId(
1209
- table.baseChange.fieldChanges,
1210
- table.baseChange.nodeChanges,
1211
- parentFieldIdBase,
1212
- );
1419
+ if (parentBase.root !== undefined) {
1420
+ const renamedRoot = firstAttachIdFromDetachId(
1421
+ table.baseChange.rootNodes,
1422
+ parentBase.root,
1423
+ 1,
1424
+ ).value;
1425
+
1426
+ const attachField = table.baseChange.crossFieldKeys.getFirst(
1427
+ { ...renamedRoot, target: CrossFieldTarget.Destination },
1428
+ 1,
1429
+ ).value;
1430
+
1431
+ if (attachField === undefined) {
1432
+ const baseDetachLocation = table.baseChange.rootNodes.detachLocations.getFirst(
1433
+ parentBase.root,
1434
+ 1,
1435
+ ).value;
1436
+
1437
+ assignRootChange(
1438
+ table.rebasedRootNodes,
1439
+ table.rebasedNodeToParent,
1440
+ renamedRoot,
1441
+ baseNodeId,
1442
+ baseDetachLocation,
1443
+ table.rebaseVersion,
1444
+ );
1445
+
1446
+ // We need to make sure the rebased changeset includes the detach location,
1447
+ // so we add that field to `affectedBaseFields` unless it's already been processed.
1448
+ if (
1449
+ baseDetachLocation !== undefined &&
1450
+ !table.baseFieldToContext.has(
1451
+ fieldChangeFromId(table.baseChange, baseDetachLocation),
1452
+ )
1453
+ ) {
1454
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(baseDetachLocation), true);
1455
+ }
1456
+ } else {
1457
+ // The base change inserts this node into `attachField`, so the rebased change should represent this node there.
1458
+ const normalizedAttachField = normalizeFieldId(
1459
+ attachField,
1460
+ table.baseChange.nodeAliases,
1461
+ );
1462
+
1463
+ const entry: DetachedNodeEntry = table.entries.getFirst(renamedRoot, 1).value ?? {};
1464
+ table.entries.set(renamedRoot, 1, { ...entry, nodeChange: baseNodeId });
1465
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(normalizedAttachField), true);
1466
+ this.attachRebasedNode(
1467
+ rebasedFields,
1468
+ rebasedNodes,
1469
+ table,
1470
+ baseNodeId,
1471
+ { field: normalizedAttachField },
1472
+ idAllocator,
1473
+ metadata,
1474
+ );
1475
+ }
1476
+
1477
+ return;
1478
+ }
1479
+
1480
+ const parentFieldIdBase = parentBase.field;
1481
+ const baseFieldChange = fieldChangeFromId(table.baseChange, parentFieldIdBase);
1213
1482
 
1214
1483
  const rebasedFieldId = rebasedFieldIdFromBaseId(table, parentFieldIdBase);
1215
- setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, rebasedFieldId);
1484
+ setInChangeAtomIdMap(table.rebasedNodeToParent, baseNodeId, { field: rebasedFieldId });
1216
1485
 
1217
1486
  const context = table.baseFieldToContext.get(baseFieldChange);
1218
1487
  if (context !== undefined) {
1219
1488
  // We've already processed this field.
1220
- // The new child node will be attached in rebaseFieldsWithUnattachedChild.
1221
- context.baseNodeIds.push(baseNodeId);
1222
- table.fieldsWithUnattachedChild.add(baseFieldChange);
1489
+ // The new child node will be attached in the next pass.
1490
+ // Note that adding to `fieldsWithUnattachedChild` allows that field to generate new invalidations,
1491
+ // so to avoid invalidation cycles we make sure we only add to it once per new unattached child.
1492
+ // This is done by checking whether `context.baseNodeIds` already contained `baseNodeId`.
1493
+ if (setInChangeAtomIdMap(context.baseNodeIds, baseNodeId, true)) {
1494
+ table.fieldsWithUnattachedChild.add(baseFieldChange);
1495
+ table.affectedBaseFields.set(fieldIdKeyFromFieldId(parentFieldIdBase), true);
1496
+ }
1223
1497
  return;
1224
1498
  }
1225
1499
 
1226
- const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
1227
-
1228
- const fieldChange: FieldChange = {
1229
- ...baseFieldChange,
1230
- change: brand(handler.createEmpty()),
1231
- };
1232
-
1233
- const rebasedChangeset = handler.rebaser.rebase(
1234
- handler.createEmpty(),
1235
- baseFieldChange.change,
1236
- (_idNew, idBase) =>
1237
- idBase !== undefined && areEqualChangeAtomIds(idBase, baseNodeId)
1238
- ? baseNodeId
1239
- : undefined,
1240
- idAllocator,
1241
- new RebaseManager(table, baseFieldChange, rebasedFieldId),
1242
- metadata,
1243
- );
1244
-
1245
- const rebasedField: FieldChange = { ...baseFieldChange, change: brand(rebasedChangeset) };
1246
- table.rebasedFields.add(rebasedField);
1247
- table.baseFieldToContext.set(baseFieldChange, {
1248
- newChange: fieldChange,
1249
- baseChange: baseFieldChange,
1250
- rebasedChange: rebasedField,
1251
- fieldId: rebasedFieldId,
1252
- baseNodeIds: [],
1253
- });
1254
-
1255
- this.attachRebasedField(
1500
+ this.rebaseFieldWithoutNewChanges(
1501
+ baseFieldChange,
1502
+ parentFieldIdBase,
1503
+ table,
1256
1504
  rebasedFields,
1257
1505
  rebasedNodes,
1258
- table,
1259
- rebasedField,
1260
- rebasedFieldId,
1261
1506
  idAllocator,
1262
1507
  metadata,
1508
+ baseNodeId,
1263
1509
  );
1264
1510
  }
1265
1511
 
@@ -1298,7 +1544,7 @@ export class ModularChangeFamily
1298
1544
  change2: baseChangeset,
1299
1545
  } = this.normalizeFieldChanges(fieldChange, baseChange);
1300
1546
 
1301
- const manager = new RebaseManager(crossFieldTable, baseChange, fieldId);
1547
+ const manager = new RebaseNodeManagerI(crossFieldTable, fieldId);
1302
1548
 
1303
1549
  const rebasedField = changeHandler.rebaser.rebase(
1304
1550
  fieldChangeset,
@@ -1307,6 +1553,7 @@ export class ModularChangeFamily
1307
1553
  genId,
1308
1554
  manager,
1309
1555
  revisionMetadata,
1556
+ crossFieldTable.rebaseVersion,
1310
1557
  );
1311
1558
 
1312
1559
  const rebasedFieldChange: FieldChange = {
@@ -1321,7 +1568,7 @@ export class ModularChangeFamily
1321
1568
  newChange: fieldChange,
1322
1569
  rebasedChange: rebasedFieldChange,
1323
1570
  fieldId,
1324
- baseNodeIds: [],
1571
+ baseNodeIds: newTupleBTree(),
1325
1572
  });
1326
1573
 
1327
1574
  crossFieldTable.rebasedFields.add(rebasedFieldChange);
@@ -1337,8 +1584,16 @@ export class ModularChangeFamily
1337
1584
  crossFieldTable: RebaseTable,
1338
1585
  revisionMetadata: RebaseRevisionMetadata,
1339
1586
  ): NodeChangeset {
1340
- const change = nodeChangeFromId(crossFieldTable.newChange.nodeChanges, newId);
1341
- const over = nodeChangeFromId(crossFieldTable.baseChange.nodeChanges, baseId);
1587
+ const change = nodeChangeFromId(
1588
+ crossFieldTable.newChange.nodeChanges,
1589
+ crossFieldTable.newChange.nodeAliases,
1590
+ newId,
1591
+ );
1592
+ const over = nodeChangeFromId(
1593
+ crossFieldTable.baseChange.nodeChanges,
1594
+ crossFieldTable.baseChange.nodeAliases,
1595
+ baseId,
1596
+ );
1342
1597
 
1343
1598
  const baseMap: FieldChangeMap = over?.fieldChanges ?? new Map();
1344
1599
 
@@ -1354,7 +1609,7 @@ export class ModularChangeFamily
1354
1609
  )
1355
1610
  : change.fieldChanges;
1356
1611
 
1357
- const rebasedChange: NodeChangeset = {};
1612
+ const rebasedChange: Mutable<NodeChangeset> = {};
1358
1613
 
1359
1614
  if (fieldChanges !== undefined && fieldChanges.size > 0) {
1360
1615
  rebasedChange.fieldChanges = fieldChanges;
@@ -1372,6 +1627,37 @@ export class ModularChangeFamily
1372
1627
  return rebasedChange;
1373
1628
  }
1374
1629
 
1630
+ private updateConstraints(
1631
+ rebasedFields: FieldChangeMap,
1632
+ rebasedNodes: ChangeAtomIdBTree<NodeChangeset>,
1633
+ rebasedRoots: RootNodeTable,
1634
+ constraintState: ConstraintState,
1635
+ revertConstraintState: ConstraintState,
1636
+ ): void {
1637
+ this.updateConstraintsForFields(
1638
+ rebasedFields,
1639
+ NodeAttachState.Attached,
1640
+ NodeAttachState.Attached,
1641
+ constraintState,
1642
+ revertConstraintState,
1643
+ rebasedNodes,
1644
+ );
1645
+
1646
+ for (const [_detachId, nodeId] of rebasedRoots.nodeChanges.entries()) {
1647
+ // XXX: This is incorrect if the rebased changeset attaches the node.
1648
+ // Efficiently computing whether the changeset attaches the node would require maintaining a mapping from node ID to attach ID.
1649
+ const detachedInOutput = true;
1650
+ this.updateConstraintsForNode(
1651
+ nodeId,
1652
+ NodeAttachState.Detached,
1653
+ detachedInOutput ? NodeAttachState.Detached : NodeAttachState.Attached,
1654
+ rebasedNodes,
1655
+ constraintState,
1656
+ revertConstraintState,
1657
+ );
1658
+ }
1659
+ }
1660
+
1375
1661
  private updateConstraintsForFields(
1376
1662
  fields: FieldChangeMap,
1377
1663
  parentInputAttachState: NodeAttachState,
@@ -1382,20 +1668,18 @@ export class ModularChangeFamily
1382
1668
  ): void {
1383
1669
  for (const field of fields.values()) {
1384
1670
  const handler = getChangeHandler(this.fieldKinds, field.fieldKind);
1385
- for (const [nodeId, inputIndex, outputIndex] of handler.getNestedChanges(field.change)) {
1386
- const isInputDetached = inputIndex === undefined;
1387
- const inputAttachState =
1388
- parentInputAttachState === NodeAttachState.Detached || isInputDetached
1389
- ? NodeAttachState.Detached
1390
- : NodeAttachState.Attached;
1391
- const isOutputDetached = outputIndex === undefined;
1671
+ for (const [nodeId] of handler.getNestedChanges(field.change)) {
1672
+ // XXX: This is incorrect if the rebased changeset detaches this node.
1673
+ // Efficiently computing whether the changeset detaches the node would require maintaining a mapping from node ID to detach ID.
1674
+ const isOutputDetached = false;
1392
1675
  const outputAttachState =
1393
1676
  parentOutputAttachState === NodeAttachState.Detached || isOutputDetached
1394
1677
  ? NodeAttachState.Detached
1395
1678
  : NodeAttachState.Attached;
1679
+
1396
1680
  this.updateConstraintsForNode(
1397
1681
  nodeId,
1398
- inputAttachState,
1682
+ parentInputAttachState,
1399
1683
  outputAttachState,
1400
1684
  nodes,
1401
1685
  constraintState,
@@ -1413,12 +1697,15 @@ export class ModularChangeFamily
1413
1697
  constraintState: ConstraintState,
1414
1698
  revertConstraintState: ConstraintState,
1415
1699
  ): void {
1416
- const node =
1417
- nodes.get([nodeId.revision, nodeId.localId]) ?? fail(0xb24 /* Unknown node ID */);
1700
+ const node = getFromChangeAtomIdMap(nodes, nodeId) ?? fail(0xb24 /* Unknown node ID */);
1701
+
1702
+ const updatedNode: Mutable<NodeChangeset> = { ...node };
1703
+ setInChangeAtomIdMap(nodes, nodeId, updatedNode);
1704
+
1418
1705
  if (node.nodeExistsConstraint !== undefined) {
1419
1706
  const isNowViolated = inputAttachState === NodeAttachState.Detached;
1420
1707
  if (node.nodeExistsConstraint.violated !== isNowViolated) {
1421
- node.nodeExistsConstraint = {
1708
+ updatedNode.nodeExistsConstraint = {
1422
1709
  ...node.nodeExistsConstraint,
1423
1710
  violated: isNowViolated,
1424
1711
  };
@@ -1428,7 +1715,7 @@ export class ModularChangeFamily
1428
1715
  if (node.nodeExistsConstraintOnRevert !== undefined) {
1429
1716
  const isNowViolated = outputAttachState === NodeAttachState.Detached;
1430
1717
  if (node.nodeExistsConstraintOnRevert.violated !== isNowViolated) {
1431
- node.nodeExistsConstraintOnRevert = {
1718
+ updatedNode.nodeExistsConstraintOnRevert = {
1432
1719
  ...node.nodeExistsConstraintOnRevert,
1433
1720
  violated: isNowViolated,
1434
1721
  };
@@ -1450,7 +1737,13 @@ export class ModularChangeFamily
1450
1737
 
1451
1738
  private pruneFieldMap(
1452
1739
  changeset: FieldChangeMap | undefined,
1740
+ parentId: NodeId | undefined,
1453
1741
  nodeMap: ChangeAtomIdBTree<NodeChangeset>,
1742
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>,
1743
+ aliases: ChangeAtomIdBTree<NodeId>,
1744
+ roots: RootNodeTable,
1745
+ fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
1746
+ fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
1454
1747
  ): FieldChangeMap | undefined {
1455
1748
  if (changeset === undefined) {
1456
1749
  return undefined;
@@ -1461,10 +1754,48 @@ export class ModularChangeFamily
1461
1754
  const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
1462
1755
 
1463
1756
  const prunedFieldChangeset = handler.rebaser.prune(fieldChange.change, (nodeId) =>
1464
- this.pruneNodeChange(nodeId, nodeMap),
1757
+ this.pruneNodeChange(
1758
+ nodeId,
1759
+ nodeMap,
1760
+ nodeToParent,
1761
+ aliases,
1762
+ roots,
1763
+ fieldsWithRootMoves,
1764
+ fieldsToRootChanges,
1765
+ ),
1465
1766
  );
1466
1767
 
1467
- if (!handler.isEmpty(prunedFieldChangeset)) {
1768
+ const fieldId: FieldId = { nodeId: parentId, field };
1769
+ const fieldIdKey = fieldIdKeyFromFieldId(fieldId);
1770
+ const rootsWithChanges = fieldsToRootChanges.get(fieldIdKey) ?? [];
1771
+ let hasRootWithNodeChange = false;
1772
+ for (const rootId of rootsWithChanges) {
1773
+ const nodeId =
1774
+ getFromChangeAtomIdMap(roots.nodeChanges, rootId) ?? fail("No root change found");
1775
+
1776
+ const isRootChangeEmpty =
1777
+ this.pruneNodeChange(
1778
+ nodeId,
1779
+ nodeMap,
1780
+ nodeToParent,
1781
+ aliases,
1782
+ roots,
1783
+ fieldsWithRootMoves,
1784
+ fieldsToRootChanges,
1785
+ ) === undefined;
1786
+
1787
+ if (isRootChangeEmpty) {
1788
+ roots.nodeChanges.delete([rootId.revision, rootId.localId]);
1789
+ tryRemoveDetachLocation(roots, rootId, 1);
1790
+ } else {
1791
+ hasRootWithNodeChange = true;
1792
+ }
1793
+ }
1794
+
1795
+ const hasRootChanges =
1796
+ hasRootWithNodeChange || fieldsWithRootMoves.get(fieldIdKey) === true;
1797
+
1798
+ if (!handler.isEmpty(prunedFieldChangeset) || hasRootChanges) {
1468
1799
  prunedChangeset.set(field, { ...fieldChange, change: brand(prunedFieldChangeset) });
1469
1800
  }
1470
1801
  }
@@ -1472,15 +1803,65 @@ export class ModularChangeFamily
1472
1803
  return prunedChangeset.size > 0 ? prunedChangeset : undefined;
1473
1804
  }
1474
1805
 
1806
+ private pruneRoots(
1807
+ roots: RootNodeTable,
1808
+ nodeMap: ChangeAtomIdBTree<NodeChangeset>,
1809
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>,
1810
+ aliases: ChangeAtomIdBTree<NodeId>,
1811
+ fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
1812
+ fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
1813
+ ): RootNodeTable {
1814
+ const pruned: RootNodeTable = { ...roots, nodeChanges: newTupleBTree() };
1815
+ for (const [rootIdKey, nodeId] of roots.nodeChanges.entries()) {
1816
+ const rootId: ChangeAtomId = { revision: rootIdKey[0], localId: rootIdKey[1] };
1817
+ const hasDetachLocation = roots.detachLocations.getFirst(rootId, 1).value !== undefined;
1818
+
1819
+ // If the root has a detach location it should be pruned by recursion when pruning the field it was detached from.
1820
+ const prunedId = hasDetachLocation
1821
+ ? nodeId
1822
+ : this.pruneNodeChange(
1823
+ nodeId,
1824
+ nodeMap,
1825
+ nodeToParent,
1826
+ aliases,
1827
+ roots,
1828
+ fieldsWithRootMoves,
1829
+ fieldsToRootChanges,
1830
+ );
1831
+
1832
+ if (prunedId !== undefined) {
1833
+ pruned.nodeChanges.set(rootIdKey, prunedId);
1834
+ }
1835
+
1836
+ tryRemoveDetachLocation(pruned, rootId, 1);
1837
+ }
1838
+
1839
+ return pruned;
1840
+ }
1841
+
1475
1842
  private pruneNodeChange(
1476
1843
  nodeId: NodeId,
1477
- nodeMap: ChangeAtomIdBTree<NodeChangeset>,
1844
+ nodes: ChangeAtomIdBTree<NodeChangeset>,
1845
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>,
1846
+ aliases: ChangeAtomIdBTree<NodeId>,
1847
+ roots: RootNodeTable,
1848
+ fieldsWithRootMoves: TupleBTree<FieldIdKey, boolean>,
1849
+ fieldsToRootChanges: TupleBTree<FieldIdKey, ChangeAtomId[]>,
1478
1850
  ): NodeId | undefined {
1479
- const changeset = nodeChangeFromId(nodeMap, nodeId);
1851
+ const changeset = nodeChangeFromId(nodes, aliases, nodeId);
1480
1852
  const prunedFields =
1481
- changeset.fieldChanges !== undefined
1482
- ? this.pruneFieldMap(changeset.fieldChanges, nodeMap)
1483
- : undefined;
1853
+ changeset.fieldChanges === undefined
1854
+ ? undefined
1855
+ : this.pruneFieldMap(
1856
+ changeset.fieldChanges,
1857
+ nodeId,
1858
+ nodes,
1859
+ nodeToParent,
1860
+ aliases,
1861
+ roots,
1862
+ fieldsWithRootMoves,
1863
+ fieldsToRootChanges,
1864
+ );
1484
1865
 
1485
1866
  const prunedChange = { ...changeset, fieldChanges: prunedFields };
1486
1867
  if (prunedChange.fieldChanges === undefined) {
@@ -1488,103 +1869,92 @@ export class ModularChangeFamily
1488
1869
  }
1489
1870
 
1490
1871
  if (isEmptyNodeChangeset(prunedChange)) {
1491
- nodeMap.delete([nodeId.revision, nodeId.localId]);
1872
+ const nodeIdKey: [RevisionTag | undefined, ChangesetLocalId] = [
1873
+ nodeId.revision,
1874
+ nodeId.localId,
1875
+ ];
1876
+
1877
+ // TODO: Shouldn't we also delete all aliases associated with this node?
1878
+ nodes.delete(nodeIdKey);
1879
+ nodeToParent.delete(nodeIdKey);
1492
1880
  return undefined;
1493
1881
  } else {
1494
- setInChangeAtomIdMap(nodeMap, nodeId, prunedChange);
1882
+ setInChangeAtomIdMap(nodes, nodeId, prunedChange);
1495
1883
  return nodeId;
1496
1884
  }
1497
1885
  }
1498
1886
 
1887
+ public getRevisions(change: ModularChangeset): Set<RevisionTag | undefined> {
1888
+ if (change.revisions === undefined || change.revisions.length === 0) {
1889
+ return new Set([undefined]);
1890
+ }
1891
+ const aggregated: Set<RevisionTag | undefined> = new Set();
1892
+ for (const revInfo of change.revisions) {
1893
+ aggregated.add(revInfo.revision);
1894
+ }
1895
+ return aggregated;
1896
+ }
1897
+
1499
1898
  public changeRevision(
1500
1899
  change: ModularChangeset,
1501
- newRevision: RevisionTag | undefined,
1502
- rollbackOf?: RevisionTag,
1900
+ replacer: RevisionReplacer,
1503
1901
  ): ModularChangeset {
1504
- const oldRevisions = new Set(
1505
- change.revisions === undefined || change.revisions.length === 0
1506
- ? [undefined]
1507
- : change.revisions.map((revInfo) => revInfo.revision),
1508
- );
1509
- const updatedFields = this.replaceFieldMapRevisions(
1510
- change.fieldChanges,
1511
- oldRevisions,
1512
- newRevision,
1902
+ const updatedFields = this.replaceFieldMapRevisions(change.fieldChanges, replacer);
1903
+ const updatedNodes = replaceIdMapRevisions(change.nodeChanges, replacer, (nodeChangeset) =>
1904
+ this.replaceNodeChangesetRevisions(nodeChangeset, replacer),
1513
1905
  );
1514
-
1515
- const updatedNodes: ChangeAtomIdBTree<NodeChangeset> = newTupleBTree();
1516
- for (const [[revision, id], nodeChangeset] of change.nodeChanges.entries()) {
1517
- updatedNodes.set(
1518
- [replaceRevision(revision, oldRevisions, newRevision), id],
1519
- this.replaceNodeChangesetRevisions(nodeChangeset, oldRevisions, newRevision),
1520
- );
1521
- }
1522
-
1523
- const updatedNodeToParent: ChangeAtomIdBTree<FieldId> = newTupleBTree();
1524
- for (const [[revision, id], fieldId] of change.nodeToParent.entries()) {
1525
- updatedNodeToParent.set(
1526
- [replaceRevision(revision, oldRevisions, newRevision), id],
1527
- replaceFieldIdRevision(
1528
- normalizeFieldId(fieldId, change.nodeAliases),
1529
- oldRevisions,
1530
- newRevision,
1906
+ const updatedNodeToParent = replaceIdMapRevisions(
1907
+ change.nodeToParent,
1908
+ replacer,
1909
+ (location) =>
1910
+ replaceNodeLocationRevision(
1911
+ normalizeNodeLocation(location, change.nodeAliases),
1912
+ replacer,
1531
1913
  ),
1532
- );
1533
- }
1914
+ );
1534
1915
 
1535
1916
  const updated: Mutable<ModularChangeset> = {
1536
1917
  ...change,
1537
1918
  fieldChanges: updatedFields,
1538
1919
  nodeChanges: updatedNodes,
1539
1920
  nodeToParent: updatedNodeToParent,
1921
+ rootNodes: replaceRootTableRevision(change.rootNodes, replacer, change.nodeAliases),
1540
1922
 
1541
1923
  // We've updated all references to old node IDs, so we no longer need an alias table.
1542
1924
  nodeAliases: newTupleBTree(),
1543
1925
  crossFieldKeys: replaceCrossFieldKeyTableRevisions(
1544
1926
  change.crossFieldKeys,
1545
- oldRevisions,
1546
- newRevision,
1927
+ replacer,
1547
1928
  change.nodeAliases,
1548
1929
  ),
1549
1930
  };
1550
1931
 
1551
1932
  if (change.builds !== undefined) {
1552
- updated.builds = replaceIdMapRevisions(change.builds, oldRevisions, newRevision);
1933
+ updated.builds = replaceIdMapRevisions(change.builds, replacer);
1553
1934
  }
1554
1935
 
1555
1936
  if (change.destroys !== undefined) {
1556
- updated.destroys = replaceIdMapRevisions(change.destroys, oldRevisions, newRevision);
1937
+ updated.destroys = replaceIdMapRevisions(change.destroys, replacer);
1557
1938
  }
1558
1939
 
1559
1940
  if (change.refreshers !== undefined) {
1560
- updated.refreshers = replaceIdMapRevisions(change.refreshers, oldRevisions, newRevision);
1941
+ updated.refreshers = replaceIdMapRevisions(change.refreshers, replacer);
1561
1942
  }
1562
1943
 
1563
- if (newRevision !== undefined) {
1564
- const revInfo: Mutable<RevisionInfo> = { revision: newRevision };
1565
- if (rollbackOf !== undefined) {
1566
- revInfo.rollbackOf = rollbackOf;
1567
- }
1568
-
1569
- updated.revisions = [revInfo];
1570
- } else {
1571
- delete updated.revisions;
1572
- }
1944
+ updated.revisions = [{ revision: replacer.updatedRevision }];
1573
1945
 
1574
1946
  return updated;
1575
1947
  }
1576
1948
 
1577
1949
  private replaceNodeChangesetRevisions(
1578
1950
  nodeChangeset: NodeChangeset,
1579
- oldRevisions: Set<RevisionTag | undefined>,
1580
- newRevision: RevisionTag | undefined,
1951
+ replacer: RevisionReplacer,
1581
1952
  ): NodeChangeset {
1582
1953
  const updated = { ...nodeChangeset };
1583
1954
  if (nodeChangeset.fieldChanges !== undefined) {
1584
1955
  updated.fieldChanges = this.replaceFieldMapRevisions(
1585
1956
  nodeChangeset.fieldChanges,
1586
- oldRevisions,
1587
- newRevision,
1957
+ replacer,
1588
1958
  );
1589
1959
  }
1590
1960
 
@@ -1593,15 +1963,14 @@ export class ModularChangeFamily
1593
1963
 
1594
1964
  private replaceFieldMapRevisions(
1595
1965
  fields: FieldChangeMap,
1596
- oldRevisions: Set<RevisionTag | undefined>,
1597
- newRevision: RevisionTag | undefined,
1966
+ replacer: RevisionReplacer,
1598
1967
  ): FieldChangeMap {
1599
1968
  const updatedFields: FieldChangeMap = new Map();
1600
1969
  for (const [field, fieldChange] of fields) {
1601
1970
  const updatedFieldChange = getChangeHandler(
1602
1971
  this.fieldKinds,
1603
1972
  fieldChange.fieldKind,
1604
- ).rebaser.replaceRevisions(fieldChange.change, oldRevisions, newRevision);
1973
+ ).rebaser.replaceRevisions(fieldChange.change, replacer);
1605
1974
 
1606
1975
  updatedFields.set(field, { ...fieldChange, change: brand(updatedFieldChange) });
1607
1976
  }
@@ -1613,7 +1982,7 @@ export class ModularChangeFamily
1613
1982
  fields: FieldChangeMap,
1614
1983
  nodes: ChangeAtomIdBTree<NodeChangeset>,
1615
1984
  ): CrossFieldKeyTable {
1616
- const keys: CrossFieldKeyTable = newCrossFieldKeyTable();
1985
+ const keys: CrossFieldKeyTable = newCrossFieldRangeTable();
1617
1986
  this.populateCrossFieldKeyTableForFieldMap(keys, fields, undefined);
1618
1987
  nodes.forEachPair(([revision, localId], node) => {
1619
1988
  if (node.fieldChanges !== undefined) {
@@ -1643,9 +2012,10 @@ export class ModularChangeFamily
1643
2012
  }
1644
2013
 
1645
2014
  public buildEditor(
2015
+ mintRevisionTag: () => RevisionTag,
1646
2016
  changeReceiver: (change: TaggedChange<ModularChangeset>) => void,
1647
2017
  ): ModularEditBuilder {
1648
- return new ModularEditBuilder(this, this.fieldKinds, changeReceiver);
2018
+ return new ModularEditBuilder(this, this.fieldKinds, changeReceiver, this.codecOptions);
1649
2019
  }
1650
2020
 
1651
2021
  private createEmptyFieldChange(fieldKind: FieldKindIdentifier): FieldChange {
@@ -1653,63 +2023,6 @@ export class ModularChangeFamily
1653
2023
  return { fieldKind, change: brand(emptyChange) };
1654
2024
  }
1655
2025
 
1656
- public validateChangeset(change: ModularChangeset): void {
1657
- let numNodes = this.validateFieldChanges(change, change.fieldChanges, undefined);
1658
-
1659
- for (const [[revision, localId], node] of change.nodeChanges.entries()) {
1660
- if (node.fieldChanges === undefined) {
1661
- continue;
1662
- }
1663
-
1664
- const nodeId: NodeId = { revision, localId };
1665
- const numChildren = this.validateFieldChanges(change, node.fieldChanges, nodeId);
1666
-
1667
- numNodes += numChildren;
1668
- }
1669
-
1670
- assert(
1671
- numNodes === change.nodeChanges.size,
1672
- 0xa4d /* Node table contains unparented nodes */,
1673
- );
1674
- }
1675
-
1676
- /**
1677
- * Asserts that each child and cross field key in each field has a correct entry in
1678
- * `nodeToParent` or `crossFieldKeyTable`.
1679
- * @returns the number of children found.
1680
- */
1681
- private validateFieldChanges(
1682
- change: ModularChangeset,
1683
- fieldChanges: FieldChangeMap,
1684
- nodeParent: NodeId | undefined,
1685
- ): number {
1686
- let numChildren = 0;
1687
- for (const [field, fieldChange] of fieldChanges.entries()) {
1688
- const fieldId = { nodeId: nodeParent, field };
1689
- const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
1690
- for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
1691
- const parentFieldId = getParentFieldId(change, child);
1692
- assert(
1693
- areEqualFieldIds(parentFieldId, fieldId),
1694
- 0xa4e /* Inconsistent node parentage */,
1695
- );
1696
- numChildren += 1;
1697
- }
1698
-
1699
- for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
1700
- const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
1701
- assert(
1702
- fields.length === 1 &&
1703
- fields[0] !== undefined &&
1704
- areEqualFieldIds(fields[0], fieldId),
1705
- 0xa4f /* Inconsistent cross field keys */,
1706
- );
1707
- }
1708
- }
1709
-
1710
- return numChildren;
1711
- }
1712
-
1713
2026
  private getEffectiveChange(change: ModularChangeset): ModularChangeset {
1714
2027
  if (hasConflicts(change)) {
1715
2028
  return this.muteChange(change);
@@ -1723,7 +2036,8 @@ export class ModularChangeFamily
1723
2036
  private muteChange(change: ModularChangeset): ModularChangeset {
1724
2037
  const muted: Mutable<ModularChangeset> = {
1725
2038
  ...change,
1726
- crossFieldKeys: newCrossFieldKeyTable(),
2039
+ rootNodes: muteRootChanges(change.rootNodes),
2040
+ crossFieldKeys: newCrossFieldRangeTable(),
1727
2041
  fieldChanges: this.muteFieldChanges(change.fieldChanges),
1728
2042
  nodeChanges: brand(change.nodeChanges.mapValues((v) => this.muteNodeChange(v))),
1729
2043
  };
@@ -1757,25 +2071,20 @@ export class ModularChangeFamily
1757
2071
 
1758
2072
  function replaceCrossFieldKeyTableRevisions(
1759
2073
  table: CrossFieldKeyTable,
1760
- oldRevisions: Set<RevisionTag | undefined>,
1761
- newRevision: RevisionTag | undefined,
2074
+ replacer: RevisionReplacer,
1762
2075
  nodeAliases: ChangeAtomIdBTree<NodeId>,
1763
2076
  ): CrossFieldKeyTable {
1764
- const updated: CrossFieldKeyTable = newCrossFieldKeyTable();
2077
+ const updated: CrossFieldKeyTable = newCrossFieldRangeTable();
1765
2078
  for (const entry of table.entries()) {
1766
2079
  const key = entry.start;
1767
- const updatedKey: CrossFieldKey = {
1768
- target: key.target,
1769
- revision: replaceRevision(key.revision, oldRevisions, newRevision),
1770
- localId: key.localId,
1771
- };
2080
+ const updatedKey: CrossFieldKey = replacer.getUpdatedAtomId(key);
1772
2081
 
1773
2082
  const field = entry.value;
1774
2083
  const normalizedFieldId = normalizeFieldId(field, nodeAliases);
1775
2084
  const updatedNodeId =
1776
- normalizedFieldId.nodeId !== undefined
1777
- ? replaceAtomRevisions(normalizedFieldId.nodeId, oldRevisions, newRevision)
1778
- : undefined;
2085
+ normalizedFieldId.nodeId === undefined
2086
+ ? undefined
2087
+ : replacer.getUpdatedAtomId(normalizedFieldId.nodeId);
1779
2088
 
1780
2089
  const updatedValue: FieldId = {
1781
2090
  ...normalizedFieldId,
@@ -1788,22 +2097,15 @@ function replaceCrossFieldKeyTableRevisions(
1788
2097
  return updated;
1789
2098
  }
1790
2099
 
1791
- function replaceRevision(
1792
- revision: RevisionTag | undefined,
1793
- oldRevisions: Set<RevisionTag | undefined>,
1794
- newRevision: RevisionTag | undefined,
1795
- ): RevisionTag | undefined {
1796
- return oldRevisions.has(revision) ? newRevision : revision;
1797
- }
1798
-
1799
2100
  function replaceIdMapRevisions<T>(
1800
2101
  map: ChangeAtomIdBTree<T>,
1801
- oldRevisions: Set<RevisionTag | undefined>,
1802
- newRevision: RevisionTag | undefined,
2102
+ replacer: RevisionReplacer,
2103
+ valueMapper: (value: T) => T = (value) => value,
1803
2104
  ): ChangeAtomIdBTree<T> {
1804
2105
  const updated: ChangeAtomIdBTree<T> = newTupleBTree();
1805
- for (const [[revision, id], value] of map.entries()) {
1806
- updated.set([replaceRevision(revision, oldRevisions, newRevision), id], value);
2106
+ for (const [[revision, localId], value] of map.entries()) {
2107
+ const newAtom = replacer.getUpdatedAtomId({ revision, localId });
2108
+ updated.set([newAtom.revision, newAtom.localId], valueMapper(value));
1807
2109
  }
1808
2110
 
1809
2111
  return updated;
@@ -1879,6 +2181,20 @@ function composeBuildsDestroysAndRefreshers(
1879
2181
  }
1880
2182
  }
1881
2183
 
2184
+ // 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.
2185
+ if (change1.builds !== undefined && change2.refreshers !== undefined) {
2186
+ for (const [key, chunk] of change2.refreshers.entries()) {
2187
+ assert(chunk.topLevelLength === 1, "Expected refresher chunk to have length 1");
2188
+ const match = change1.builds.getPairOrNextLower(key);
2189
+ if (match !== undefined) {
2190
+ const [buildKey, buildChunk] = match;
2191
+ if (buildKey[0] === key[0] && buildKey[1] + buildChunk.topLevelLength > key[1]) {
2192
+ allRefreshers.delete(key);
2193
+ }
2194
+ }
2195
+ }
2196
+ }
2197
+
1882
2198
  return { allBuilds, allDestroys, allRefreshers };
1883
2199
  }
1884
2200
 
@@ -1910,27 +2226,77 @@ export function* relevantRemovedRoots(
1910
2226
  change: ModularChangeset,
1911
2227
  fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
1912
2228
  ): Iterable<DeltaDetachedNodeId> {
1913
- yield* relevantRemovedRootsFromFields(change.fieldChanges, change.nodeChanges, fieldKinds);
2229
+ const rootIds: ChangeAtomIdRangeMap<boolean> = newChangeAtomIdRangeMap();
2230
+ addAttachesToSet(change, rootIds);
2231
+ addRenamesToSet(change, rootIds);
2232
+
2233
+ for (const [[revision, localId]] of change.rootNodes.nodeChanges.entries()) {
2234
+ rootIds.set({ revision, localId }, 1, true);
2235
+ }
2236
+
2237
+ for (const entry of rootIds.entries()) {
2238
+ for (let offset = 0; offset < entry.length; offset++) {
2239
+ const detachId = offsetChangeAtomId(entry.start, offset);
2240
+ yield makeDetachedNodeId(detachId.revision, detachId.localId);
2241
+ }
2242
+ }
1914
2243
  }
1915
2244
 
1916
- function* relevantRemovedRootsFromFields(
1917
- change: FieldChangeMap,
1918
- nodeChanges: ChangeAtomIdBTree<NodeChangeset>,
1919
- fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
1920
- ): Iterable<DeltaDetachedNodeId> {
1921
- for (const [_, fieldChange] of change) {
1922
- const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
1923
- const delegate = function* (node: NodeId): Iterable<DeltaDetachedNodeId> {
1924
- const nodeChangeset = nodeChangeFromId(nodeChanges, node);
1925
- if (nodeChangeset.fieldChanges !== undefined) {
1926
- yield* relevantRemovedRootsFromFields(
1927
- nodeChangeset.fieldChanges,
1928
- nodeChanges,
1929
- fieldKinds,
1930
- );
2245
+ export function* getBuildIds(change: ModularChangeset): Iterable<DeltaDetachedNodeId> {
2246
+ if (change.builds !== undefined) {
2247
+ for (const [[revision, localId]] of change.builds.entries()) {
2248
+ yield makeDetachedNodeId(revision, localId);
2249
+ }
2250
+ }
2251
+ }
2252
+
2253
+ function addAttachesToSet(
2254
+ change: ModularChangeset,
2255
+ rootIds: ChangeAtomIdRangeMap<boolean>,
2256
+ ): void {
2257
+ // This includes each attach which does not have a corresponding detach.
2258
+ for (const entry of change.crossFieldKeys.entries()) {
2259
+ if (entry.start.target !== CrossFieldTarget.Destination) {
2260
+ continue;
2261
+ }
2262
+
2263
+ for (const detachIdEntry of change.rootNodes.newToOldId.getAll2(
2264
+ entry.start,
2265
+ entry.length,
2266
+ )) {
2267
+ const detachId =
2268
+ detachIdEntry.value ?? offsetChangeAtomId(entry.start, detachIdEntry.offset);
2269
+ for (const detachEntry of change.crossFieldKeys.getAll2(
2270
+ { ...detachId, target: CrossFieldTarget.Source },
2271
+ detachIdEntry.length,
2272
+ )) {
2273
+ if (detachEntry.value === undefined) {
2274
+ rootIds.set(
2275
+ offsetChangeAtomId(detachId, detachEntry.offset),
2276
+ detachEntry.length,
2277
+ true,
2278
+ );
2279
+ }
1931
2280
  }
1932
- };
1933
- yield* handler.relevantRemovedRoots(fieldChange.change, delegate);
2281
+ }
2282
+ }
2283
+ }
2284
+
2285
+ function addRenamesToSet(
2286
+ change: ModularChangeset,
2287
+ rootIds: ChangeAtomIdRangeMap<boolean>,
2288
+ ): void {
2289
+ for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
2290
+ for (const detachEntry of change.crossFieldKeys.getAll2(
2291
+ { ...renameEntry.start, target: CrossFieldTarget.Source },
2292
+ renameEntry.length,
2293
+ )) {
2294
+ // We only want to include renames of nodes which are detached in the input context of the changeset.
2295
+ // So if there is a detach for the node, the rename is not relevant.
2296
+ if (detachEntry.value === undefined) {
2297
+ rootIds.set(renameEntry.start, renameEntry.length, true);
2298
+ }
2299
+ }
1934
2300
  }
1935
2301
  }
1936
2302
 
@@ -1990,20 +2356,25 @@ export function updateRefreshers(
1990
2356
  const {
1991
2357
  fieldChanges,
1992
2358
  nodeChanges,
2359
+ nodeToParent,
2360
+ nodeAliases,
2361
+ crossFieldKeys,
1993
2362
  maxId,
1994
2363
  revisions,
1995
2364
  constraintViolationCount,
1996
2365
  constraintViolationCountOnRevert,
1997
2366
  builds,
1998
2367
  destroys,
2368
+ rootNodes,
1999
2369
  } = change;
2000
2370
 
2001
2371
  return makeModularChangeset({
2002
2372
  fieldChanges,
2003
2373
  nodeChanges,
2004
- nodeToParent: change.nodeToParent,
2005
- nodeAliases: change.nodeAliases,
2006
- crossFieldKeys: change.crossFieldKeys,
2374
+ nodeToParent,
2375
+ nodeAliases,
2376
+ crossFieldKeys,
2377
+ rootNodes,
2007
2378
  maxId: maxId as number,
2008
2379
  revisions,
2009
2380
  constraintViolationCount,
@@ -2026,18 +2397,42 @@ export function intoDelta(
2026
2397
  ): DeltaRoot {
2027
2398
  const change = taggedChange.change;
2028
2399
  const rootDelta: Mutable<DeltaRoot> = {};
2029
- const global: DeltaDetachedNodeChanges[] = [];
2030
- const rename: DeltaDetachedNodeRename[] = [];
2031
2400
 
2032
2401
  if (!hasConflicts(change)) {
2033
2402
  // If there are no constraint violations, then tree changes apply.
2034
2403
  const fieldDeltas = intoDeltaImpl(
2035
2404
  change.fieldChanges,
2036
2405
  change.nodeChanges,
2406
+ change.nodeAliases,
2037
2407
  fieldKinds,
2038
- global,
2039
- rename,
2040
2408
  );
2409
+
2410
+ const global: DeltaDetachedNodeChanges[] = [];
2411
+ for (const [[major, minor], nodeId] of change.rootNodes.nodeChanges.entries()) {
2412
+ global.push({
2413
+ id: { major, minor },
2414
+ fields: deltaFromNodeChange(
2415
+ nodeChangeFromId(change.nodeChanges, change.nodeAliases, nodeId),
2416
+ change.nodeChanges,
2417
+ change.nodeAliases,
2418
+ fieldKinds,
2419
+ ),
2420
+ });
2421
+ }
2422
+
2423
+ const rename: DeltaDetachedNodeRename[] = [];
2424
+ for (const {
2425
+ start: oldId,
2426
+ value: newId,
2427
+ length,
2428
+ } of change.rootNodes.oldToNewId.entries()) {
2429
+ rename.push({
2430
+ count: length,
2431
+ oldId: makeDetachedNodeId(oldId.revision, oldId.localId),
2432
+ newId: makeDetachedNodeId(newId.revision, newId.localId),
2433
+ });
2434
+ }
2435
+
2041
2436
  if (fieldDeltas.size > 0) {
2042
2437
  rootDelta.fields = fieldDeltas;
2043
2438
  }
@@ -2066,6 +2461,7 @@ export function intoDelta(
2066
2461
  if (change.refreshers && change.refreshers.size > 0) {
2067
2462
  rootDelta.refreshers = copyDetachedNodes(change.refreshers);
2068
2463
  }
2464
+
2069
2465
  return rootDelta;
2070
2466
  }
2071
2467
 
@@ -2091,29 +2487,22 @@ function copyDetachedNodes(
2091
2487
  function intoDeltaImpl(
2092
2488
  change: FieldChangeMap,
2093
2489
  nodeChanges: ChangeAtomIdBTree<NodeChangeset>,
2490
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
2094
2491
  fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
2095
- global: DeltaDetachedNodeChanges[],
2096
- rename: DeltaDetachedNodeRename[],
2097
2492
  ): Map<FieldKey, DeltaFieldChanges> {
2098
2493
  const delta: Map<FieldKey, DeltaFieldChanges> = new Map();
2099
2494
 
2100
2495
  for (const [field, fieldChange] of change) {
2101
- const {
2102
- local: fieldChanges,
2103
- global: fieldGlobal,
2104
- rename: fieldRename,
2105
- } = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(
2496
+ const fieldDelta = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(
2106
2497
  fieldChange.change,
2107
2498
  (childChange): DeltaFieldMap => {
2108
- const nodeChange = nodeChangeFromId(nodeChanges, childChange);
2109
- return deltaFromNodeChange(nodeChange, nodeChanges, fieldKinds, global, rename);
2499
+ const nodeChange = nodeChangeFromId(nodeChanges, nodeAliases, childChange);
2500
+ return deltaFromNodeChange(nodeChange, nodeChanges, nodeAliases, fieldKinds);
2110
2501
  },
2111
2502
  );
2112
- if (fieldChanges !== undefined && fieldChanges.length > 0) {
2113
- delta.set(field, fieldChanges);
2503
+ if (fieldDelta !== undefined && fieldDelta.length > 0) {
2504
+ delta.set(field, fieldDelta);
2114
2505
  }
2115
- fieldGlobal?.forEach((c) => global.push(c));
2116
- fieldRename?.forEach((r) => rename.push(r));
2117
2506
  }
2118
2507
  return delta;
2119
2508
  }
@@ -2121,12 +2510,11 @@ function intoDeltaImpl(
2121
2510
  function deltaFromNodeChange(
2122
2511
  change: NodeChangeset,
2123
2512
  nodeChanges: ChangeAtomIdBTree<NodeChangeset>,
2513
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
2124
2514
  fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
2125
- global: DeltaDetachedNodeChanges[],
2126
- rename: DeltaDetachedNodeRename[],
2127
2515
  ): DeltaFieldMap {
2128
2516
  if (change.fieldChanges !== undefined) {
2129
- return intoDeltaImpl(change.fieldChanges, nodeChanges, fieldKinds, global, rename);
2517
+ return intoDeltaImpl(change.fieldChanges, nodeChanges, nodeAliases, fieldKinds);
2130
2518
  }
2131
2519
  // TODO: update the API to allow undefined to be returned here
2132
2520
  return new Map();
@@ -2191,19 +2579,21 @@ export function getChangeHandler(
2191
2579
  return getFieldKind(fieldKinds, kind).changeHandler;
2192
2580
  }
2193
2581
 
2194
- // TODO: TFieldData could instead just be a numeric ID generated by the CrossFieldTable
2195
- // The CrossFieldTable could have a generic field ID to context table
2196
- interface CrossFieldTable<TFieldData> {
2197
- srcTable: CrossFieldMap<unknown>;
2198
- dstTable: CrossFieldMap<unknown>;
2199
- srcDependents: CrossFieldMap<TFieldData>;
2200
- dstDependents: CrossFieldMap<TFieldData>;
2201
- invalidatedFields: Set<TFieldData>;
2202
- }
2582
+ interface InvertTable {
2583
+ change: ModularChangeset;
2203
2584
 
2204
- interface InvertTable extends CrossFieldTable<FieldChange> {
2585
+ // Entries are keyed on attach ID
2586
+ entries: CrossFieldMap<NodeId>;
2205
2587
  originalFieldToContext: Map<FieldChange, InvertContext>;
2206
- invertedNodeToParent: ChangeAtomIdBTree<FieldId>;
2588
+ invertedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
2589
+ invertRevision: RevisionTag;
2590
+ invalidatedFields: Set<FieldChange>;
2591
+ invertedRoots: RootNodeTable;
2592
+
2593
+ /**
2594
+ * Maps from attach ID in the inverted changeset to the corresponding detach ID in the base changeset.
2595
+ */
2596
+ attachToDetachId: ChangeAtomIdRangeMap<ChangeAtomId>;
2207
2597
  }
2208
2598
 
2209
2599
  interface InvertContext {
@@ -2211,7 +2601,11 @@ interface InvertContext {
2211
2601
  invertedField: FieldChange;
2212
2602
  }
2213
2603
 
2214
- interface RebaseTable extends CrossFieldTable<FieldChange> {
2604
+ interface RebaseTable {
2605
+ readonly rebaseVersion: RebaseVersion;
2606
+
2607
+ // Entries are keyed on attach ID
2608
+ readonly entries: CrossFieldMap<RebaseDetachedNodeEntry>;
2215
2609
  readonly baseChange: ModularChangeset;
2216
2610
  readonly newChange: ModularChangeset;
2217
2611
 
@@ -2220,10 +2614,13 @@ interface RebaseTable extends CrossFieldTable<FieldChange> {
2220
2614
  * to the context for the field.
2221
2615
  */
2222
2616
  readonly baseFieldToContext: Map<FieldChange, RebaseFieldContext>;
2617
+ readonly baseRoots: RootNodeTable;
2223
2618
  readonly baseToRebasedNodeId: ChangeAtomIdBTree<NodeId>;
2224
2619
  readonly rebasedFields: Set<FieldChange>;
2225
- readonly rebasedNodeToParent: ChangeAtomIdBTree<FieldId>;
2226
- readonly rebasedCrossFieldKeys: CrossFieldKeyTable;
2620
+ readonly rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
2621
+ readonly rebasedDetachLocations: ChangeAtomIdRangeMap<FieldId>;
2622
+ readonly movedDetaches: ChangeAtomIdRangeMap<boolean>;
2623
+ readonly rebasedRootNodes: RootNodeTable;
2227
2624
 
2228
2625
  /**
2229
2626
  * List of unprocessed (newId, baseId) pairs encountered so far.
@@ -2237,7 +2634,7 @@ interface RebaseTable extends CrossFieldTable<FieldChange> {
2237
2634
  readonly fieldsWithUnattachedChild: Set<FieldChange>;
2238
2635
  }
2239
2636
 
2240
- type FieldIdKey = [RevisionTag | undefined, ChangesetLocalId | undefined, FieldKey];
2637
+ export type FieldIdKey = [RevisionTag | undefined, ChangesetLocalId | undefined, FieldKey];
2241
2638
 
2242
2639
  interface RebaseFieldContext {
2243
2640
  baseChange: FieldChange;
@@ -2249,32 +2646,43 @@ interface RebaseFieldContext {
2249
2646
  * The set of node IDs in the base changeset which should be included in the rebased field,
2250
2647
  * even if there is no corresponding node changeset in the new change.
2251
2648
  */
2252
- baseNodeIds: NodeId[];
2649
+ baseNodeIds: ChangeAtomIdBTree<boolean>;
2253
2650
  }
2254
2651
 
2255
2652
  function newComposeTable(
2256
2653
  baseChange: ModularChangeset,
2257
2654
  newChange: ModularChangeset,
2258
- composedNodeToParent: ChangeAtomIdBTree<FieldId>,
2655
+ composedRootNodes: RootNodeTable,
2656
+ movedCrossFieldKeys: CrossFieldKeyTable,
2657
+ removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
2658
+ pendingCompositions: PendingCompositions,
2259
2659
  ): ComposeTable {
2260
2660
  return {
2261
- ...newCrossFieldTable<FieldChange>(),
2661
+ rebaseVersion: Math.max(
2662
+ baseChange.rebaseVersion,
2663
+ newChange.rebaseVersion,
2664
+ ) as RebaseVersion,
2665
+ entries: newDetachedEntryMap(),
2262
2666
  baseChange,
2263
2667
  newChange,
2264
2668
  fieldToContext: new Map(),
2265
2669
  newFieldToBaseField: new Map(),
2266
2670
  newToBaseNodeId: newTupleBTree(),
2267
2671
  composedNodes: new Set(),
2268
- composedNodeToParent,
2269
- pendingCompositions: {
2270
- nodeIdsToCompose: [],
2271
- affectedBaseFields: newTupleBTree(),
2272
- affectedNewFields: newTupleBTree(),
2273
- },
2672
+ movedNodeToParent: newTupleBTree(),
2673
+ composedRootNodes,
2674
+ movedCrossFieldKeys,
2675
+ removedCrossFieldKeys,
2676
+ renamesToDelete: newChangeAtomIdRangeMap(),
2677
+ pendingCompositions,
2274
2678
  };
2275
2679
  }
2276
2680
 
2277
- interface ComposeTable extends CrossFieldTable<FieldChange> {
2681
+ interface ComposeTable {
2682
+ readonly rebaseVersion: RebaseVersion;
2683
+
2684
+ // Entries are keyed on detach ID
2685
+ readonly entries: ChangeAtomIdRangeMap<DetachedNodeEntry>;
2278
2686
  readonly baseChange: ModularChangeset;
2279
2687
  readonly newChange: ModularChangeset;
2280
2688
 
@@ -2285,7 +2693,11 @@ interface ComposeTable extends CrossFieldTable<FieldChange> {
2285
2693
  readonly newFieldToBaseField: Map<FieldChange, FieldChange>;
2286
2694
  readonly newToBaseNodeId: ChangeAtomIdBTree<NodeId>;
2287
2695
  readonly composedNodes: Set<NodeChangeset>;
2288
- readonly composedNodeToParent: ChangeAtomIdBTree<FieldId>;
2696
+ readonly movedNodeToParent: ChangeAtomIdBTree<NodeLocation>;
2697
+ readonly composedRootNodes: RootNodeTable;
2698
+ readonly movedCrossFieldKeys: CrossFieldKeyTable;
2699
+ readonly removedCrossFieldKeys: CrossFieldRangeTable<boolean>;
2700
+ readonly renamesToDelete: ChangeAtomIdRangeMap<boolean>;
2289
2701
  readonly pendingCompositions: PendingCompositions;
2290
2702
  }
2291
2703
 
@@ -2300,11 +2712,6 @@ interface PendingCompositions {
2300
2712
  * The set of fields in the base changeset which have been affected by a cross field effect.
2301
2713
  */
2302
2714
  readonly affectedBaseFields: BTree<FieldIdKey, true>;
2303
-
2304
- /**
2305
- * The set of fields in the new changeset which have been affected by a cross field effect.
2306
- */
2307
- readonly affectedNewFields: BTree<FieldIdKey, true>;
2308
2715
  }
2309
2716
 
2310
2717
  interface ComposeFieldContext {
@@ -2317,16 +2724,6 @@ interface ComposeFieldContext {
2317
2724
  composedChange: FieldChange;
2318
2725
  }
2319
2726
 
2320
- function newCrossFieldTable<T>(): CrossFieldTable<T> {
2321
- return {
2322
- srcTable: newChangeAtomIdRangeMap(),
2323
- dstTable: newChangeAtomIdRangeMap(),
2324
- srcDependents: newChangeAtomIdRangeMap(),
2325
- dstDependents: newChangeAtomIdRangeMap(),
2326
- invalidatedFields: new Set(),
2327
- };
2328
- }
2329
-
2330
2727
  interface ConstraintState {
2331
2728
  violationCount: number;
2332
2729
  }
@@ -2337,342 +2734,771 @@ function newConstraintState(violationCount: number): ConstraintState {
2337
2734
  };
2338
2735
  }
2339
2736
 
2340
- abstract class CrossFieldManagerI<T> implements CrossFieldManager {
2737
+ class InvertNodeManagerI implements InvertNodeManager {
2341
2738
  public constructor(
2342
- protected readonly crossFieldTable: CrossFieldTable<T>,
2343
- private readonly currentFieldKey: T,
2344
- protected readonly allowInval = true,
2739
+ private readonly table: InvertTable,
2740
+ private readonly fieldId: FieldId,
2345
2741
  ) {}
2346
2742
 
2347
- public set(
2348
- target: CrossFieldTarget,
2349
- revision: RevisionTag | undefined,
2350
- id: ChangesetLocalId,
2743
+ public invertDetach(
2744
+ detachId: ChangeAtomId,
2351
2745
  count: number,
2352
- newValue: unknown,
2353
- invalidateDependents: boolean,
2746
+ nodeChange: NodeId | undefined,
2747
+ newAttachId: ChangeAtomId,
2354
2748
  ): void {
2355
- if (invalidateDependents && this.allowInval) {
2356
- const lastChangedId = (id as number) + count - 1;
2357
- let firstId = id;
2358
- while (firstId <= lastChangedId) {
2359
- const dependentEntry = getFirstFromCrossFieldMap(
2360
- this.getDependents(target),
2361
- revision,
2362
- firstId,
2363
- lastChangedId - firstId + 1,
2364
- );
2365
- if (dependentEntry.value !== undefined) {
2366
- this.crossFieldTable.invalidatedFields.add(dependentEntry.value);
2367
- }
2749
+ if (nodeChange !== undefined) {
2750
+ assert(count === 1, "A node change should only affect one node");
2751
+
2752
+ const attachEntry = firstAttachIdFromDetachId(
2753
+ this.table.change.rootNodes,
2754
+ detachId,
2755
+ count,
2756
+ );
2757
+
2758
+ const attachFieldEntry = this.table.change.crossFieldKeys.getFirst(
2759
+ { target: CrossFieldTarget.Destination, ...attachEntry.value },
2760
+ count,
2761
+ );
2368
2762
 
2369
- firstId = brand(firstId + dependentEntry.length);
2763
+ if (attachFieldEntry.value === undefined) {
2764
+ assignRootChange(
2765
+ this.table.invertedRoots,
2766
+ this.table.invertedNodeToParent,
2767
+ attachEntry.value,
2768
+ nodeChange,
2769
+ this.fieldId,
2770
+ this.table.change.rebaseVersion,
2771
+ );
2772
+ } else {
2773
+ setInCrossFieldMap(this.table.entries, attachEntry.value, count, nodeChange);
2774
+ this.table.invalidatedFields.add(
2775
+ fieldChangeFromId(this.table.change, attachFieldEntry.value),
2776
+ );
2370
2777
  }
2371
2778
  }
2372
- setInCrossFieldMap(this.getMap(target), revision, id, count, newValue);
2373
- }
2374
2779
 
2375
- public get(
2376
- target: CrossFieldTarget,
2377
- revision: RevisionTag | undefined,
2378
- id: ChangesetLocalId,
2379
- count: number,
2380
- addDependency: boolean,
2381
- ): RangeQueryResult<ChangeAtomId, unknown> {
2382
- if (addDependency) {
2383
- // We assume that if there is already an entry for this ID it is because
2384
- // a field handler has called compose on the same node multiple times.
2385
- // In this case we only want to update the latest version, so we overwrite the dependency.
2386
- setInCrossFieldMap(
2387
- this.getDependents(target),
2388
- revision,
2389
- id,
2780
+ if (!areEqualChangeAtomIds(detachId, newAttachId)) {
2781
+ for (const entry of doesChangeAttachNodes(
2782
+ this.table.change.crossFieldKeys,
2783
+ detachId,
2390
2784
  count,
2391
- this.currentFieldKey,
2392
- );
2785
+ )) {
2786
+ if (!entry.value) {
2787
+ this.table.attachToDetachId.set(newAttachId, count, detachId);
2788
+ this.table.invertedRoots.detachLocations.set(detachId, count, this.fieldId);
2789
+ }
2790
+ }
2393
2791
  }
2394
- return getFirstFromCrossFieldMap(this.getMap(target), revision, id, count);
2395
2792
  }
2396
2793
 
2397
- public abstract onMoveIn(id: NodeId): void;
2398
-
2399
- public abstract moveKey(
2400
- target: CrossFieldTarget,
2401
- revision: RevisionTag | undefined,
2402
- id: ChangesetLocalId,
2794
+ public invertAttach(
2795
+ attachId: ChangeAtomId,
2403
2796
  count: number,
2404
- ): void;
2405
-
2406
- private getMap(target: CrossFieldTarget): CrossFieldMap<unknown> {
2407
- return target === CrossFieldTarget.Source
2408
- ? this.crossFieldTable.srcTable
2409
- : this.crossFieldTable.dstTable;
2410
- }
2797
+ ): RangeQueryResult<DetachedNodeEntry> {
2798
+ let countToProcess = count;
2411
2799
 
2412
- private getDependents(target: CrossFieldTarget): CrossFieldMap<T> {
2413
- return target === CrossFieldTarget.Source
2414
- ? this.crossFieldTable.srcDependents
2415
- : this.crossFieldTable.dstDependents;
2416
- }
2417
- }
2800
+ const detachIdEntry = firstDetachIdFromAttachId(
2801
+ this.table.change.rootNodes,
2802
+ attachId,
2803
+ countToProcess,
2804
+ );
2418
2805
 
2419
- class InvertManager extends CrossFieldManagerI<FieldChange> {
2420
- public constructor(
2421
- table: InvertTable,
2422
- field: FieldChange,
2423
- private readonly fieldId: FieldId,
2424
- allowInval = true,
2425
- ) {
2426
- super(table, field, allowInval);
2427
- }
2806
+ countToProcess = detachIdEntry.length;
2428
2807
 
2429
- public override onMoveIn(id: ChangeAtomId): void {
2430
- setInChangeAtomIdMap(this.table.invertedNodeToParent, id, this.fieldId);
2431
- }
2808
+ const detachEntry = getFirstFieldForCrossFieldKey(
2809
+ this.table.change,
2810
+ { target: CrossFieldTarget.Source, ...detachIdEntry.value },
2811
+ countToProcess,
2812
+ );
2813
+ countToProcess = detachEntry.length;
2814
+
2815
+ let result: RangeQueryResult<DetachedNodeEntry>;
2816
+ if (detachEntry.value === undefined) {
2817
+ // This node is detached in the input context of the original change.
2818
+ const nodeIdEntry = rangeQueryChangeAtomIdMap(
2819
+ this.table.change.rootNodes.nodeChanges,
2820
+ detachIdEntry.value,
2821
+ countToProcess,
2822
+ );
2432
2823
 
2433
- public override moveKey(
2434
- target: CrossFieldTarget,
2435
- revision: RevisionTag | undefined,
2436
- id: ChangesetLocalId,
2437
- count: number,
2438
- ): void {
2439
- assert(false, 0x9c5 /* Keys should not be moved manually during invert */);
2440
- }
2824
+ countToProcess = nodeIdEntry.length;
2825
+ result = {
2826
+ value: { nodeChange: nodeIdEntry.value, detachId: detachIdEntry.value },
2827
+ length: countToProcess,
2828
+ };
2829
+ } else {
2830
+ const moveEntry = this.table.entries.getFirst(attachId, countToProcess);
2831
+ result = { ...moveEntry, value: { nodeChange: moveEntry.value } };
2832
+ }
2441
2833
 
2442
- private get table(): InvertTable {
2443
- return this.crossFieldTable as InvertTable;
2834
+ if (result.value?.nodeChange !== undefined) {
2835
+ setInChangeAtomIdMap(this.table.invertedNodeToParent, result.value.nodeChange, {
2836
+ field: this.fieldId,
2837
+ });
2838
+ }
2839
+ return result;
2444
2840
  }
2445
2841
  }
2446
2842
 
2447
- class RebaseManager extends CrossFieldManagerI<FieldChange> {
2843
+ class RebaseNodeManagerI implements RebaseNodeManager {
2448
2844
  public constructor(
2449
- table: RebaseTable,
2450
- currentField: FieldChange,
2845
+ private readonly table: RebaseTable,
2451
2846
  private readonly fieldId: FieldId,
2452
- allowInval = true,
2453
- ) {
2454
- super(table, currentField, allowInval);
2455
- }
2847
+ private readonly allowInval: boolean = true,
2848
+ ) {}
2456
2849
 
2457
- public override set(
2458
- target: CrossFieldTarget,
2459
- revision: RevisionTag | undefined,
2460
- id: ChangesetLocalId,
2850
+ public getNewChangesForBaseAttach(
2851
+ baseAttachId: ChangeAtomId,
2461
2852
  count: number,
2462
- newValue: unknown,
2463
- invalidateDependents: boolean,
2464
- ): void {
2465
- if (invalidateDependents && this.allowInval) {
2466
- const newFieldIds = getFieldsForCrossFieldKey(
2467
- this.table.newChange,
2468
- {
2469
- target,
2470
- revision,
2471
- localId: id,
2472
- },
2473
- count,
2474
- );
2853
+ ): RangeQueryResult<RebaseDetachedNodeEntry | undefined> {
2854
+ let countToProcess = count;
2475
2855
 
2476
- assert(
2477
- newFieldIds.length === 0,
2478
- 0x9c6 /* TODO: Modifying a cross-field key from the new changeset is currently unsupported */,
2479
- );
2856
+ const detachEntry = firstDetachIdFromAttachId(
2857
+ this.table.baseChange.rootNodes,
2858
+ baseAttachId,
2859
+ countToProcess,
2860
+ );
2480
2861
 
2481
- const baseFieldIds = getFieldsForCrossFieldKey(
2482
- this.table.baseChange,
2483
- {
2484
- target,
2485
- revision,
2486
- localId: id,
2487
- },
2488
- count,
2489
- );
2862
+ countToProcess = detachEntry.length;
2490
2863
 
2491
- assert(
2492
- baseFieldIds.length > 0,
2493
- 0x9c7 /* Cross field key not registered in base or new change */,
2864
+ const nodeEntry = rangeQueryChangeAtomIdMap(
2865
+ this.table.newChange.rootNodes.nodeChanges,
2866
+ detachEntry.value,
2867
+ countToProcess,
2868
+ );
2869
+
2870
+ countToProcess = nodeEntry.length;
2871
+ const newNodeId = nodeEntry.value;
2872
+
2873
+ const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(
2874
+ detachEntry.value,
2875
+ countToProcess,
2876
+ );
2877
+
2878
+ countToProcess = newRenameEntry.length;
2879
+
2880
+ let result: RangeQueryResult<DetachedNodeEntry | undefined>;
2881
+ // eslint-disable-next-line unicorn/prefer-ternary
2882
+ if (newNodeId !== undefined || newRenameEntry.value !== undefined) {
2883
+ result = {
2884
+ ...newRenameEntry,
2885
+ value: { detachId: newRenameEntry.value, nodeChange: newNodeId },
2886
+ };
2887
+ } else {
2888
+ // This handles the case where the base changeset has moved these nodes,
2889
+ // meaning they were attached in the input context of the base changeset.
2890
+ result = this.table.entries.getFirst(baseAttachId, countToProcess);
2891
+ }
2892
+
2893
+ // TODO: Consider moving these two checks into a separate method so that this function has no side effects.
2894
+ if (result.value?.detachId !== undefined) {
2895
+ this.table.rebasedDetachLocations.set(
2896
+ result.value.detachId,
2897
+ result.length,
2898
+ this.fieldId,
2494
2899
  );
2900
+ }
2495
2901
 
2496
- for (const baseFieldId of baseFieldIds) {
2497
- this.table.affectedBaseFields.set(
2498
- [baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field],
2499
- true,
2902
+ if (result.value?.nodeChange !== undefined) {
2903
+ setInChangeAtomIdMap(this.table.rebasedNodeToParent, result.value.nodeChange, {
2904
+ field: this.fieldId,
2905
+ });
2906
+ }
2907
+
2908
+ return result;
2909
+ }
2910
+
2911
+ public rebaseOverDetach(
2912
+ baseDetachId: ChangeAtomId,
2913
+ count: number,
2914
+ newDetachId: ChangeAtomId | undefined,
2915
+ nodeChange: NodeId | undefined,
2916
+ cellRename?: ChangeAtomId,
2917
+ ): void {
2918
+ let countToProcess = count;
2919
+ const attachIdEntry = firstAttachIdFromDetachId(
2920
+ this.table.baseRoots,
2921
+ baseDetachId,
2922
+ countToProcess,
2923
+ );
2924
+ const baseAttachId = attachIdEntry.value;
2925
+ countToProcess = attachIdEntry.length;
2926
+
2927
+ const attachFieldEntry = getFirstFieldForCrossFieldKey(
2928
+ this.table.baseChange,
2929
+ { ...baseAttachId, target: CrossFieldTarget.Destination },
2930
+ countToProcess,
2931
+ );
2932
+ countToProcess = attachFieldEntry.length;
2933
+
2934
+ const detachedMoveEntry = this.table.baseChange.rootNodes.outputDetachLocations.getFirst(
2935
+ baseDetachId,
2936
+ countToProcess,
2937
+ );
2938
+ countToProcess = detachedMoveEntry.length;
2939
+
2940
+ const destinationField = attachFieldEntry.value ?? detachedMoveEntry.value;
2941
+ if (destinationField !== undefined) {
2942
+ // The base detach is part of a move (or move of detach location) in the base changeset.
2943
+ setInCrossFieldMap(this.table.entries, baseAttachId, countToProcess, {
2944
+ nodeChange,
2945
+ detachId: newDetachId,
2946
+ cellRename,
2947
+ });
2948
+
2949
+ if (nodeChange !== undefined || newDetachId !== undefined) {
2950
+ this.invalidateBaseFields([destinationField]);
2951
+ }
2952
+ }
2953
+
2954
+ if (attachFieldEntry.value === undefined) {
2955
+ // These nodes are detached in the output context of the base changeset.
2956
+ if (nodeChange !== undefined) {
2957
+ assignRootChange(
2958
+ this.table.rebasedRootNodes,
2959
+ this.table.rebasedNodeToParent,
2960
+ baseAttachId,
2961
+ nodeChange,
2962
+ this.fieldId,
2963
+ this.table.rebaseVersion,
2500
2964
  );
2501
2965
  }
2966
+
2967
+ if (newDetachId !== undefined) {
2968
+ addNodeRename(
2969
+ this.table.rebasedRootNodes,
2970
+ baseAttachId,
2971
+ newDetachId,
2972
+ countToProcess,
2973
+ this.fieldId,
2974
+ );
2975
+ }
2976
+ }
2977
+
2978
+ if (newDetachId !== undefined) {
2979
+ this.table.movedDetaches.set(newDetachId, countToProcess, true);
2980
+ }
2981
+
2982
+ if (countToProcess < count) {
2983
+ const remainingCount = count - countToProcess;
2984
+
2985
+ const nextDetachId =
2986
+ newDetachId === undefined
2987
+ ? undefined
2988
+ : offsetChangeAtomId(newDetachId, countToProcess);
2989
+
2990
+ this.rebaseOverDetach(
2991
+ offsetChangeAtomId(baseDetachId, countToProcess),
2992
+ remainingCount,
2993
+ nextDetachId,
2994
+ nodeChange,
2995
+ );
2502
2996
  }
2997
+ }
2503
2998
 
2504
- super.set(target, revision, id, count, newValue, invalidateDependents);
2999
+ public addDetach(id: ChangeAtomId, count: number): void {
3000
+ this.table.rebasedDetachLocations.set(id, count, this.fieldId);
2505
3001
  }
2506
3002
 
2507
- public override onMoveIn(id: ChangeAtomId): void {
2508
- setInChangeAtomIdMap(this.table.rebasedNodeToParent, id, this.fieldId);
3003
+ public removeDetach(id: ChangeAtomId, count: number): void {
3004
+ this.table.movedDetaches.set(id, count, true);
2509
3005
  }
2510
3006
 
2511
- public override moveKey(
2512
- target: CrossFieldTarget,
2513
- revision: RevisionTag | undefined,
2514
- id: ChangesetLocalId,
3007
+ public doesBaseAttachNodes(
3008
+ id: ChangeAtomId,
2515
3009
  count: number,
2516
- ): void {
2517
- this.table.rebasedCrossFieldKeys.set(
2518
- { target, revision, localId: id },
2519
- count,
2520
- this.fieldId,
3010
+ ): RangeQueryEntry<ChangeAtomId, boolean> {
3011
+ let countToProcess = count;
3012
+ const attachEntry = getFirstAttachField(
3013
+ this.table.baseChange.crossFieldKeys,
3014
+ id,
3015
+ countToProcess,
3016
+ );
3017
+
3018
+ countToProcess = attachEntry.length;
3019
+ return { start: id, value: attachEntry.value !== undefined, length: countToProcess };
3020
+ }
3021
+
3022
+ public getBaseRename(
3023
+ id: ChangeAtomId,
3024
+ count: number,
3025
+ ): RangeQueryResult<ChangeAtomId | undefined> {
3026
+ return this.table.baseChange.rootNodes.oldToNewId.getFirst(id, count);
3027
+ }
3028
+
3029
+ public getNewRenameForBaseRename(
3030
+ baseRenameTo: ChangeAtomId,
3031
+ count: number,
3032
+ ): RangeQueryResult<ChangeAtomId | undefined> {
3033
+ let countToProcess = count;
3034
+ const inputEntry = firstDetachIdFromAttachId(
3035
+ this.table.baseChange.rootNodes,
3036
+ baseRenameTo,
3037
+ countToProcess,
3038
+ );
3039
+
3040
+ const attachEntry = getFirstAttachField(
3041
+ this.table.baseChange.crossFieldKeys,
3042
+ baseRenameTo,
3043
+ countToProcess,
2521
3044
  );
3045
+
3046
+ countToProcess = attachEntry.length;
3047
+ if (attachEntry.value !== undefined) {
3048
+ // These nodes are attached in the output context of the base changeset.
3049
+ return { value: undefined, length: countToProcess };
3050
+ }
3051
+
3052
+ countToProcess = inputEntry.length;
3053
+ const inputId = inputEntry.value;
3054
+
3055
+ const moveEntry = this.table.entries.getFirst(inputId, countToProcess);
3056
+
3057
+ countToProcess = moveEntry.length;
3058
+ if (moveEntry.value !== undefined) {
3059
+ return { ...moveEntry, value: moveEntry.value.cellRename ?? moveEntry.value.detachId };
3060
+ }
3061
+
3062
+ return this.table.newChange.rootNodes.oldToNewId.getFirst(inputId, countToProcess);
3063
+ }
3064
+
3065
+ private invalidateBaseFields(fields: FieldId[]): void {
3066
+ if (this.allowInval) {
3067
+ for (const fieldId of fields) {
3068
+ this.table.affectedBaseFields.set(fieldIdKeyFromFieldId(fieldId), true);
3069
+ }
3070
+ }
2522
3071
  }
3072
+ }
3073
+
3074
+ function assignRootChange(
3075
+ table: RootNodeTable,
3076
+ nodeToParent: ChangeAtomIdBTree<NodeLocation> | undefined,
3077
+ detachId: ChangeAtomId,
3078
+ nodeId: NodeId,
3079
+ detachLocation: FieldId | undefined,
3080
+ rebaseVersion: RebaseVersion,
3081
+ ): void {
3082
+ assert(
3083
+ rebaseVersion >= 2 || detachLocation !== undefined,
3084
+ "All root changes need a detach location to support compatibility with older client versions",
3085
+ );
3086
+
3087
+ setInChangeAtomIdMap(table.nodeChanges, detachId, nodeId);
2523
3088
 
2524
- private get table(): RebaseTable {
2525
- return this.crossFieldTable as RebaseTable;
3089
+ if (nodeToParent !== undefined) {
3090
+ setInChangeAtomIdMap(nodeToParent, nodeId, { root: detachId });
2526
3091
  }
3092
+
3093
+ table.detachLocations.set(detachId, 1, detachLocation);
2527
3094
  }
2528
3095
 
2529
- // TODO: Deduplicate this with RebaseTable
2530
- class ComposeManager extends CrossFieldManagerI<FieldChange> {
3096
+ class ComposeNodeManagerI implements ComposeNodeManager {
2531
3097
  public constructor(
2532
- table: ComposeTable,
2533
- currentField: FieldChange,
3098
+ private readonly table: ComposeTable,
2534
3099
  private readonly fieldId: FieldId,
2535
- allowInval = true,
2536
- ) {
2537
- super(table, currentField, allowInval);
3100
+ private readonly allowInval: boolean = true,
3101
+ ) {}
3102
+
3103
+ public getNewChangesForBaseDetach(
3104
+ baseDetachId: ChangeAtomId,
3105
+ count: number,
3106
+ ): RangeQueryResult<DetachedNodeEntry | undefined> {
3107
+ let countToProcess = count;
3108
+
3109
+ const baseAttachEntry = getFirstFieldForCrossFieldKey(
3110
+ this.table.baseChange,
3111
+ { target: CrossFieldTarget.Destination, ...baseDetachId },
3112
+ countToProcess,
3113
+ );
3114
+
3115
+ countToProcess = baseAttachEntry.length;
3116
+
3117
+ let result: RangeQueryResult<DetachedNodeEntry | undefined>;
3118
+ if (baseAttachEntry.value === undefined) {
3119
+ // The detached nodes are still detached in the new change's input context.
3120
+ const rootEntry = rangeQueryChangeAtomIdMap(
3121
+ this.table.newChange.rootNodes.nodeChanges,
3122
+ baseDetachId,
3123
+ countToProcess,
3124
+ );
3125
+
3126
+ countToProcess = rootEntry.length;
3127
+
3128
+ const newRenameEntry = this.table.newChange.rootNodes.oldToNewId.getFirst(
3129
+ baseDetachId,
3130
+ countToProcess,
3131
+ );
3132
+
3133
+ countToProcess = newRenameEntry.length;
3134
+
3135
+ result = {
3136
+ value: { nodeChange: rootEntry.value, detachId: newRenameEntry.value },
3137
+ length: countToProcess,
3138
+ };
3139
+ } else {
3140
+ // The base detach was part of a move.
3141
+ // We check if we've previously seen a node change at the move destination.
3142
+ const entry = this.table.entries.getFirst(baseDetachId, countToProcess);
3143
+ result = { value: entry.value, length: entry.length };
3144
+ }
3145
+
3146
+ // TODO: Consider moving this to a separate method so that this method can be side-effect free.
3147
+ if (result.value?.nodeChange !== undefined) {
3148
+ setInChangeAtomIdMap(this.table.movedNodeToParent, result.value.nodeChange, {
3149
+ field: this.fieldId,
3150
+ });
3151
+ }
3152
+
3153
+ return result;
2538
3154
  }
2539
3155
 
2540
- public override set(
2541
- target: CrossFieldTarget,
2542
- revision: RevisionTag | undefined,
2543
- id: ChangesetLocalId,
3156
+ public composeAttachDetach(
3157
+ baseAttachId: ChangeAtomId,
3158
+ newDetachId: ChangeAtomId,
2544
3159
  count: number,
2545
- newValue: unknown,
2546
- invalidateDependents: boolean,
2547
3160
  ): void {
2548
- if (invalidateDependents && this.allowInval) {
2549
- const newFieldIds = getFieldsForCrossFieldKey(
2550
- this.table.newChange,
2551
- {
2552
- target,
2553
- revision,
2554
- localId: id,
2555
- },
2556
- count,
3161
+ let countToProcess = count;
3162
+
3163
+ const newAttachEntry = getFirstAttachField(
3164
+ this.table.newChange.crossFieldKeys,
3165
+ newDetachId,
3166
+ countToProcess,
3167
+ );
3168
+
3169
+ countToProcess = newAttachEntry.length;
3170
+
3171
+ // Both changes can have the same ID if they came from inverse changesets.
3172
+ // If the new detach is part of a move,
3173
+ // then both input changesets contain the attach cross-field key for this ID.
3174
+ // The new attach may still exist in the composed changeset so we do not remove it here.
3175
+ // The new attach will typically cancel with a base detach,
3176
+ // in which case the cross-field key will be removed in `composeDetachAttach`.
3177
+ const hasNewAttachWithBaseAttachId =
3178
+ areEqualChangeAtomIds(baseAttachId, newDetachId) && newAttachEntry.value !== undefined;
3179
+
3180
+ if (!hasNewAttachWithBaseAttachId) {
3181
+ this.table.removedCrossFieldKeys.set(
3182
+ { ...baseAttachId, target: CrossFieldTarget.Destination },
3183
+ countToProcess,
3184
+ true,
2557
3185
  );
3186
+ }
2558
3187
 
2559
- if (newFieldIds.length > 0) {
2560
- for (const newFieldId of newFieldIds) {
2561
- this.table.pendingCompositions.affectedNewFields.set(
2562
- [newFieldId.nodeId?.revision, newFieldId.nodeId?.localId, newFieldId.field],
2563
- true,
2564
- );
2565
- }
2566
- } else {
2567
- const baseFieldIds = getFieldsForCrossFieldKey(
2568
- this.table.baseChange,
2569
- {
2570
- target,
2571
- revision,
2572
- localId: id,
2573
- },
2574
- count,
3188
+ const baseDetachEntry = getFirstDetachField(
3189
+ this.table.baseChange.crossFieldKeys,
3190
+ baseAttachId,
3191
+ countToProcess,
3192
+ );
3193
+
3194
+ countToProcess = baseDetachEntry.length;
3195
+
3196
+ const baseRootIdEntry = firstDetachIdFromAttachId(
3197
+ this.table.baseChange.rootNodes,
3198
+ baseAttachId,
3199
+ countToProcess,
3200
+ );
3201
+ countToProcess = baseRootIdEntry.length;
3202
+
3203
+ const baseDetachId = baseRootIdEntry.value;
3204
+
3205
+ if (baseDetachEntry.value === undefined) {
3206
+ const baseDetachLocationEntry = this.table.baseChange.rootNodes.detachLocations.getFirst(
3207
+ baseDetachId,
3208
+ countToProcess,
3209
+ );
3210
+ countToProcess = baseDetachLocationEntry.length;
3211
+
3212
+ // These nodes were detached in the base change's input context,
3213
+ // so the net effect of the two changes is a rename.
3214
+ appendNodeRename(
3215
+ this.table.composedRootNodes,
3216
+ baseAttachId,
3217
+ newDetachId,
3218
+ baseDetachEntry.length,
3219
+ this.table.baseChange.rootNodes,
3220
+ baseDetachLocationEntry.value ?? this.fieldId,
3221
+ );
3222
+
3223
+ this.table.removedCrossFieldKeys.set(
3224
+ { ...newDetachId, target: CrossFieldTarget.Source },
3225
+ countToProcess,
3226
+ true,
3227
+ );
3228
+ } else {
3229
+ // The base change moves these nodes.
3230
+ const prevEntry =
3231
+ this.table.entries.getFirst(baseAttachId, baseDetachEntry.length).value ?? {};
3232
+
3233
+ this.table.entries.set(baseAttachId, baseDetachEntry.length, {
3234
+ ...prevEntry,
3235
+ detachId: newDetachId,
3236
+ });
3237
+
3238
+ // The new detach will replace the base detach, so we remove the key for the base detach, unless they have the same ID.
3239
+ if (!areEqualChangeAtomIds(baseAttachId, newDetachId)) {
3240
+ this.table.removedCrossFieldKeys.set(
3241
+ { ...baseAttachId, target: CrossFieldTarget.Source },
3242
+ countToProcess,
3243
+ true,
2575
3244
  );
3245
+ }
2576
3246
 
2577
- assert(
2578
- baseFieldIds.length > 0,
2579
- 0x9c8 /* Cross field key not registered in base or new change */,
3247
+ this.table.movedCrossFieldKeys.set(
3248
+ { ...newDetachId, target: CrossFieldTarget.Source },
3249
+ countToProcess,
3250
+ baseDetachEntry.value,
3251
+ );
3252
+
3253
+ this.invalidateBaseFields([baseDetachEntry.value]);
3254
+ }
3255
+
3256
+ if (newAttachEntry.value === undefined) {
3257
+ const newOutputDetachLocationEntry =
3258
+ this.table.newChange.rootNodes.outputDetachLocations.getFirst(
3259
+ newDetachId,
3260
+ countToProcess,
2580
3261
  );
2581
3262
 
2582
- for (const baseFieldId of baseFieldIds) {
2583
- this.table.pendingCompositions.affectedBaseFields.set(
2584
- [baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field],
2585
- true,
2586
- );
2587
- }
2588
- }
3263
+ countToProcess = newOutputDetachLocationEntry.length;
3264
+
3265
+ this.table.composedRootNodes.outputDetachLocations.set(
3266
+ newDetachId,
3267
+ countToProcess,
3268
+ newOutputDetachLocationEntry.value ?? this.fieldId,
3269
+ );
3270
+ }
3271
+
3272
+ if (countToProcess < count) {
3273
+ const remainingCount = count - countToProcess;
3274
+ this.composeAttachDetach(
3275
+ offsetChangeAtomId(baseAttachId, countToProcess),
3276
+ offsetChangeAtomId(newDetachId, countToProcess),
3277
+ remainingCount,
3278
+ );
2589
3279
  }
3280
+ }
3281
+
3282
+ public sendNewChangesToBaseSourceLocation(
3283
+ baseAttachId: ChangeAtomId,
3284
+ newChanges: NodeId,
3285
+ ): void {
3286
+ const { value: baseDetachId } = firstDetachIdFromAttachId(
3287
+ this.table.baseChange.rootNodes,
3288
+ baseAttachId,
3289
+ 1,
3290
+ );
3291
+
3292
+ const detachFields = getFieldsForCrossFieldKey(
3293
+ this.table.baseChange,
3294
+ {
3295
+ ...baseDetachId,
3296
+ target: CrossFieldTarget.Source,
3297
+ },
3298
+ 1,
3299
+ );
3300
+
3301
+ if (detachFields.length > 0) {
3302
+ // The base attach is part of a move in the base changeset.
3303
+ const prevEntry = this.table.entries.getFirst(baseDetachId, 1).value ?? {};
3304
+ this.table.entries.set(baseDetachId, 1, { ...prevEntry, nodeChange: newChanges });
2590
3305
 
2591
- super.set(target, revision, id, count, newValue, invalidateDependents);
3306
+ if (newChanges !== undefined) {
3307
+ this.invalidateBaseFields(detachFields);
3308
+ }
3309
+ } else {
3310
+ const baseNodeId = getFromChangeAtomIdMap(
3311
+ this.table.baseChange.rootNodes.nodeChanges,
3312
+ baseDetachId,
3313
+ );
3314
+
3315
+ if (baseNodeId === undefined) {
3316
+ assignRootChange(
3317
+ this.table.composedRootNodes,
3318
+ this.table.movedNodeToParent,
3319
+ baseDetachId,
3320
+ newChanges,
3321
+ this.fieldId,
3322
+ this.table.rebaseVersion,
3323
+ );
3324
+ } else {
3325
+ addNodesToCompose(this.table, baseNodeId, newChanges);
3326
+ }
3327
+ }
2592
3328
  }
2593
3329
 
2594
- public override onMoveIn(id: ChangeAtomId): void {
2595
- setInChangeAtomIdMap(this.table.composedNodeToParent, id, this.fieldId);
3330
+ private areSameNodes(
3331
+ baseDetachId: ChangeAtomId,
3332
+ newAttachId: ChangeAtomId,
3333
+ count: number,
3334
+ ): RangeQueryResult<boolean> {
3335
+ const renamedDetachEntry = firstAttachIdFromDetachId(
3336
+ this.table.composedRootNodes,
3337
+ baseDetachId,
3338
+ count,
3339
+ );
3340
+
3341
+ const isReattachOfSameNodes = areEqualChangeAtomIds(renamedDetachEntry.value, newAttachId);
3342
+ return { ...renamedDetachEntry, value: isReattachOfSameNodes };
2596
3343
  }
2597
3344
 
2598
- public override moveKey(
2599
- target: CrossFieldTarget,
2600
- revision: RevisionTag | undefined,
2601
- id: ChangesetLocalId,
3345
+ public composeDetachAttach(
3346
+ baseDetachId: ChangeAtomId,
3347
+ newAttachId: ChangeAtomId,
2602
3348
  count: number,
3349
+ composeToPin: boolean,
2603
3350
  ): void {
2604
- throw new Error("Moving cross-field keys during compose is currently unsupported");
2605
- }
2606
-
2607
- private get table(): ComposeTable {
2608
- return this.crossFieldTable as ComposeTable;
2609
- }
2610
- }
2611
-
2612
- function makeModularChangeset(
2613
- props: {
2614
- fieldChanges?: FieldChangeMap;
2615
- nodeChanges?: ChangeAtomIdBTree<NodeChangeset>;
2616
- nodeToParent?: ChangeAtomIdBTree<FieldId>;
2617
- nodeAliases?: ChangeAtomIdBTree<NodeId>;
2618
- crossFieldKeys?: CrossFieldKeyTable;
2619
- maxId: number;
2620
- revisions?: readonly RevisionInfo[];
2621
- constraintViolationCount?: number;
2622
- constraintViolationCountOnRevert?: number;
2623
- builds?: ChangeAtomIdBTree<TreeChunk>;
2624
- destroys?: ChangeAtomIdBTree<number>;
2625
- refreshers?: ChangeAtomIdBTree<TreeChunk>;
2626
- } = {
2627
- maxId: -1,
2628
- },
2629
- ): ModularChangeset {
3351
+ const areSameEntry = this.areSameNodes(baseDetachId, newAttachId, count);
3352
+
3353
+ const countToProcess = areSameEntry.length;
3354
+ if (areSameEntry.value) {
3355
+ // These nodes have been moved back to their original location, so the composed changeset should not have any renames for them.
3356
+ // Note that deleting the rename from `this.table.composedRootNodes` would change the result of this method
3357
+ // if it were rerun due to the field being invalidated, so we instead record that the rename should be deleted later.
3358
+ this.table.renamesToDelete.set(baseDetachId, countToProcess, true);
3359
+ }
3360
+
3361
+ if (composeToPin) {
3362
+ this.table.movedCrossFieldKeys.set(
3363
+ { target: CrossFieldTarget.Source, ...newAttachId },
3364
+ countToProcess,
3365
+ this.fieldId,
3366
+ );
3367
+
3368
+ if (!areEqualChangeAtomIds(baseDetachId, newAttachId)) {
3369
+ // The pin will have `newAttachId` as both its detach and attach ID.
3370
+ // So we remove `baseDetachId` unless that is equal to the pin's detach ID.
3371
+ this.table.removedCrossFieldKeys.set(
3372
+ { target: CrossFieldTarget.Source, ...baseDetachId },
3373
+ countToProcess,
3374
+ true,
3375
+ );
3376
+ }
3377
+
3378
+ // Note that while change2 should already have this key, change1 may have a rollback for the same ID in a different location.
3379
+ // In that case, change1's attach should be canceled out by a detach from change2.
3380
+ // Here we make sure that the composed change has the correct location (this field) for the attach ID.
3381
+ this.table.movedCrossFieldKeys.set(
3382
+ { target: CrossFieldTarget.Destination, ...newAttachId },
3383
+ countToProcess,
3384
+ this.fieldId,
3385
+ );
3386
+ } else {
3387
+ this.table.removedCrossFieldKeys.set(
3388
+ { target: CrossFieldTarget.Source, ...baseDetachId },
3389
+ countToProcess,
3390
+ true,
3391
+ );
3392
+
3393
+ this.table.removedCrossFieldKeys.set(
3394
+ { target: CrossFieldTarget.Destination, ...newAttachId },
3395
+ countToProcess,
3396
+ true,
3397
+ );
3398
+ }
3399
+
3400
+ if (countToProcess < count) {
3401
+ this.composeAttachDetach(
3402
+ offsetChangeAtomId(baseDetachId, countToProcess),
3403
+ offsetChangeAtomId(newAttachId, countToProcess),
3404
+ count - countToProcess,
3405
+ );
3406
+ }
3407
+ }
3408
+
3409
+ private invalidateBaseFields(fields: FieldId[]): void {
3410
+ if (this.allowInval) {
3411
+ for (const fieldId of fields) {
3412
+ this.table.pendingCompositions.affectedBaseFields.set(
3413
+ fieldIdKeyFromFieldId(fieldId),
3414
+ true,
3415
+ );
3416
+ }
3417
+ }
3418
+ }
3419
+ }
3420
+
3421
+ function makeModularChangeset(props?: {
3422
+ rebaseVersion?: RebaseVersion;
3423
+ fieldChanges?: FieldChangeMap;
3424
+ nodeChanges?: ChangeAtomIdBTree<NodeChangeset>;
3425
+ rootNodes?: RootNodeTable;
3426
+ nodeToParent?: ChangeAtomIdBTree<NodeLocation>;
3427
+ nodeAliases?: ChangeAtomIdBTree<NodeId>;
3428
+ crossFieldKeys?: CrossFieldKeyTable;
3429
+ maxId: number;
3430
+ revisions?: readonly RevisionInfo[];
3431
+ constraintViolationCount?: number;
3432
+ constraintViolationCountOnRevert?: number;
3433
+ noChangeConstraint?: NoChangeConstraint;
3434
+ noChangeConstraintOnRevert?: NoChangeConstraint;
3435
+ builds?: ChangeAtomIdBTree<TreeChunk>;
3436
+ destroys?: ChangeAtomIdBTree<number>;
3437
+ refreshers?: ChangeAtomIdBTree<TreeChunk>;
3438
+ }): ModularChangeset {
3439
+ const p = props ?? { maxId: -1 };
2630
3440
  const changeset: Mutable<ModularChangeset> = {
2631
- fieldChanges: props.fieldChanges ?? new Map(),
2632
- nodeChanges: props.nodeChanges ?? newTupleBTree(),
2633
- nodeToParent: props.nodeToParent ?? newTupleBTree(),
2634
- nodeAliases: props.nodeAliases ?? newTupleBTree(),
2635
- crossFieldKeys: props.crossFieldKeys ?? newCrossFieldKeyTable(),
3441
+ rebaseVersion: p.rebaseVersion ?? 1,
3442
+ fieldChanges: p.fieldChanges ?? new Map(),
3443
+ nodeChanges: p.nodeChanges ?? newTupleBTree(),
3444
+ rootNodes: p.rootNodes ?? newRootTable(),
3445
+ nodeToParent: p.nodeToParent ?? newTupleBTree(),
3446
+ nodeAliases: p.nodeAliases ?? newTupleBTree(),
3447
+ crossFieldKeys: p.crossFieldKeys ?? newCrossFieldRangeTable(),
2636
3448
  };
2637
3449
 
2638
- if (props.revisions !== undefined && props.revisions.length > 0) {
2639
- changeset.revisions = props.revisions;
3450
+ if (p.revisions !== undefined && p.revisions.length > 0) {
3451
+ changeset.revisions = p.revisions;
2640
3452
  }
2641
- if (props.maxId >= 0) {
2642
- changeset.maxId = brand(props.maxId);
3453
+ if (p.maxId >= 0) {
3454
+ changeset.maxId = brand(p.maxId);
2643
3455
  }
2644
- if (props.constraintViolationCount !== undefined && props.constraintViolationCount > 0) {
2645
- changeset.constraintViolationCount = props.constraintViolationCount;
3456
+ if (p.constraintViolationCount !== undefined && p.constraintViolationCount > 0) {
3457
+ changeset.constraintViolationCount = p.constraintViolationCount;
2646
3458
  }
2647
3459
  if (
2648
- props.constraintViolationCountOnRevert !== undefined &&
2649
- props.constraintViolationCountOnRevert > 0
3460
+ p.constraintViolationCountOnRevert !== undefined &&
3461
+ p.constraintViolationCountOnRevert > 0
2650
3462
  ) {
2651
- changeset.constraintViolationCountOnRevert = props.constraintViolationCountOnRevert;
3463
+ changeset.constraintViolationCountOnRevert = p.constraintViolationCountOnRevert;
3464
+ }
3465
+ if (p.noChangeConstraint !== undefined) {
3466
+ changeset.noChangeConstraint = p.noChangeConstraint;
2652
3467
  }
2653
- if (props.builds !== undefined && props.builds.size > 0) {
2654
- changeset.builds = props.builds;
3468
+ if (p.noChangeConstraintOnRevert !== undefined) {
3469
+ changeset.noChangeConstraintOnRevert = p.noChangeConstraintOnRevert;
2655
3470
  }
2656
- if (props.destroys !== undefined && props.destroys.size > 0) {
2657
- changeset.destroys = props.destroys;
3471
+ if (p.builds !== undefined && p.builds.size > 0) {
3472
+ changeset.builds = p.builds;
2658
3473
  }
2659
- if (props.refreshers !== undefined && props.refreshers.size > 0) {
2660
- changeset.refreshers = props.refreshers;
3474
+ if (p.destroys !== undefined && p.destroys.size > 0) {
3475
+ changeset.destroys = p.destroys;
2661
3476
  }
3477
+ if (p.refreshers !== undefined && p.refreshers.size > 0) {
3478
+ changeset.refreshers = p.refreshers;
3479
+ }
3480
+
2662
3481
  return changeset;
2663
3482
  }
2664
3483
 
2665
3484
  export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2666
3485
  private transactionDepth: number = 0;
2667
3486
  private idAllocator: IdAllocator;
3487
+ private readonly codecOptions: CodecWriteOptions;
2668
3488
 
2669
3489
  public constructor(
2670
3490
  family: ChangeFamily<ChangeFamilyEditor, ModularChangeset>,
2671
3491
  private readonly fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
2672
3492
  changeReceiver: (change: TaggedChange<ModularChangeset>) => void,
3493
+ codecOptions: CodecWriteOptions,
2673
3494
  ) {
2674
3495
  super(family, changeReceiver);
2675
3496
  this.idAllocator = idAllocatorFromMaxId();
3497
+ this.codecOptions = codecOptions;
3498
+ }
3499
+
3500
+ public isInTransaction(): boolean {
3501
+ return this.transactionDepth > 0;
2676
3502
  }
2677
3503
 
2678
3504
  public override enterTransaction(): void {
@@ -2729,7 +3555,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2729
3555
  * @param revision - the revision of the change
2730
3556
  */
2731
3557
  public submitChange(
2732
- field: FieldUpPath,
3558
+ field: NormalizedFieldUpPath,
2733
3559
  fieldKind: FieldKindIdentifier,
2734
3560
  change: FieldChangeset,
2735
3561
  revision: RevisionTag,
@@ -2743,7 +3569,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2743
3569
  fieldChange: { fieldKind, change },
2744
3570
  nodeChanges: newTupleBTree(),
2745
3571
  nodeToParent: newTupleBTree(),
2746
- crossFieldKeys: newCrossFieldKeyTable(),
3572
+ crossFieldKeys: newCrossFieldRangeTable(),
3573
+ rootNodes: newRootTable(),
2747
3574
  idAllocator: this.idAllocator,
2748
3575
  localCrossFieldKeys,
2749
3576
  revision,
@@ -2765,6 +3592,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2765
3592
  ? makeModularChangeset({
2766
3593
  maxId: this.idAllocator.getMaxId(),
2767
3594
  builds: change.builds,
3595
+ rootNodes: renameTableFromRenameDescriptions(change.renames ?? []),
2768
3596
  revisions: [{ revision: change.revision }],
2769
3597
  })
2770
3598
  : buildModularChangesetFromField({
@@ -2775,7 +3603,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2775
3603
  },
2776
3604
  nodeChanges: newTupleBTree(),
2777
3605
  nodeToParent: newTupleBTree(),
2778
- crossFieldKeys: newCrossFieldKeyTable(),
3606
+ crossFieldKeys: newCrossFieldRangeTable(),
3607
+ rootNodes: newRootTable(),
2779
3608
  idAllocator: this.idAllocator,
2780
3609
  localCrossFieldKeys: getChangeHandler(
2781
3610
  this.fieldKinds,
@@ -2785,7 +3614,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2785
3614
  }),
2786
3615
  );
2787
3616
  });
2788
- const revInfo = Array.from(revisions).map((revision) => ({ revision }));
3617
+ const revInfo = [...revisions].map((revision) => ({ revision }));
2789
3618
  const composedChange: Mutable<ModularChangeset> = {
2790
3619
  ...this.changeFamily.rebaser.compose(changeMaps),
2791
3620
  revisions: revInfo,
@@ -2802,7 +3631,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2802
3631
  return brand(this.idAllocator.allocate(count));
2803
3632
  }
2804
3633
 
2805
- public addNodeExistsConstraint(path: UpPath, revision: RevisionTag): void {
3634
+ public addNodeExistsConstraint(path: NormalizedUpPath, revision: RevisionTag): void {
2806
3635
  const nodeChange: NodeChangeset = {
2807
3636
  nodeExistsConstraint: { violated: false },
2808
3637
  };
@@ -2814,7 +3643,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2814
3643
  nodeChange,
2815
3644
  nodeChanges: newTupleBTree(),
2816
3645
  nodeToParent: newTupleBTree(),
2817
- crossFieldKeys: newCrossFieldKeyTable(),
3646
+ crossFieldKeys: newCrossFieldRangeTable(),
3647
+ rootNodes: newRootTable(),
2818
3648
  idAllocator: this.idAllocator,
2819
3649
  revision,
2820
3650
  }),
@@ -2823,7 +3653,7 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2823
3653
  );
2824
3654
  }
2825
3655
 
2826
- public addNodeExistsConstraintOnRevert(path: UpPath, revision: RevisionTag): void {
3656
+ public addNodeExistsConstraintOnRevert(path: NormalizedUpPath, revision: RevisionTag): void {
2827
3657
  const nodeChange: NodeChangeset = {
2828
3658
  nodeExistsConstraintOnRevert: { violated: false },
2829
3659
  };
@@ -2835,7 +3665,8 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2835
3665
  nodeChange,
2836
3666
  nodeChanges: newTupleBTree(),
2837
3667
  nodeToParent: newTupleBTree(),
2838
- crossFieldKeys: newCrossFieldKeyTable(),
3668
+ crossFieldKeys: newCrossFieldRangeTable(),
3669
+ rootNodes: newRootTable(),
2839
3670
  idAllocator: this.idAllocator,
2840
3671
  revision,
2841
3672
  }),
@@ -2843,14 +3674,45 @@ export class ModularEditBuilder extends EditBuilder<ModularChangeset> {
2843
3674
  ),
2844
3675
  );
2845
3676
  }
3677
+
3678
+ public addNoChangeConstraint(revision: RevisionTag): void {
3679
+ if (lt(this.codecOptions.minVersionForCollab, FluidClientVersion.v2_80)) {
3680
+ throw new UsageError(
3681
+ `No change constraints require min client version of at least ${FluidClientVersion.v2_80}`,
3682
+ );
3683
+ }
3684
+
3685
+ const changeset = makeModularChangeset({
3686
+ maxId: -1,
3687
+ noChangeConstraint: { violated: false },
3688
+ });
3689
+
3690
+ this.applyChange(tagChange(changeset, revision));
3691
+ }
3692
+
3693
+ public addNoChangeConstraintOnRevert(revision: RevisionTag): void {
3694
+ if (lt(this.codecOptions.minVersionForCollab, FluidClientVersion.v2_80)) {
3695
+ throw new UsageError(
3696
+ `No change constraints require min client version of at least ${FluidClientVersion.v2_80}`,
3697
+ );
3698
+ }
3699
+
3700
+ const changeset = makeModularChangeset({
3701
+ maxId: -1,
3702
+ noChangeConstraintOnRevert: { violated: false },
3703
+ });
3704
+
3705
+ this.applyChange(tagChange(changeset, revision));
3706
+ }
2846
3707
  }
2847
3708
 
2848
- function buildModularChangesetFromField(props: {
2849
- path: FieldUpPath;
3709
+ export function buildModularChangesetFromField(props: {
3710
+ path: NormalizedFieldUpPath;
2850
3711
  fieldChange: FieldChange;
2851
3712
  nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
2852
- nodeToParent: ChangeAtomIdBTree<FieldId>;
3713
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>;
2853
3714
  crossFieldKeys: CrossFieldKeyTable;
3715
+ rootNodes: RootNodeTable;
2854
3716
  localCrossFieldKeys?: CrossFieldKeyRange[];
2855
3717
  revision: RevisionTag;
2856
3718
  idAllocator?: IdAllocator;
@@ -2862,6 +3724,7 @@ function buildModularChangesetFromField(props: {
2862
3724
  nodeChanges,
2863
3725
  nodeToParent,
2864
3726
  crossFieldKeys,
3727
+ rootNodes,
2865
3728
  idAllocator = idAllocatorFromMaxId(),
2866
3729
  localCrossFieldKeys = [],
2867
3730
  childId,
@@ -2870,14 +3733,17 @@ function buildModularChangesetFromField(props: {
2870
3733
  const fieldChanges: FieldChangeMap = new Map([[path.field, fieldChange]]);
2871
3734
 
2872
3735
  if (path.parent === undefined) {
3736
+ const field = { nodeId: undefined, field: path.field };
2873
3737
  for (const { key, count } of localCrossFieldKeys) {
2874
- crossFieldKeys.set(key, count, { nodeId: undefined, field: path.field });
3738
+ crossFieldKeys.set(key, count, field);
2875
3739
  }
2876
3740
 
2877
3741
  if (childId !== undefined) {
2878
3742
  setInChangeAtomIdMap(nodeToParent, childId, {
2879
- nodeId: undefined,
2880
- field: path.field,
3743
+ field: {
3744
+ nodeId: undefined,
3745
+ field: path.field,
3746
+ },
2881
3747
  });
2882
3748
  }
2883
3749
 
@@ -2886,6 +3752,7 @@ function buildModularChangesetFromField(props: {
2886
3752
  nodeChanges,
2887
3753
  nodeToParent,
2888
3754
  crossFieldKeys,
3755
+ rootNodes,
2889
3756
  maxId: idAllocator.getMaxId(),
2890
3757
  revisions: [{ revision }],
2891
3758
  });
@@ -2896,6 +3763,7 @@ function buildModularChangesetFromField(props: {
2896
3763
  };
2897
3764
 
2898
3765
  const parentId: NodeId = { localId: brand(idAllocator.allocate()), revision };
3766
+ const fieldId = { nodeId: parentId, field: path.field };
2899
3767
 
2900
3768
  for (const { key, count } of localCrossFieldKeys) {
2901
3769
  crossFieldKeys.set(key, count, { nodeId: parentId, field: path.field });
@@ -2903,8 +3771,7 @@ function buildModularChangesetFromField(props: {
2903
3771
 
2904
3772
  if (childId !== undefined) {
2905
3773
  setInChangeAtomIdMap(nodeToParent, childId, {
2906
- nodeId: parentId,
2907
- field: path.field,
3774
+ field: fieldId,
2908
3775
  });
2909
3776
  }
2910
3777
 
@@ -2914,6 +3781,7 @@ function buildModularChangesetFromField(props: {
2914
3781
  nodeChanges,
2915
3782
  nodeToParent,
2916
3783
  crossFieldKeys,
3784
+ rootNodes,
2917
3785
  idAllocator,
2918
3786
  revision,
2919
3787
  nodeId: parentId,
@@ -2921,41 +3789,62 @@ function buildModularChangesetFromField(props: {
2921
3789
  }
2922
3790
 
2923
3791
  function buildModularChangesetFromNode(props: {
2924
- path: UpPath;
3792
+ path: NormalizedUpPath;
2925
3793
  nodeChange: NodeChangeset;
2926
3794
  nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
2927
- nodeToParent: ChangeAtomIdBTree<FieldId>;
3795
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>;
2928
3796
  crossFieldKeys: CrossFieldKeyTable;
3797
+ rootNodes: RootNodeTable;
2929
3798
  idAllocator: IdAllocator;
2930
3799
  revision: RevisionTag;
2931
3800
  nodeId?: NodeId;
2932
3801
  }): ModularChangeset {
2933
3802
  const {
2934
3803
  path,
2935
- nodeId = { localId: brand(props.idAllocator.allocate()), revision: props.revision },
3804
+ idAllocator,
3805
+ revision,
3806
+ nodeChanges,
3807
+ nodeChange,
3808
+ nodeId = { localId: brand(idAllocator.allocate()), revision },
2936
3809
  } = props;
2937
- setInChangeAtomIdMap(props.nodeChanges, nodeId, props.nodeChange);
2938
- const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
2939
- [path.parentIndex, nodeId],
2940
- ]);
2941
-
2942
- const fieldChange: FieldChange = {
2943
- fieldKind: genericFieldKind.identifier,
2944
- change: fieldChangeset,
2945
- };
3810
+ setInChangeAtomIdMap(nodeChanges, nodeId, nodeChange);
2946
3811
 
2947
- return buildModularChangesetFromField({
2948
- ...props,
2949
- path: { parent: path.parent, field: path.parentField },
2950
- fieldChange,
2951
- localCrossFieldKeys: [],
2952
- childId: nodeId,
2953
- });
3812
+ if (isDetachedUpPathRoot(path)) {
3813
+ props.rootNodes.nodeChanges.set(
3814
+ [path.detachedNodeId.major, brand(path.detachedNodeId.minor)],
3815
+ nodeId,
3816
+ );
3817
+ return makeModularChangeset({
3818
+ rootNodes: props.rootNodes,
3819
+ nodeChanges: props.nodeChanges,
3820
+ nodeToParent: props.nodeToParent,
3821
+ crossFieldKeys: props.crossFieldKeys,
3822
+ maxId: props.idAllocator.getMaxId(),
3823
+ revisions: [{ revision: props.revision }],
3824
+ });
3825
+ } else {
3826
+ const fieldChangeset = genericFieldKind.changeHandler.editor.buildChildChanges([
3827
+ [path.parentIndex, nodeId],
3828
+ ]);
3829
+
3830
+ const fieldChange: FieldChange = {
3831
+ fieldKind: genericFieldKind.identifier,
3832
+ change: fieldChangeset,
3833
+ };
3834
+
3835
+ return buildModularChangesetFromField({
3836
+ ...props,
3837
+ path: { parent: path.parent, field: path.parentField },
3838
+ fieldChange,
3839
+ localCrossFieldKeys: [],
3840
+ childId: nodeId,
3841
+ });
3842
+ }
2954
3843
  }
2955
3844
 
2956
3845
  export interface FieldEditDescription {
2957
3846
  type: "field";
2958
- field: FieldUpPath;
3847
+ field: NormalizedFieldUpPath;
2959
3848
  fieldKind: FieldKindIdentifier;
2960
3849
  change: FieldChangeset;
2961
3850
  revision: RevisionTag;
@@ -2965,6 +3854,23 @@ export interface GlobalEditDescription {
2965
3854
  type: "global";
2966
3855
  revision: RevisionTag;
2967
3856
  builds?: ChangeAtomIdBTree<TreeChunk>;
3857
+ renames?: RenameDescription[];
3858
+ }
3859
+
3860
+ export interface RenameDescription {
3861
+ count: number;
3862
+ oldId: ChangeAtomId;
3863
+ newId: ChangeAtomId;
3864
+ detachLocation: FieldId | undefined;
3865
+ }
3866
+
3867
+ function renameTableFromRenameDescriptions(renames: RenameDescription[]): RootNodeTable {
3868
+ const table = newRootTable();
3869
+ for (const rename of renames) {
3870
+ addNodeRename(table, rename.oldId, rename.newId, rename.count, rename.detachLocation);
3871
+ }
3872
+
3873
+ return table;
2968
3874
  }
2969
3875
 
2970
3876
  export type EditDescription = FieldEditDescription | GlobalEditDescription;
@@ -2975,25 +3881,16 @@ function getRevInfoFromTaggedChanges(changes: TaggedChange<ModularChangeset>[]):
2975
3881
  } {
2976
3882
  let maxId = -1;
2977
3883
  const revInfos: RevisionInfo[] = [];
3884
+ const revisions = new Set<RevisionTag>();
2978
3885
  for (const taggedChange of changes) {
2979
3886
  const change = taggedChange.change;
2980
3887
  maxId = Math.max(change.maxId ?? -1, maxId);
2981
- revInfos.push(...revisionInfoFromTaggedChange(taggedChange));
2982
- }
2983
-
2984
- const revisions = new Set<RevisionTag>();
2985
- const rolledBackRevisions: RevisionTag[] = [];
2986
- for (const info of revInfos) {
2987
- revisions.add(info.revision);
2988
- if (info.rollbackOf !== undefined) {
2989
- rolledBackRevisions.push(info.rollbackOf);
2990
- }
2991
- }
2992
-
2993
- rolledBackRevisions.reverse();
2994
- for (const revision of rolledBackRevisions) {
2995
- if (!revisions.has(revision)) {
2996
- revInfos.push({ revision });
3888
+ const infosToAdd = revisionInfoFromTaggedChange(taggedChange);
3889
+ for (const info of infosToAdd) {
3890
+ if (!revisions.has(info.revision)) {
3891
+ revisions.add(info.revision);
3892
+ revInfos.push(info);
3893
+ }
2997
3894
  }
2998
3895
  }
2999
3896
 
@@ -3016,25 +3913,28 @@ function revisionInfoFromTaggedChange(
3016
3913
  return revInfos;
3017
3914
  }
3018
3915
 
3019
- function fieldChangeFromId(
3020
- fields: FieldChangeMap,
3021
- nodes: ChangeAtomIdBTree<NodeChangeset>,
3022
- id: FieldId,
3023
- ): FieldChange {
3024
- const fieldMap = fieldMapFromNodeId(fields, nodes, id.nodeId);
3916
+ function fieldChangeFromId(change: ModularChangeset, id: FieldId): FieldChange {
3917
+ const fieldId = normalizeFieldId(id, change.nodeAliases);
3918
+ const fieldMap = fieldMapFromNodeId(
3919
+ change.fieldChanges,
3920
+ change.nodeChanges,
3921
+ change.nodeAliases,
3922
+ fieldId.nodeId,
3923
+ );
3025
3924
  return fieldMap.get(id.field) ?? fail(0xb25 /* No field exists for the given ID */);
3026
3925
  }
3027
3926
 
3028
3927
  function fieldMapFromNodeId(
3029
3928
  rootFieldMap: FieldChangeMap,
3030
3929
  nodes: ChangeAtomIdBTree<NodeChangeset>,
3930
+ aliases: ChangeAtomIdBTree<NodeId>,
3031
3931
  nodeId: NodeId | undefined,
3032
3932
  ): FieldChangeMap {
3033
3933
  if (nodeId === undefined) {
3034
3934
  return rootFieldMap;
3035
3935
  }
3036
3936
 
3037
- const node = nodeChangeFromId(nodes, nodeId);
3937
+ const node = nodeChangeFromId(nodes, aliases, nodeId);
3038
3938
  assert(node.fieldChanges !== undefined, 0x9c9 /* Expected node to have field changes */);
3039
3939
  return node.fieldChanges;
3040
3940
  }
@@ -3051,17 +3951,26 @@ function rebasedNodeIdFromBaseNodeId(table: RebaseTable, baseId: NodeId): NodeId
3051
3951
  return getFromChangeAtomIdMap(table.baseToRebasedNodeId, baseId) ?? baseId;
3052
3952
  }
3053
3953
 
3054
- function nodeChangeFromId(nodes: ChangeAtomIdBTree<NodeChangeset>, id: NodeId): NodeChangeset {
3055
- const node = getFromChangeAtomIdMap(nodes, id);
3954
+ function nodeChangeFromId(
3955
+ nodes: ChangeAtomIdBTree<NodeChangeset>,
3956
+ aliases: ChangeAtomIdBTree<NodeId>,
3957
+ id: NodeId,
3958
+ ): NodeChangeset {
3959
+ const normalizedId = normalizeNodeId(id, aliases);
3960
+ const node = getFromChangeAtomIdMap(nodes, normalizedId);
3056
3961
  assert(node !== undefined, 0x9ca /* Unknown node ID */);
3057
3962
  return node;
3058
3963
  }
3059
3964
 
3060
3965
  function fieldIdFromFieldIdKey([revision, localId, field]: FieldIdKey): FieldId {
3061
- const nodeId = localId !== undefined ? { revision, localId } : undefined;
3966
+ const nodeId = localId === undefined ? undefined : { revision, localId };
3062
3967
  return { nodeId, field };
3063
3968
  }
3064
3969
 
3970
+ function fieldIdKeyFromFieldId(fieldId: FieldId): FieldIdKey {
3971
+ return [fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field];
3972
+ }
3973
+
3065
3974
  function cloneNodeChangeset(nodeChangeset: NodeChangeset): NodeChangeset {
3066
3975
  if (nodeChangeset.fieldChanges !== undefined) {
3067
3976
  return { ...nodeChangeset, fieldChanges: new Map(nodeChangeset.fieldChanges) };
@@ -3070,28 +3979,39 @@ function cloneNodeChangeset(nodeChangeset: NodeChangeset): NodeChangeset {
3070
3979
  return { ...nodeChangeset };
3071
3980
  }
3072
3981
 
3073
- function replaceFieldIdRevision(
3074
- fieldId: FieldId,
3075
- oldRevisions: Set<RevisionTag | undefined>,
3076
- newRevision: RevisionTag | undefined,
3077
- ): FieldId {
3982
+ function replaceNodeLocationRevision(
3983
+ location: NodeLocation,
3984
+ replacer: RevisionReplacer,
3985
+ ): NodeLocation {
3986
+ return location.field === undefined
3987
+ ? { root: replacer.getUpdatedAtomId(location.root) }
3988
+ : { field: replaceFieldIdRevision(location.field, replacer) };
3989
+ }
3990
+
3991
+ function replaceFieldIdRevision(fieldId: FieldId, replacer: RevisionReplacer): FieldId {
3078
3992
  if (fieldId.nodeId === undefined) {
3079
3993
  return fieldId;
3080
3994
  }
3081
3995
 
3082
3996
  return {
3083
3997
  ...fieldId,
3084
- nodeId: replaceAtomRevisions(fieldId.nodeId, oldRevisions, newRevision),
3998
+ nodeId: replacer.getUpdatedAtomId(fieldId.nodeId),
3085
3999
  };
3086
4000
  }
3087
4001
 
3088
- export function getParentFieldId(changeset: ModularChangeset, nodeId: NodeId): FieldId {
3089
- const parentId = getFromChangeAtomIdMap(changeset.nodeToParent, nodeId);
3090
- assert(parentId !== undefined, 0x9cb /* Parent field should be defined */);
3091
- return normalizeFieldId(parentId, changeset.nodeAliases);
3092
- }
4002
+ export function getNodeParent(changeset: ModularChangeset, nodeId: NodeId): NodeLocation {
4003
+ const normalizedNodeId = normalizeNodeId(nodeId, changeset.nodeAliases);
4004
+ const location = getFromChangeAtomIdMap(changeset.nodeToParent, normalizedNodeId);
4005
+ assert(location !== undefined, 0x9cb /* Parent field should be defined */);
3093
4006
 
3094
- function getFieldsForCrossFieldKey(
4007
+ if (location.field !== undefined) {
4008
+ return { field: normalizeFieldId(location.field, changeset.nodeAliases) };
4009
+ }
4010
+
4011
+ return location;
4012
+ }
4013
+
4014
+ function getFieldsForCrossFieldKey(
3095
4015
  changeset: ModularChangeset,
3096
4016
  key: CrossFieldKey,
3097
4017
  count: number,
@@ -3101,21 +4021,49 @@ function getFieldsForCrossFieldKey(
3101
4021
  .map(({ value: fieldId }) => normalizeFieldId(fieldId, changeset.nodeAliases));
3102
4022
  }
3103
4023
 
4024
+ function getFirstFieldForCrossFieldKey(
4025
+ changeset: ModularChangeset,
4026
+ key: CrossFieldKey,
4027
+ count: number,
4028
+ ): RangeQueryResult<FieldId | undefined> {
4029
+ const result = changeset.crossFieldKeys.getFirst(key, count);
4030
+ if (result.value === undefined) {
4031
+ return result;
4032
+ }
4033
+
4034
+ return { ...result, value: normalizeFieldId(result.value, changeset.nodeAliases) };
4035
+ }
4036
+
4037
+ function normalizeNodeLocation(
4038
+ location: NodeLocation,
4039
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
4040
+ ): NodeLocation {
4041
+ if (location.field !== undefined) {
4042
+ return { field: normalizeFieldId(location.field, nodeAliases) };
4043
+ }
4044
+
4045
+ return location;
4046
+ }
4047
+
3104
4048
  // This is only exported for use in test utilities.
3105
4049
  export function normalizeFieldId(
3106
4050
  fieldId: FieldId,
3107
4051
  nodeAliases: ChangeAtomIdBTree<NodeId>,
3108
4052
  ): FieldId {
3109
- return fieldId.nodeId !== undefined
3110
- ? { ...fieldId, nodeId: normalizeNodeId(fieldId.nodeId, nodeAliases) }
3111
- : fieldId;
4053
+ return fieldId.nodeId === undefined
4054
+ ? fieldId
4055
+ : { ...fieldId, nodeId: normalizeNodeId(fieldId.nodeId, nodeAliases) };
3112
4056
  }
3113
4057
 
3114
4058
  /**
3115
4059
  * @returns The canonical form of nodeId, according to nodeAliases
3116
4060
  */
3117
- function normalizeNodeId(nodeId: NodeId, nodeAliases: ChangeAtomIdBTree<NodeId>): NodeId {
4061
+ export function normalizeNodeId(
4062
+ nodeId: NodeId,
4063
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
4064
+ ): NodeId {
3118
4065
  let currentId = nodeId;
4066
+ let cycleProbeId: NodeId | undefined = nodeId;
3119
4067
 
3120
4068
  // eslint-disable-next-line no-constant-condition
3121
4069
  while (true) {
@@ -3125,6 +4073,16 @@ function normalizeNodeId(nodeId: NodeId, nodeAliases: ChangeAtomIdBTree<NodeId>)
3125
4073
  }
3126
4074
 
3127
4075
  currentId = dealiased;
4076
+
4077
+ if (cycleProbeId !== undefined) {
4078
+ cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
4079
+ }
4080
+
4081
+ if (cycleProbeId !== undefined) {
4082
+ cycleProbeId = getFromChangeAtomIdMap(nodeAliases, cycleProbeId);
4083
+ }
4084
+
4085
+ assert(!areEqualChangeAtomIdOpts(cycleProbeId, currentId), "Alias cycle detected");
3128
4086
  }
3129
4087
  }
3130
4088
 
@@ -3135,22 +4093,936 @@ function hasConflicts(change: ModularChangeset): boolean {
3135
4093
  interface ModularChangesetContent {
3136
4094
  fieldChanges: FieldChangeMap;
3137
4095
  nodeChanges: ChangeAtomIdBTree<NodeChangeset>;
3138
- nodeToParent: ChangeAtomIdBTree<FieldId>;
4096
+ nodeToParent: ChangeAtomIdBTree<NodeLocation>;
4097
+ rootNodes: RootNodeTable;
3139
4098
  nodeAliases: ChangeAtomIdBTree<NodeId>;
3140
4099
  crossFieldKeys: CrossFieldKeyTable;
3141
4100
  }
3142
4101
 
3143
- function getFromChangeAtomIdMap<T>(
3144
- map: ChangeAtomIdBTree<T>,
4102
+ function areEqualFieldIds(a: FieldId, b: FieldId): boolean {
4103
+ return areEqualChangeAtomIdOpts(a.nodeId, b.nodeId) && a.field === b.field;
4104
+ }
4105
+
4106
+ function firstAttachIdFromDetachId(
4107
+ roots: RootNodeTable,
4108
+ detachId: ChangeAtomId,
4109
+ count: number,
4110
+ ): RangeQueryResult<ChangeAtomId> {
4111
+ const result = roots.oldToNewId.getFirst(detachId, count);
4112
+ return { ...result, value: result.value ?? detachId };
4113
+ }
4114
+
4115
+ function firstDetachIdFromAttachId(
4116
+ roots: RootNodeTable,
4117
+ attachId: ChangeAtomId,
4118
+ count: number,
4119
+ ): RangeQueryEntry<ChangeAtomId, ChangeAtomId> {
4120
+ const result = roots.newToOldId.getFirst(attachId, count);
4121
+ return { ...result, start: attachId, value: result.value ?? attachId };
4122
+ }
4123
+
4124
+ function rebaseCrossFieldKeys(
4125
+ sourceTable: CrossFieldKeyTable,
4126
+ movedDetaches: ChangeAtomIdRangeMap<boolean>,
4127
+ newDetachLocations: ChangeAtomIdRangeMap<FieldId>,
4128
+ ): CrossFieldKeyTable {
4129
+ const rebasedTable = sourceTable.clone();
4130
+ for (const entry of movedDetaches.entries()) {
4131
+ rebasedTable.delete({ ...entry.start, target: CrossFieldTarget.Source }, entry.length);
4132
+ }
4133
+
4134
+ for (const entry of newDetachLocations.entries()) {
4135
+ rebasedTable.set(
4136
+ { ...entry.start, target: CrossFieldTarget.Source },
4137
+ entry.length,
4138
+ entry.value,
4139
+ );
4140
+ }
4141
+
4142
+ return rebasedTable;
4143
+ }
4144
+
4145
+ export function newRootTable(): RootNodeTable {
4146
+ return {
4147
+ newToOldId: newChangeAtomIdTransform(),
4148
+ oldToNewId: newChangeAtomIdTransform(),
4149
+ nodeChanges: newTupleBTree(),
4150
+ detachLocations: newChangeAtomIdRangeMap(),
4151
+ outputDetachLocations: newChangeAtomIdRangeMap(),
4152
+ };
4153
+ }
4154
+
4155
+ function rebaseRoots(
4156
+ change: ModularChangeset,
4157
+ base: ModularChangeset,
4158
+ affectedBaseFields: TupleBTree<FieldIdKey, boolean>,
4159
+ nodesToRebase: [newChangeset: NodeId, baseChangeset: NodeId][],
4160
+ rebasedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
4161
+ rebaseVersion: RebaseVersion,
4162
+ ): RootNodeTable {
4163
+ const rebasedRoots = newRootTable();
4164
+ for (const renameEntry of change.rootNodes.oldToNewId.entries()) {
4165
+ rebaseRename(change.rootNodes, rebasedRoots, renameEntry, base, affectedBaseFields);
4166
+ }
4167
+
4168
+ for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
4169
+ const changes = base.rootNodes.nodeChanges.get(detachIdKey);
4170
+ if (changes !== undefined) {
4171
+ nodesToRebase.push([nodeId, changes]);
4172
+ }
4173
+
4174
+ const detachId = makeChangeAtomId(detachIdKey[1], detachIdKey[0]);
4175
+ const attachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
4176
+ const baseAttachEntry = base.crossFieldKeys.getFirst(
4177
+ { target: CrossFieldTarget.Destination, ...attachId },
4178
+ 1,
4179
+ );
4180
+ if (baseAttachEntry.value === undefined) {
4181
+ const renamedDetachId = firstAttachIdFromDetachId(base.rootNodes, detachId, 1).value;
4182
+ const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(
4183
+ renamedDetachId,
4184
+ 1,
4185
+ ).value;
4186
+
4187
+ if (baseOutputDetachLocation !== undefined) {
4188
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
4189
+ }
4190
+
4191
+ const detachLocation =
4192
+ baseOutputDetachLocation ??
4193
+ change.rootNodes.detachLocations.getFirst(detachId, 1).value;
4194
+
4195
+ // Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
4196
+ // We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
4197
+ assignRootChange(
4198
+ rebasedRoots,
4199
+ rebasedNodeToParent,
4200
+ renamedDetachId,
4201
+ nodeId,
4202
+ detachLocation,
4203
+ rebaseVersion,
4204
+ );
4205
+ } else {
4206
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseAttachEntry.value), true);
4207
+ rebasedNodeToParent.delete(detachIdKey);
4208
+ }
4209
+ }
4210
+
4211
+ for (const entry of change.rootNodes.outputDetachLocations.entries()) {
4212
+ rebasedRoots.outputDetachLocations.set(entry.start, entry.length, entry.value);
4213
+ }
4214
+
4215
+ return rebasedRoots;
4216
+ }
4217
+
4218
+ function rebaseRename(
4219
+ newRoots: RootNodeTable,
4220
+ rebasedRoots: RootNodeTable,
4221
+ renameEntry: RangeQueryEntry<ChangeAtomId, ChangeAtomId>,
4222
+ base: ModularChangeset,
4223
+ affectedBaseFields: TupleBTree<FieldIdKey, boolean>,
4224
+ ): void {
4225
+ let count = renameEntry.length;
4226
+ const baseRenameEntry = firstAttachIdFromDetachId(base.rootNodes, renameEntry.start, count);
4227
+ count = baseRenameEntry.length;
4228
+
4229
+ const baseAttachEntry = base.crossFieldKeys.getFirst(
4230
+ {
4231
+ ...baseRenameEntry.value,
4232
+ target: CrossFieldTarget.Destination,
4233
+ },
4234
+ count,
4235
+ );
4236
+
4237
+ count = baseAttachEntry.length;
4238
+
4239
+ if (baseAttachEntry.value === undefined) {
4240
+ const baseOutputDetachLocation = base.rootNodes.outputDetachLocations.getFirst(
4241
+ baseRenameEntry.value,
4242
+ 1,
4243
+ ).value;
4244
+
4245
+ if (baseOutputDetachLocation !== undefined) {
4246
+ affectedBaseFields.set(fieldIdKeyFromFieldId(baseOutputDetachLocation), true);
4247
+ }
4248
+
4249
+ const detachEntry = newRoots.detachLocations.getFirst(renameEntry.start, count);
4250
+ count = detachEntry.length;
4251
+
4252
+ const detachLocation = baseOutputDetachLocation ?? detachEntry.value;
4253
+
4254
+ // Note that `baseOutputDetachLocation` may contain a node ID from the base changeset.
4255
+ // We will replace the detach location entry with the node ID from the rebased changeset in `fixupRebasedDetachLocations`
4256
+ addNodeRename(
4257
+ rebasedRoots,
4258
+ baseRenameEntry.value,
4259
+ renameEntry.value,
4260
+ count,
4261
+ detachLocation,
4262
+ );
4263
+ } else {
4264
+ // This rename represents an intention to detach these nodes.
4265
+ // The rebased change should have a detach in the field where the base change attaches the nodes,
4266
+ // so we need to ensure that field is processed.
4267
+ affectedBaseFields.set(
4268
+ fieldIdKeyFromFieldId(normalizeFieldId(baseAttachEntry.value, base.nodeAliases)),
4269
+ true,
4270
+ );
4271
+ }
4272
+
4273
+ const countRemaining = renameEntry.length - count;
4274
+ if (countRemaining > 0) {
4275
+ rebaseRename(
4276
+ newRoots,
4277
+ rebasedRoots,
4278
+ {
4279
+ start: offsetChangeAtomId(renameEntry.start, count),
4280
+ value: offsetChangeAtomId(renameEntry.value, count),
4281
+ length: countRemaining,
4282
+ },
4283
+ base,
4284
+ affectedBaseFields,
4285
+ );
4286
+ }
4287
+ }
4288
+
4289
+ /**
4290
+ * For each root detach location, replaces any node ID from the base changeset
4291
+ * with the corresponding ID in the new changeset.
4292
+ */
4293
+ function fixupRebasedDetachLocations(table: RebaseTable): void {
4294
+ for (const {
4295
+ start,
4296
+ length,
4297
+ value: detachLocation,
4298
+ } of table.rebasedRootNodes.detachLocations.entries()) {
4299
+ const normalizedDetachLocation = normalizeFieldId(
4300
+ detachLocation,
4301
+ table.baseChange.nodeAliases,
4302
+ );
4303
+
4304
+ if (normalizedDetachLocation.nodeId !== undefined) {
4305
+ const rebasedNodeId = getFromChangeAtomIdMap(
4306
+ table.baseToRebasedNodeId,
4307
+ normalizedDetachLocation.nodeId,
4308
+ );
4309
+
4310
+ if (rebasedNodeId !== undefined) {
4311
+ table.rebasedRootNodes.detachLocations.set(start, length, {
4312
+ ...normalizedDetachLocation,
4313
+ nodeId: rebasedNodeId,
4314
+ });
4315
+ }
4316
+ }
4317
+ }
4318
+ }
4319
+
4320
+ function addNodesToCompose(table: ComposeTable, id1: NodeId, id2: NodeId): void {
4321
+ const normalizedId1 = normalizeNodeId(id1, table.baseChange.nodeAliases);
4322
+ const normalizedId2 = normalizeNodeId(id2, table.newChange.nodeAliases);
4323
+ if (getFromChangeAtomIdMap(table.newToBaseNodeId, normalizedId2) === undefined) {
4324
+ setInChangeAtomIdMap(table.newToBaseNodeId, normalizedId2, normalizedId1);
4325
+ table.pendingCompositions.nodeIdsToCompose.push([normalizedId1, normalizedId2]);
4326
+ }
4327
+ }
4328
+
4329
+ function composeRevInfos(
4330
+ revisions1: readonly RevisionInfo[] | undefined,
4331
+ revisions2: readonly RevisionInfo[] | undefined,
4332
+ ): readonly RevisionInfo[] {
4333
+ if (
4334
+ revisions1?.length === 1 &&
4335
+ revisions2?.length === 1 &&
4336
+ revisions1[0]?.revision === revisions2[0]?.revision
4337
+ ) {
4338
+ // This is a special case where we are composing two changesets from the same transaction.
4339
+ // We return one of the input arrays to avoid duplicating revision entries.
4340
+ return revisions1;
4341
+ }
4342
+ const result: RevisionInfo[] = [...(revisions1 ?? []), ...(revisions2 ?? [])];
4343
+ return result;
4344
+ }
4345
+
4346
+ function composeCrossFieldKeyTables(
4347
+ table1: CrossFieldKeyTable,
4348
+ table2: CrossFieldKeyTable,
4349
+ movedCrossFieldKeys: CrossFieldKeyTable,
4350
+ removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
4351
+ ): CrossFieldKeyTable {
4352
+ const composedTable = RangeMap.union(table1, table2);
4353
+ for (const entry of movedCrossFieldKeys.entries()) {
4354
+ composedTable.set(entry.start, entry.length, entry.value);
4355
+ }
4356
+
4357
+ for (const entry of removedCrossFieldKeys.entries()) {
4358
+ composedTable.delete(entry.start, entry.length);
4359
+ }
4360
+
4361
+ return composedTable;
4362
+ }
4363
+
4364
+ function composeRootTables(
4365
+ change1: ModularChangeset,
4366
+ change2: ModularChangeset,
4367
+ composedNodeToParent: ChangeAtomIdBTree<NodeLocation>,
4368
+ movedCrossFieldKeys: CrossFieldKeyTable,
4369
+ removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
4370
+ pendingCompositions: PendingCompositions,
4371
+ ): RootNodeTable {
4372
+ const composedTable = cloneRootTable(change1.rootNodes);
4373
+
4374
+ for (const renameEntry of change2.rootNodes.oldToNewId.entries()) {
4375
+ composeRename(
4376
+ change1,
4377
+ change2,
4378
+ composedTable,
4379
+ renameEntry.start,
4380
+ renameEntry.value,
4381
+ renameEntry.length,
4382
+ movedCrossFieldKeys,
4383
+ removedCrossFieldKeys,
4384
+ pendingCompositions,
4385
+ );
4386
+ }
4387
+
4388
+ for (const [[revision2, id2], nodeId2] of change2.rootNodes.nodeChanges.entries()) {
4389
+ const detachId2 = { revision: revision2, localId: id2 };
4390
+ const detachId1 = firstDetachIdFromAttachId(change1.rootNodes, detachId2, 1).value;
4391
+ const nodeId1 = getFromChangeAtomIdMap(change1.rootNodes.nodeChanges, detachId1);
4392
+
4393
+ if (nodeId1 === undefined) {
4394
+ const fieldId = getFieldsForCrossFieldKey(
4395
+ change1,
4396
+ { ...detachId1, target: CrossFieldTarget.Source },
4397
+ 1,
4398
+ )[0];
4399
+
4400
+ if (fieldId === undefined) {
4401
+ assignRootChange(
4402
+ composedTable,
4403
+ composedNodeToParent,
4404
+ detachId1,
4405
+ nodeId2,
4406
+ change1.rootNodes.detachLocations.getFirst(detachId1, 1).value ??
4407
+ change2.rootNodes.detachLocations.getFirst(detachId2, 1).value,
4408
+ Math.max(change1.rebaseVersion, change2.rebaseVersion) as RebaseVersion,
4409
+ );
4410
+ } else {
4411
+ // In this case, this node is attached in the input context of change1,
4412
+ // and is represented in detachFieldId.
4413
+ pendingCompositions.affectedBaseFields.set(
4414
+ [fieldId.nodeId?.revision, fieldId.nodeId?.localId, fieldId.field],
4415
+ true,
4416
+ );
4417
+ }
4418
+ } else {
4419
+ pendingCompositions.nodeIdsToCompose.push([nodeId1, nodeId2]);
4420
+ }
4421
+ }
4422
+
4423
+ for (const outputDetachEntry of change1.rootNodes.outputDetachLocations.entries()) {
4424
+ composeOutputDetachLocation(
4425
+ outputDetachEntry.start,
4426
+ outputDetachEntry.length,
4427
+ outputDetachEntry.value,
4428
+ change2,
4429
+ composedTable,
4430
+ );
4431
+ }
4432
+
4433
+ for (const entry of change2.rootNodes.outputDetachLocations.entries()) {
4434
+ composedTable.outputDetachLocations.set(entry.start, entry.length, entry.value);
4435
+ }
4436
+
4437
+ return composedTable;
4438
+ }
4439
+
4440
+ function composeOutputDetachLocation(
4441
+ outputDetachId1: ChangeAtomId,
4442
+ count: number,
4443
+ detachLocation: FieldId,
4444
+ change2: ModularChangeset,
4445
+ composedTable: RootNodeTable,
4446
+ ): void {
4447
+ let countToProcess = count;
4448
+ const renameEntry = firstAttachIdFromDetachId(
4449
+ change2.rootNodes,
4450
+ outputDetachId1,
4451
+ countToProcess,
4452
+ );
4453
+ countToProcess = renameEntry.length;
4454
+
4455
+ const attachEntry = getFirstAttachField(
4456
+ change2.crossFieldKeys,
4457
+ renameEntry.value,
4458
+ countToProcess,
4459
+ );
4460
+ countToProcess = attachEntry.length;
4461
+
4462
+ composedTable.outputDetachLocations.delete(outputDetachId1, countToProcess);
4463
+
4464
+ if (attachEntry.value === undefined) {
4465
+ // We update the key for the detach location to the renamed ID of the root in the composed output context.
4466
+ composedTable.outputDetachLocations.set(renameEntry.value, countToProcess, detachLocation);
4467
+ } else {
4468
+ // These nodes are attached by `change2` and thus attached in the composed output context,
4469
+ // so there should be no output detach location.
4470
+ }
4471
+
4472
+ const countRemaining = count - countToProcess;
4473
+ if (countRemaining > 0) {
4474
+ composeOutputDetachLocation(
4475
+ offsetChangeAtomId(outputDetachId1, countToProcess),
4476
+ countRemaining,
4477
+ detachLocation,
4478
+ change2,
4479
+ composedTable,
4480
+ );
4481
+ }
4482
+ }
4483
+
4484
+ function composeRename(
4485
+ change1: ModularChangeset,
4486
+ change2: ModularChangeset,
4487
+ mergedTable: RootNodeTable,
4488
+ oldId: ChangeAtomId,
4489
+ newId: ChangeAtomId,
4490
+ count: number,
4491
+ movedCrossFieldKeys: CrossFieldKeyTable,
4492
+ removedCrossFieldKeys: CrossFieldRangeTable<boolean>,
4493
+ pendingCompositions: PendingCompositions,
4494
+ ): void {
4495
+ let countToProcess = count;
4496
+ const detachEntry = getFirstDetachField(change1.crossFieldKeys, oldId, countToProcess);
4497
+ countToProcess = detachEntry.length;
4498
+
4499
+ if (detachEntry.value === undefined) {
4500
+ // `change1` may also have a rename to `renameEntry.value`, in which case it must refer to a different node.
4501
+ // That node must have been attached by `change1` and detached by `change2`.
4502
+ // The final rename for that node will be created in `composeAttachDetach`.
4503
+ // We delete any such rename for now to avoid colliding with the rename currently being processed.
4504
+ deleteNodeRenameTo(mergedTable, newId, countToProcess);
4505
+
4506
+ // The nodes were detached before `change`, so we append this rename.
4507
+ appendNodeRename(
4508
+ mergedTable,
4509
+ oldId,
4510
+ newId,
4511
+ countToProcess,
4512
+ change1.rootNodes,
4513
+ change2.rootNodes.detachLocations.getFirst(oldId, countToProcess).value,
4514
+ );
4515
+ } else {
4516
+ // `change1` detached these nodes,
4517
+ // so we invalidate the detach location so that the detach's ID can be replaced with the new ID.
4518
+ pendingCompositions.affectedBaseFields.set(fieldIdKeyFromFieldId(detachEntry.value), true);
4519
+
4520
+ if (!areEqualChangeAtomIds(oldId, newId)) {
4521
+ // `change1`'s detach will be replaced by `change2`'s detach, so we update the cross-field keys.
4522
+ removedCrossFieldKeys.set(
4523
+ { ...oldId, target: CrossFieldTarget.Source },
4524
+ countToProcess,
4525
+ true,
4526
+ );
4527
+ }
4528
+
4529
+ movedCrossFieldKeys.set(
4530
+ { ...newId, target: CrossFieldTarget.Source },
4531
+ countToProcess,
4532
+ detachEntry.value,
4533
+ );
4534
+ }
4535
+
4536
+ if (countToProcess < count) {
4537
+ composeRename(
4538
+ change1,
4539
+ change2,
4540
+ mergedTable,
4541
+ offsetChangeAtomId(oldId, countToProcess),
4542
+ offsetChangeAtomId(newId, countToProcess),
4543
+ count - countToProcess,
4544
+ movedCrossFieldKeys,
4545
+ removedCrossFieldKeys,
4546
+ pendingCompositions,
4547
+ );
4548
+ }
4549
+ }
4550
+
4551
+ export function cloneRootTable(table: RootNodeTable): RootNodeTable {
4552
+ return {
4553
+ oldToNewId: table.oldToNewId.clone(),
4554
+ newToOldId: table.newToOldId.clone(),
4555
+ nodeChanges: brand(table.nodeChanges.clone()),
4556
+ detachLocations: table.detachLocations.clone(),
4557
+ outputDetachLocations: table.outputDetachLocations.clone(),
4558
+ };
4559
+ }
4560
+
4561
+ function invertRootTable(change: ModularChangeset, isRollback: boolean): RootNodeTable {
4562
+ const invertedRoots: RootNodeTable = newRootTable();
4563
+ for (const [[revision, localId], nodeId] of change.rootNodes.nodeChanges.entries()) {
4564
+ const detachId: ChangeAtomId = { revision, localId };
4565
+ const renamedId = firstAttachIdFromDetachId(change.rootNodes, detachId, 1).value;
4566
+
4567
+ // This checks whether `change` attaches this node.
4568
+ // 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.
4569
+ if (
4570
+ change.crossFieldKeys.getFirst({ ...renamedId, target: CrossFieldTarget.Destination }, 1)
4571
+ .value === undefined
4572
+ ) {
4573
+ assignRootChange(
4574
+ invertedRoots,
4575
+ undefined,
4576
+ renamedId,
4577
+ nodeId,
4578
+ change.rootNodes.detachLocations.getFirst(detachId, 1).value,
4579
+ change.rebaseVersion,
4580
+ );
4581
+ }
4582
+ }
4583
+
4584
+ if (isRollback) {
4585
+ // We only invert renames of nodes which are not attached or detached by this changeset.
4586
+ // When we invert an attach we will create a detach which incorporates the rename.
4587
+ for (const {
4588
+ start: oldId,
4589
+ value: newId,
4590
+ length,
4591
+ } of change.rootNodes.oldToNewId.entries()) {
4592
+ invertRename(change, invertedRoots, oldId, newId, length);
4593
+ }
4594
+ }
4595
+
4596
+ return invertedRoots;
4597
+ }
4598
+
4599
+ function invertRename(
4600
+ change: ModularChangeset,
4601
+ invertedRoots: RootNodeTable,
4602
+ oldId: ChangeAtomId,
4603
+ newId: ChangeAtomId,
4604
+ length: number,
4605
+ ): void {
4606
+ for (const detachEntry of doesChangeDetachNodes(change.crossFieldKeys, newId, length)) {
4607
+ assert(
4608
+ !detachEntry.value,
4609
+ "A changeset should not have a rename and detach for the same node.",
4610
+ );
4611
+ }
4612
+
4613
+ let countProcessed = length;
4614
+ const attachEntry = getFirstAttachField(change.crossFieldKeys, newId, countProcessed);
4615
+ countProcessed = attachEntry.length;
4616
+ if (attachEntry.value === undefined) {
4617
+ const outputDetachEntry = change.rootNodes.outputDetachLocations.getFirst(
4618
+ newId,
4619
+ countProcessed,
4620
+ );
4621
+ countProcessed = outputDetachEntry.length;
4622
+
4623
+ const inputDetachEntry = change.rootNodes.detachLocations.getFirst(oldId, countProcessed);
4624
+ countProcessed = inputDetachEntry.length;
4625
+
4626
+ addNodeRename(
4627
+ invertedRoots,
4628
+ newId,
4629
+ oldId,
4630
+ countProcessed,
4631
+ outputDetachEntry.value ?? inputDetachEntry.value,
4632
+ );
4633
+ }
4634
+
4635
+ if (countProcessed < length) {
4636
+ invertRename(
4637
+ change,
4638
+ invertedRoots,
4639
+ offsetChangeAtomId(oldId, countProcessed),
4640
+ offsetChangeAtomId(newId, countProcessed),
4641
+ length - countProcessed,
4642
+ );
4643
+ }
4644
+ }
4645
+
4646
+ function doesChangeAttachNodes(
4647
+ table: CrossFieldKeyTable,
3145
4648
  id: ChangeAtomId,
3146
- ): T | undefined {
3147
- return map.get([id.revision, id.localId]);
4649
+ count: number,
4650
+ ): RangeQueryResultFragment<boolean>[] {
4651
+ return table
4652
+ .getAll2({ ...id, target: CrossFieldTarget.Destination }, count)
4653
+ .map((entry) => ({ ...entry, value: entry.value !== undefined }));
3148
4654
  }
3149
4655
 
3150
- function setInChangeAtomIdMap<T>(map: ChangeAtomIdBTree<T>, id: ChangeAtomId, value: T): void {
3151
- map.set([id.revision, id.localId], value);
4656
+ function doesChangeDetachNodes(
4657
+ table: CrossFieldKeyTable,
4658
+ id: ChangeAtomId,
4659
+ count: number,
4660
+ ): RangeQueryResultFragment<boolean>[] {
4661
+ return table
4662
+ .getAll2({ ...id, target: CrossFieldTarget.Source }, count)
4663
+ .map((entry) => ({ ...entry, value: entry.value !== undefined }));
3152
4664
  }
3153
4665
 
3154
- function areEqualFieldIds(a: FieldId, b: FieldId): boolean {
3155
- return areEqualChangeAtomIdOpts(a.nodeId, b.nodeId) && a.field === b.field;
4666
+ export function getFirstDetachField(
4667
+ table: CrossFieldKeyTable,
4668
+ id: ChangeAtomId,
4669
+ count: number,
4670
+ ): RangeQueryResult<FieldId | undefined> {
4671
+ return table.getFirst({ target: CrossFieldTarget.Source, ...id }, count);
4672
+ }
4673
+
4674
+ export function getFirstAttachField(
4675
+ table: CrossFieldKeyTable,
4676
+ id: ChangeAtomId,
4677
+ count: number,
4678
+ ): RangeQueryResult<FieldId | undefined> {
4679
+ return table.getFirst({ target: CrossFieldTarget.Destination, ...id }, count);
4680
+ }
4681
+
4682
+ export function addNodeRename(
4683
+ table: RootNodeTable,
4684
+ oldId: ChangeAtomId,
4685
+ newId: ChangeAtomId,
4686
+ count: number,
4687
+ detachLocation: FieldId | undefined,
4688
+ ): void {
4689
+ if (areEqualChangeAtomIds(oldId, newId)) {
4690
+ return;
4691
+ }
4692
+
4693
+ for (const entry of table.oldToNewId.getAll2(oldId, count)) {
4694
+ assert(
4695
+ entry.value === undefined ||
4696
+ areEqualChangeAtomIds(entry.value, offsetChangeAtomId(newId, entry.offset)),
4697
+ "Rename collision detected",
4698
+ );
4699
+ }
4700
+
4701
+ for (const entry of table.newToOldId.getAll2(newId, count)) {
4702
+ assert(
4703
+ entry.value === undefined ||
4704
+ areEqualChangeAtomIds(entry.value, offsetChangeAtomId(oldId, entry.offset)),
4705
+ "Rename collision detected",
4706
+ );
4707
+ }
4708
+
4709
+ table.oldToNewId.set(oldId, count, newId);
4710
+ table.newToOldId.set(newId, count, oldId);
4711
+
4712
+ if (detachLocation !== undefined) {
4713
+ table.detachLocations.set(oldId, count, detachLocation);
4714
+ }
4715
+ }
4716
+
4717
+ /**
4718
+ * Deletes any renames from `id`.
4719
+ */
4720
+ function deleteNodeRenameFrom(roots: RootNodeTable, id: ChangeAtomId, count: number): void {
4721
+ for (const entry of roots.oldToNewId.getAll(id, count)) {
4722
+ deleteNodeRenameEntry(roots, entry.start, entry.value, entry.length);
4723
+ }
4724
+ }
4725
+
4726
+ /**
4727
+ * Deletes any renames to `id`.
4728
+ */
4729
+ function deleteNodeRenameTo(roots: RootNodeTable, id: ChangeAtomId, count: number): void {
4730
+ for (const entry of roots.newToOldId.getAll(id, count)) {
4731
+ deleteNodeRenameEntry(roots, entry.value, entry.start, entry.length);
4732
+ }
4733
+ }
4734
+
4735
+ function appendNodeRename(
4736
+ composedTable: RootNodeTable,
4737
+ oldId: ChangeAtomId,
4738
+ newId: ChangeAtomId,
4739
+ count: number,
4740
+ change1Table: RootNodeTable,
4741
+ detachLocation: FieldId | undefined,
4742
+ ): void {
4743
+ let countToProcess = count;
4744
+ const rename1Entry = change1Table.newToOldId.getFirst(oldId, countToProcess);
4745
+ countToProcess = rename1Entry.length;
4746
+
4747
+ if (rename1Entry.value !== undefined) {
4748
+ deleteNodeRenameFrom(composedTable, rename1Entry.value, countToProcess);
4749
+ }
4750
+
4751
+ addNodeRename(
4752
+ composedTable,
4753
+ rename1Entry.value ?? oldId,
4754
+ newId,
4755
+ countToProcess,
4756
+ detachLocation,
4757
+ );
4758
+
4759
+ tryRemoveDetachLocation(composedTable, newId, countToProcess);
4760
+
4761
+ if (countToProcess < count) {
4762
+ const countRemaining = count - countToProcess;
4763
+ appendNodeRename(
4764
+ composedTable,
4765
+ offsetChangeAtomId(oldId, countToProcess),
4766
+ offsetChangeAtomId(newId, countToProcess),
4767
+ countRemaining,
4768
+ change1Table,
4769
+ detachLocation,
4770
+ );
4771
+ }
4772
+ }
4773
+
4774
+ function tryRemoveDetachLocation(
4775
+ roots: RootNodeTable,
4776
+ rootId: ChangeAtomId,
4777
+ count: number,
4778
+ ): void {
4779
+ let countProcessed = count;
4780
+ const renameEntry = roots.oldToNewId.getFirst(rootId, countProcessed);
4781
+ countProcessed = renameEntry.length;
4782
+
4783
+ const outputDetachEntry = roots.outputDetachLocations.getFirst(rootId, countProcessed);
4784
+ countProcessed = outputDetachEntry.length;
4785
+
4786
+ const nodeChangeEntry = rangeQueryChangeAtomIdMap(roots.nodeChanges, rootId, countProcessed);
4787
+ countProcessed = nodeChangeEntry.length;
4788
+
4789
+ if (
4790
+ nodeChangeEntry.value === undefined &&
4791
+ renameEntry.value === undefined &&
4792
+ outputDetachEntry.value === undefined
4793
+ ) {
4794
+ roots.detachLocations.delete(rootId, countProcessed);
4795
+ }
4796
+
4797
+ const countRemaining = count - countProcessed;
4798
+ if (countRemaining > 0) {
4799
+ tryRemoveDetachLocation(roots, offsetChangeAtomId(rootId, countProcessed), countRemaining);
4800
+ }
4801
+ }
4802
+
4803
+ /**
4804
+ * Deletes the entry renaming the ID range of length `count` from `oldId` to `newId`.
4805
+ * This function assumes that such an entry exists.
4806
+ */
4807
+ function deleteNodeRenameEntry(
4808
+ roots: RootNodeTable,
4809
+ oldId: ChangeAtomId,
4810
+ newId: ChangeAtomId,
4811
+ count: number,
4812
+ ): void {
4813
+ roots.oldToNewId.delete(oldId, count);
4814
+ roots.newToOldId.delete(newId, count);
4815
+ }
4816
+
4817
+ function replaceRootTableRevision(
4818
+ table: RootNodeTable,
4819
+ replacer: RevisionReplacer,
4820
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
4821
+ ): RootNodeTable {
4822
+ const oldToNewId = table.oldToNewId.mapEntries(
4823
+ (id) => replacer.getUpdatedAtomId(id),
4824
+ (id) => replacer.getUpdatedAtomId(id),
4825
+ );
4826
+
4827
+ const newToOldId = table.newToOldId.mapEntries(
4828
+ (id) => replacer.getUpdatedAtomId(id),
4829
+ (id) => replacer.getUpdatedAtomId(id),
4830
+ );
4831
+
4832
+ const nodeChanges: ChangeAtomIdBTree<NodeId> = replaceIdMapRevisions(
4833
+ table.nodeChanges,
4834
+ replacer,
4835
+ (nodeId) => replacer.getUpdatedAtomId(normalizeNodeId(nodeId, nodeAliases)),
4836
+ );
4837
+
4838
+ const detachLocations = table.detachLocations.mapEntries(
4839
+ (id) => replacer.getUpdatedAtomId(id),
4840
+ (fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer),
4841
+ );
4842
+
4843
+ const outputDetachLocations = table.outputDetachLocations.mapEntries(
4844
+ (id) => replacer.getUpdatedAtomId(id),
4845
+ (fieldId) => replaceFieldIdRevision(normalizeFieldId(fieldId, nodeAliases), replacer),
4846
+ );
4847
+
4848
+ return { oldToNewId, newToOldId, nodeChanges, detachLocations, outputDetachLocations };
4849
+ }
4850
+
4851
+ function newDetachedEntryMap(): ChangeAtomIdRangeMap<DetachedNodeEntry> {
4852
+ return new RangeMap(offsetChangeAtomId, subtractChangeAtomIds, offsetDetachedNodeEntry);
4853
+ }
4854
+
4855
+ function offsetDetachedNodeEntry(entry: DetachedNodeEntry, count: number): DetachedNodeEntry {
4856
+ assert(
4857
+ count <= 1 || entry.nodeChange === undefined,
4858
+ "Cannot split an entry with a node change",
4859
+ );
4860
+
4861
+ return entry.detachId === undefined
4862
+ ? entry
4863
+ : { ...entry, detachId: offsetChangeAtomId(entry.detachId, count) };
4864
+ }
4865
+
4866
+ function getFieldsWithRootMoves(
4867
+ roots: RootNodeTable,
4868
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
4869
+ ): TupleBTree<FieldIdKey, boolean> {
4870
+ const fields: TupleBTree<FieldIdKey, boolean> = newTupleBTree();
4871
+ for (const { start: rootId, value: fieldId, length } of roots.detachLocations.entries()) {
4872
+ let isRootMoved = false;
4873
+ for (const renameEntry of roots.oldToNewId.getAll2(rootId, length)) {
4874
+ if (renameEntry.value !== undefined) {
4875
+ isRootMoved = true;
4876
+ }
4877
+ }
4878
+
4879
+ for (const outputDetachEntry of roots.outputDetachLocations.getAll2(rootId, length)) {
4880
+ if (outputDetachEntry.value !== undefined) {
4881
+ isRootMoved = true;
4882
+ }
4883
+ }
4884
+
4885
+ if (isRootMoved) {
4886
+ fields.set(fieldIdKeyFromFieldId(normalizeFieldId(fieldId, nodeAliases)), true);
4887
+ }
4888
+ }
4889
+
4890
+ return fields;
4891
+ }
4892
+
4893
+ function getFieldToRootChanges(
4894
+ roots: RootNodeTable,
4895
+ nodeAliases: ChangeAtomIdBTree<NodeId>,
4896
+ ): TupleBTree<FieldIdKey, ChangeAtomId[]> {
4897
+ const fields: TupleBTree<FieldIdKey, ChangeAtomId[]> = newTupleBTree();
4898
+ for (const rootIdKey of roots.nodeChanges.keys()) {
4899
+ const rootId: ChangeAtomId = { revision: rootIdKey[0], localId: rootIdKey[1] };
4900
+ const detachLocation = roots.detachLocations.getFirst(rootId, 1).value;
4901
+ if (detachLocation !== undefined) {
4902
+ const fieldIdKey = fieldIdKeyFromFieldId(normalizeFieldId(detachLocation, nodeAliases));
4903
+ let rootsInField = fields.get(fieldIdKey);
4904
+ if (rootsInField === undefined) {
4905
+ rootsInField = [];
4906
+ fields.set(fieldIdKey, rootsInField);
4907
+ }
4908
+
4909
+ rootsInField.push(rootId);
4910
+ }
4911
+ }
4912
+
4913
+ return fields;
4914
+ }
4915
+
4916
+ function muteRootChanges(roots: RootNodeTable): RootNodeTable {
4917
+ return {
4918
+ oldToNewId: newChangeAtomIdTransform(),
4919
+ newToOldId: newChangeAtomIdTransform(),
4920
+ nodeChanges: brand(roots.nodeChanges.clone()),
4921
+ detachLocations: roots.detachLocations.clone(),
4922
+ outputDetachLocations: newChangeAtomIdRangeMap(),
4923
+ };
4924
+ }
4925
+
4926
+ export function validateChangeset(
4927
+ change: ModularChangeset,
4928
+ fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
4929
+ ): void {
4930
+ const unreachableNodes: ChangeAtomIdBTree<NodeLocation> = brand(change.nodeToParent.clone());
4931
+
4932
+ const unreachableCFKs = change.crossFieldKeys.clone();
4933
+
4934
+ validateFieldChanges(
4935
+ fieldKinds,
4936
+ change,
4937
+ change.fieldChanges,
4938
+ undefined,
4939
+ unreachableNodes,
4940
+ unreachableCFKs,
4941
+ );
4942
+
4943
+ for (const [[revision, localId], node] of change.nodeChanges.entries()) {
4944
+ if (node.fieldChanges === undefined) {
4945
+ continue;
4946
+ }
4947
+
4948
+ const nodeId = normalizeNodeId({ revision, localId }, change.nodeAliases);
4949
+ validateFieldChanges(
4950
+ fieldKinds,
4951
+ change,
4952
+ node.fieldChanges,
4953
+ nodeId,
4954
+ unreachableNodes,
4955
+ unreachableCFKs,
4956
+ );
4957
+ }
4958
+
4959
+ for (const [detachIdKey, nodeId] of change.rootNodes.nodeChanges.entries()) {
4960
+ const detachId: ChangeAtomId = { revision: detachIdKey[0], localId: detachIdKey[1] };
4961
+ const location = getNodeParent(change, nodeId);
4962
+ assert(areEqualChangeAtomIdOpts(location.root, detachId), "Inconsistent node location");
4963
+
4964
+ const normalizedNodeId = normalizeNodeId(nodeId, change.nodeAliases);
4965
+ unreachableNodes.delete([normalizedNodeId.revision, normalizedNodeId.localId]);
4966
+
4967
+ const fieldChanges = nodeChangeFromId(
4968
+ change.nodeChanges,
4969
+ change.nodeAliases,
4970
+ nodeId,
4971
+ ).fieldChanges;
4972
+
4973
+ if (fieldChanges !== undefined) {
4974
+ validateFieldChanges(
4975
+ fieldKinds,
4976
+ change,
4977
+ fieldChanges,
4978
+ normalizedNodeId,
4979
+ unreachableNodes,
4980
+ unreachableCFKs,
4981
+ );
4982
+ }
4983
+ }
4984
+
4985
+ assert(unreachableNodes.size === 0, "Unreachable nodes found");
4986
+ assert(unreachableCFKs.entries().length === 0, "Unreachable cross-field keys found");
4987
+ }
4988
+
4989
+ /**
4990
+ * Asserts that each node has a correct entry in `change.nodeToParent`,
4991
+ * and each cross field key has a correct entry in `change.crossFieldKeys`.
4992
+ * @returns the number of children found.
4993
+ */
4994
+ function validateFieldChanges(
4995
+ fieldKinds: ReadonlyMap<FieldKindIdentifier, FlexFieldKind>,
4996
+ change: ModularChangeset,
4997
+ fieldChanges: FieldChangeMap,
4998
+ nodeParent: NodeId | undefined,
4999
+ unreachableNodes: ChangeAtomIdBTree<NodeLocation>,
5000
+ unreachableCFKs: CrossFieldRangeTable<FieldId>,
5001
+ ): void {
5002
+ for (const [field, fieldChange] of fieldChanges.entries()) {
5003
+ const fieldId = { nodeId: nodeParent, field };
5004
+ const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
5005
+ for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
5006
+ const parentFieldId = getNodeParent(change, child);
5007
+ assert(
5008
+ parentFieldId.field !== undefined && areEqualFieldIds(parentFieldId.field, fieldId),
5009
+ 0xa4e /* Inconsistent node parentage */,
5010
+ );
5011
+
5012
+ unreachableNodes.delete([child.revision, child.localId]);
5013
+ }
5014
+
5015
+ for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
5016
+ const fields = getFieldsForCrossFieldKey(change, keyRange.key, keyRange.count);
5017
+ assert(fields.length > 0, "Unregistered cross-field key");
5018
+ for (const fieldFromLookup of fields) {
5019
+ assert(
5020
+ areEqualFieldIds(fieldFromLookup, fieldId),
5021
+ 0xa4f /* Inconsistent cross field keys */,
5022
+ );
5023
+ }
5024
+
5025
+ unreachableCFKs.delete(keyRange.key, keyRange.count);
5026
+ }
5027
+ }
3156
5028
  }