@fluidframework/tree 2.1.0-276985 → 2.1.0-281041

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 (437) hide show
  1. package/.eslintrc.cjs +7 -0
  2. package/.vscode/Tree.code-workspace +7 -1
  3. package/README.md +51 -8
  4. package/api-report/tree.alpha.api.md +1 -0
  5. package/api-report/tree.beta.api.md +1 -0
  6. package/api-report/tree.public.api.md +1 -0
  7. package/beta.d.ts +1 -1
  8. package/dist/beta.d.ts +1 -1
  9. package/dist/core/forest/editableForest.d.ts +6 -3
  10. package/dist/core/forest/editableForest.d.ts.map +1 -1
  11. package/dist/core/forest/editableForest.js +14 -4
  12. package/dist/core/forest/editableForest.js.map +1 -1
  13. package/dist/core/index.d.ts +1 -1
  14. package/dist/core/index.d.ts.map +1 -1
  15. package/dist/core/index.js +3 -1
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/core/rebase/index.d.ts +1 -1
  18. package/dist/core/rebase/index.d.ts.map +1 -1
  19. package/dist/core/rebase/index.js +3 -1
  20. package/dist/core/rebase/index.js.map +1 -1
  21. package/dist/core/rebase/types.d.ts +2 -0
  22. package/dist/core/rebase/types.d.ts.map +1 -1
  23. package/dist/core/rebase/types.js +9 -1
  24. package/dist/core/rebase/types.js.map +1 -1
  25. package/dist/core/tree/visitDelta.d.ts.map +1 -1
  26. package/dist/core/tree/visitDelta.js.map +1 -1
  27. package/dist/events/events.d.ts +4 -1
  28. package/dist/events/events.d.ts.map +1 -1
  29. package/dist/events/events.js.map +1 -1
  30. package/dist/feature-libraries/default-schema/defaultEditBuilder.js +1 -1
  31. package/dist/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  32. package/dist/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  33. package/dist/feature-libraries/default-schema/defaultFieldKinds.js +1 -0
  34. package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  35. package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts +0 -2
  36. package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
  37. package/dist/feature-libraries/flex-map-tree/mapTreeNode.js +0 -20
  38. package/dist/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
  39. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +0 -38
  40. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  41. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  42. package/dist/feature-libraries/flex-tree/index.d.ts +1 -1
  43. package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
  44. package/dist/feature-libraries/flex-tree/index.js.map +1 -1
  45. package/dist/feature-libraries/flex-tree/lazyField.d.ts +0 -4
  46. package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  47. package/dist/feature-libraries/flex-tree/lazyField.js +1 -14
  48. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  49. package/dist/feature-libraries/flex-tree/lazyNode.d.ts +0 -1
  50. package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  51. package/dist/feature-libraries/flex-tree/lazyNode.js +0 -3
  52. package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  53. package/dist/feature-libraries/index.d.ts +3 -3
  54. package/dist/feature-libraries/index.d.ts.map +1 -1
  55. package/dist/feature-libraries/index.js +2 -2
  56. package/dist/feature-libraries/index.js.map +1 -1
  57. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts +11 -0
  58. package/dist/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  59. package/dist/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  60. package/dist/feature-libraries/modular-schema/discrepancies.d.ts +96 -0
  61. package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -0
  62. package/dist/feature-libraries/modular-schema/discrepancies.js +264 -0
  63. package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -0
  64. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +9 -2
  65. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  66. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  67. package/dist/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  68. package/dist/feature-libraries/modular-schema/genericFieldKind.js +3 -0
  69. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  70. package/dist/feature-libraries/modular-schema/index.d.ts +2 -1
  71. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  72. package/dist/feature-libraries/modular-schema/index.js +3 -1
  73. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  74. package/dist/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  75. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +42 -26
  76. package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  77. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +51 -2
  78. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  79. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +830 -245
  80. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  81. package/dist/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +1 -1
  82. package/dist/feature-libraries/modular-schema/modularChangeFormat.js +2 -0
  83. package/dist/feature-libraries/modular-schema/modularChangeFormat.js.map +1 -1
  84. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +44 -1
  85. package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  86. package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  87. package/dist/feature-libraries/node-key/index.d.ts +0 -1
  88. package/dist/feature-libraries/node-key/index.d.ts.map +1 -1
  89. package/dist/feature-libraries/node-key/index.js +1 -3
  90. package/dist/feature-libraries/node-key/index.js.map +1 -1
  91. package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  92. package/dist/feature-libraries/optional-field/optionalField.js +1 -0
  93. package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
  94. package/dist/feature-libraries/sequence-field/index.d.ts +1 -1
  95. package/dist/feature-libraries/sequence-field/index.d.ts.map +1 -1
  96. package/dist/feature-libraries/sequence-field/index.js +1 -2
  97. package/dist/feature-libraries/sequence-field/index.js.map +1 -1
  98. package/dist/feature-libraries/sequence-field/invert.js +1 -1
  99. package/dist/feature-libraries/sequence-field/invert.js.map +1 -1
  100. package/dist/feature-libraries/sequence-field/rebase.js +6 -1
  101. package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
  102. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  103. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +1 -0
  104. package/dist/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  105. package/dist/feature-libraries/sequence-field/utils.d.ts +2 -17
  106. package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  107. package/dist/feature-libraries/sequence-field/utils.js +31 -39
  108. package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
  109. package/dist/feature-libraries/typed-schema/typedTreeSchema.d.ts +1 -0
  110. package/dist/feature-libraries/typed-schema/typedTreeSchema.d.ts.map +1 -1
  111. package/dist/feature-libraries/typed-schema/typedTreeSchema.js +2 -0
  112. package/dist/feature-libraries/typed-schema/typedTreeSchema.js.map +1 -1
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js.map +1 -1
  116. package/dist/packageVersion.d.ts +1 -1
  117. package/dist/packageVersion.js +1 -1
  118. package/dist/packageVersion.js.map +1 -1
  119. package/dist/public.d.ts +1 -1
  120. package/dist/shared-tree/schematizingTreeView.d.ts +4 -2
  121. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  122. package/dist/shared-tree/schematizingTreeView.js +240 -184
  123. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  124. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  125. package/dist/shared-tree/sharedTree.js +150 -90
  126. package/dist/shared-tree/sharedTree.js.map +1 -1
  127. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  128. package/dist/shared-tree/treeCheckout.js +2 -1
  129. package/dist/shared-tree/treeCheckout.js.map +1 -1
  130. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  131. package/dist/shared-tree-core/branch.js +1 -0
  132. package/dist/shared-tree-core/branch.js.map +1 -1
  133. package/dist/shared-tree-core/sharedTreeCore.d.ts +4 -6
  134. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  135. package/dist/shared-tree-core/sharedTreeCore.js +265 -209
  136. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  137. package/dist/simple-tree/arrayNode.d.ts +4 -0
  138. package/dist/simple-tree/arrayNode.d.ts.map +1 -1
  139. package/dist/simple-tree/arrayNode.js +36 -19
  140. package/dist/simple-tree/arrayNode.js.map +1 -1
  141. package/dist/simple-tree/leafNodeSchema.d.ts +22 -1
  142. package/dist/simple-tree/leafNodeSchema.d.ts.map +1 -1
  143. package/dist/simple-tree/leafNodeSchema.js +2 -1
  144. package/dist/simple-tree/leafNodeSchema.js.map +1 -1
  145. package/dist/simple-tree/mapNode.d.ts.map +1 -1
  146. package/dist/simple-tree/mapNode.js.map +1 -1
  147. package/dist/simple-tree/objectNode.d.ts.map +1 -1
  148. package/dist/simple-tree/objectNode.js +2 -1
  149. package/dist/simple-tree/objectNode.js.map +1 -1
  150. package/dist/simple-tree/proxies.d.ts.map +1 -1
  151. package/dist/simple-tree/proxies.js +2 -4
  152. package/dist/simple-tree/proxies.js.map +1 -1
  153. package/dist/simple-tree/schemaFactory.d.ts +16 -1
  154. package/dist/simple-tree/schemaFactory.d.ts.map +1 -1
  155. package/dist/simple-tree/schemaFactory.js +32 -4
  156. package/dist/simple-tree/schemaFactory.js.map +1 -1
  157. package/dist/simple-tree/schemaTypes.d.ts +36 -1
  158. package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
  159. package/dist/simple-tree/schemaTypes.js.map +1 -1
  160. package/dist/simple-tree/toFlexSchema.d.ts +2 -2
  161. package/dist/simple-tree/toFlexSchema.d.ts.map +1 -1
  162. package/dist/simple-tree/toFlexSchema.js +3 -2
  163. package/dist/simple-tree/toFlexSchema.js.map +1 -1
  164. package/dist/simple-tree/tree.d.ts +4 -1
  165. package/dist/simple-tree/tree.d.ts.map +1 -1
  166. package/dist/simple-tree/tree.js +48 -1
  167. package/dist/simple-tree/tree.js.map +1 -1
  168. package/dist/simple-tree/treeNodeApi.d.ts.map +1 -1
  169. package/dist/simple-tree/treeNodeApi.js +10 -10
  170. package/dist/simple-tree/treeNodeApi.js.map +1 -1
  171. package/dist/simple-tree/types.d.ts +22 -3
  172. package/dist/simple-tree/types.d.ts.map +1 -1
  173. package/dist/simple-tree/types.js +32 -21
  174. package/dist/simple-tree/types.js.map +1 -1
  175. package/dist/util/breakable.d.ts +83 -0
  176. package/dist/util/breakable.d.ts.map +1 -0
  177. package/dist/util/breakable.js +178 -0
  178. package/dist/util/breakable.js.map +1 -0
  179. package/dist/util/index.d.ts +3 -2
  180. package/dist/util/index.d.ts.map +1 -1
  181. package/dist/util/index.js +9 -2
  182. package/dist/util/index.js.map +1 -1
  183. package/dist/util/nestedMap.d.ts +17 -3
  184. package/dist/util/nestedMap.d.ts.map +1 -1
  185. package/dist/util/nestedMap.js +21 -1
  186. package/dist/util/nestedMap.js.map +1 -1
  187. package/dist/util/utils.d.ts +7 -0
  188. package/dist/util/utils.d.ts.map +1 -1
  189. package/dist/util/utils.js +15 -1
  190. package/dist/util/utils.js.map +1 -1
  191. package/internal.d.ts +1 -1
  192. package/lib/beta.d.ts +1 -1
  193. package/lib/core/forest/editableForest.d.ts +6 -3
  194. package/lib/core/forest/editableForest.d.ts.map +1 -1
  195. package/lib/core/forest/editableForest.js +15 -5
  196. package/lib/core/forest/editableForest.js.map +1 -1
  197. package/lib/core/index.d.ts +1 -1
  198. package/lib/core/index.d.ts.map +1 -1
  199. package/lib/core/index.js +1 -1
  200. package/lib/core/index.js.map +1 -1
  201. package/lib/core/rebase/index.d.ts +1 -1
  202. package/lib/core/rebase/index.d.ts.map +1 -1
  203. package/lib/core/rebase/index.js +1 -1
  204. package/lib/core/rebase/index.js.map +1 -1
  205. package/lib/core/rebase/types.d.ts +2 -0
  206. package/lib/core/rebase/types.d.ts.map +1 -1
  207. package/lib/core/rebase/types.js +7 -1
  208. package/lib/core/rebase/types.js.map +1 -1
  209. package/lib/core/tree/visitDelta.d.ts.map +1 -1
  210. package/lib/core/tree/visitDelta.js.map +1 -1
  211. package/lib/events/events.d.ts +4 -1
  212. package/lib/events/events.d.ts.map +1 -1
  213. package/lib/events/events.js.map +1 -1
  214. package/lib/feature-libraries/default-schema/defaultEditBuilder.js +1 -1
  215. package/lib/feature-libraries/default-schema/defaultEditBuilder.js.map +1 -1
  216. package/lib/feature-libraries/default-schema/defaultFieldKinds.d.ts.map +1 -1
  217. package/lib/feature-libraries/default-schema/defaultFieldKinds.js +1 -0
  218. package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  219. package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts +0 -2
  220. package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
  221. package/lib/feature-libraries/flex-map-tree/mapTreeNode.js +0 -20
  222. package/lib/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
  223. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +0 -38
  224. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  225. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  226. package/lib/feature-libraries/flex-tree/index.d.ts +1 -1
  227. package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
  228. package/lib/feature-libraries/flex-tree/index.js.map +1 -1
  229. package/lib/feature-libraries/flex-tree/lazyField.d.ts +0 -4
  230. package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  231. package/lib/feature-libraries/flex-tree/lazyField.js +1 -14
  232. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  233. package/lib/feature-libraries/flex-tree/lazyNode.d.ts +0 -1
  234. package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  235. package/lib/feature-libraries/flex-tree/lazyNode.js +0 -3
  236. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  237. package/lib/feature-libraries/index.d.ts +3 -3
  238. package/lib/feature-libraries/index.d.ts.map +1 -1
  239. package/lib/feature-libraries/index.js +2 -2
  240. package/lib/feature-libraries/index.js.map +1 -1
  241. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts +11 -0
  242. package/lib/feature-libraries/modular-schema/crossFieldQueries.d.ts.map +1 -1
  243. package/lib/feature-libraries/modular-schema/crossFieldQueries.js.map +1 -1
  244. package/lib/feature-libraries/modular-schema/discrepancies.d.ts +96 -0
  245. package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -0
  246. package/lib/feature-libraries/modular-schema/discrepancies.js +260 -0
  247. package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -0
  248. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +9 -2
  249. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  250. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  251. package/lib/feature-libraries/modular-schema/genericFieldKind.d.ts.map +1 -1
  252. package/lib/feature-libraries/modular-schema/genericFieldKind.js +3 -0
  253. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  254. package/lib/feature-libraries/modular-schema/index.d.ts +2 -1
  255. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  256. package/lib/feature-libraries/modular-schema/index.js +1 -0
  257. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  258. package/lib/feature-libraries/modular-schema/modularChangeCodecs.d.ts.map +1 -1
  259. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +42 -26
  260. package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
  261. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +51 -2
  262. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  263. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +829 -247
  264. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  265. package/lib/feature-libraries/modular-schema/modularChangeFormat.d.ts.map +1 -1
  266. package/lib/feature-libraries/modular-schema/modularChangeFormat.js +2 -0
  267. package/lib/feature-libraries/modular-schema/modularChangeFormat.js.map +1 -1
  268. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +44 -1
  269. package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
  270. package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
  271. package/lib/feature-libraries/node-key/index.d.ts +0 -1
  272. package/lib/feature-libraries/node-key/index.d.ts.map +1 -1
  273. package/lib/feature-libraries/node-key/index.js +0 -1
  274. package/lib/feature-libraries/node-key/index.js.map +1 -1
  275. package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  276. package/lib/feature-libraries/optional-field/optionalField.js +1 -0
  277. package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
  278. package/lib/feature-libraries/sequence-field/index.d.ts +1 -1
  279. package/lib/feature-libraries/sequence-field/index.d.ts.map +1 -1
  280. package/lib/feature-libraries/sequence-field/index.js +1 -1
  281. package/lib/feature-libraries/sequence-field/index.js.map +1 -1
  282. package/lib/feature-libraries/sequence-field/invert.js +1 -1
  283. package/lib/feature-libraries/sequence-field/invert.js.map +1 -1
  284. package/lib/feature-libraries/sequence-field/rebase.js +6 -1
  285. package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
  286. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.d.ts.map +1 -1
  287. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js +2 -1
  288. package/lib/feature-libraries/sequence-field/sequenceFieldChangeHandler.js.map +1 -1
  289. package/lib/feature-libraries/sequence-field/utils.d.ts +2 -17
  290. package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  291. package/lib/feature-libraries/sequence-field/utils.js +31 -39
  292. package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
  293. package/lib/feature-libraries/typed-schema/typedTreeSchema.d.ts +1 -0
  294. package/lib/feature-libraries/typed-schema/typedTreeSchema.d.ts.map +1 -1
  295. package/lib/feature-libraries/typed-schema/typedTreeSchema.js +4 -2
  296. package/lib/feature-libraries/typed-schema/typedTreeSchema.js.map +1 -1
  297. package/lib/index.d.ts +1 -1
  298. package/lib/index.d.ts.map +1 -1
  299. package/lib/index.js.map +1 -1
  300. package/lib/packageVersion.d.ts +1 -1
  301. package/lib/packageVersion.js +1 -1
  302. package/lib/packageVersion.js.map +1 -1
  303. package/lib/public.d.ts +1 -1
  304. package/lib/shared-tree/schematizingTreeView.d.ts +4 -2
  305. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  306. package/lib/shared-tree/schematizingTreeView.js +242 -185
  307. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  308. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  309. package/lib/shared-tree/sharedTree.js +151 -90
  310. package/lib/shared-tree/sharedTree.js.map +1 -1
  311. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  312. package/lib/shared-tree/treeCheckout.js +2 -1
  313. package/lib/shared-tree/treeCheckout.js.map +1 -1
  314. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  315. package/lib/shared-tree-core/branch.js +1 -0
  316. package/lib/shared-tree-core/branch.js.map +1 -1
  317. package/lib/shared-tree-core/sharedTreeCore.d.ts +4 -6
  318. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  319. package/lib/shared-tree-core/sharedTreeCore.js +267 -210
  320. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  321. package/lib/simple-tree/arrayNode.d.ts +4 -0
  322. package/lib/simple-tree/arrayNode.d.ts.map +1 -1
  323. package/lib/simple-tree/arrayNode.js +38 -21
  324. package/lib/simple-tree/arrayNode.js.map +1 -1
  325. package/lib/simple-tree/leafNodeSchema.d.ts +22 -1
  326. package/lib/simple-tree/leafNodeSchema.d.ts.map +1 -1
  327. package/lib/simple-tree/leafNodeSchema.js +1 -1
  328. package/lib/simple-tree/leafNodeSchema.js.map +1 -1
  329. package/lib/simple-tree/mapNode.d.ts.map +1 -1
  330. package/lib/simple-tree/mapNode.js.map +1 -1
  331. package/lib/simple-tree/objectNode.d.ts.map +1 -1
  332. package/lib/simple-tree/objectNode.js +3 -2
  333. package/lib/simple-tree/objectNode.js.map +1 -1
  334. package/lib/simple-tree/proxies.d.ts.map +1 -1
  335. package/lib/simple-tree/proxies.js +2 -4
  336. package/lib/simple-tree/proxies.js.map +1 -1
  337. package/lib/simple-tree/schemaFactory.d.ts +16 -1
  338. package/lib/simple-tree/schemaFactory.d.ts.map +1 -1
  339. package/lib/simple-tree/schemaFactory.js +30 -3
  340. package/lib/simple-tree/schemaFactory.js.map +1 -1
  341. package/lib/simple-tree/schemaTypes.d.ts +36 -1
  342. package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
  343. package/lib/simple-tree/schemaTypes.js.map +1 -1
  344. package/lib/simple-tree/toFlexSchema.d.ts +2 -2
  345. package/lib/simple-tree/toFlexSchema.d.ts.map +1 -1
  346. package/lib/simple-tree/toFlexSchema.js +3 -2
  347. package/lib/simple-tree/toFlexSchema.js.map +1 -1
  348. package/lib/simple-tree/tree.d.ts +4 -1
  349. package/lib/simple-tree/tree.d.ts.map +1 -1
  350. package/lib/simple-tree/tree.js +44 -0
  351. package/lib/simple-tree/tree.js.map +1 -1
  352. package/lib/simple-tree/treeNodeApi.d.ts.map +1 -1
  353. package/lib/simple-tree/treeNodeApi.js +11 -11
  354. package/lib/simple-tree/treeNodeApi.js.map +1 -1
  355. package/lib/simple-tree/types.d.ts +22 -3
  356. package/lib/simple-tree/types.d.ts.map +1 -1
  357. package/lib/simple-tree/types.js +32 -21
  358. package/lib/simple-tree/types.js.map +1 -1
  359. package/lib/util/breakable.d.ts +83 -0
  360. package/lib/util/breakable.d.ts.map +1 -0
  361. package/lib/util/breakable.js +171 -0
  362. package/lib/util/breakable.js.map +1 -0
  363. package/lib/util/index.d.ts +3 -2
  364. package/lib/util/index.d.ts.map +1 -1
  365. package/lib/util/index.js +3 -2
  366. package/lib/util/index.js.map +1 -1
  367. package/lib/util/nestedMap.d.ts +17 -3
  368. package/lib/util/nestedMap.d.ts.map +1 -1
  369. package/lib/util/nestedMap.js +19 -0
  370. package/lib/util/nestedMap.js.map +1 -1
  371. package/lib/util/utils.d.ts +7 -0
  372. package/lib/util/utils.d.ts.map +1 -1
  373. package/lib/util/utils.js +13 -0
  374. package/lib/util/utils.js.map +1 -1
  375. package/package.json +29 -27
  376. package/src/core/forest/editableForest.ts +17 -4
  377. package/src/core/index.ts +2 -0
  378. package/src/core/rebase/index.ts +2 -0
  379. package/src/core/rebase/types.ts +17 -0
  380. package/src/core/tree/visitDelta.ts +1 -0
  381. package/src/events/events.ts +4 -2
  382. package/src/feature-libraries/default-schema/defaultEditBuilder.ts +1 -1
  383. package/src/feature-libraries/default-schema/defaultFieldKinds.ts +1 -0
  384. package/src/feature-libraries/flex-map-tree/mapTreeNode.ts +0 -30
  385. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +0 -43
  386. package/src/feature-libraries/flex-tree/index.ts +0 -1
  387. package/src/feature-libraries/flex-tree/lazyField.ts +1 -21
  388. package/src/feature-libraries/flex-tree/lazyNode.ts +0 -6
  389. package/src/feature-libraries/index.ts +1 -2
  390. package/src/feature-libraries/modular-schema/crossFieldQueries.ts +18 -0
  391. package/src/feature-libraries/modular-schema/discrepancies.ts +395 -0
  392. package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +10 -2
  393. package/src/feature-libraries/modular-schema/genericFieldKind.ts +3 -0
  394. package/src/feature-libraries/modular-schema/index.ts +2 -0
  395. package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +81 -35
  396. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +1529 -454
  397. package/src/feature-libraries/modular-schema/modularChangeFormat.ts +2 -0
  398. package/src/feature-libraries/modular-schema/modularChangeTypes.ts +51 -0
  399. package/src/feature-libraries/node-key/index.ts +0 -1
  400. package/src/feature-libraries/optional-field/optionalField.ts +1 -0
  401. package/src/feature-libraries/sequence-field/index.ts +0 -2
  402. package/src/feature-libraries/sequence-field/invert.ts +1 -1
  403. package/src/feature-libraries/sequence-field/rebase.ts +7 -1
  404. package/src/feature-libraries/sequence-field/sequenceFieldChangeHandler.ts +2 -1
  405. package/src/feature-libraries/sequence-field/utils.ts +37 -85
  406. package/src/feature-libraries/typed-schema/typedTreeSchema.ts +10 -0
  407. package/src/index.ts +0 -1
  408. package/src/packageVersion.ts +1 -1
  409. package/src/shared-tree/schematizingTreeView.ts +6 -2
  410. package/src/shared-tree/sharedTree.ts +4 -0
  411. package/src/shared-tree/treeCheckout.ts +6 -2
  412. package/src/shared-tree-core/branch.ts +1 -0
  413. package/src/shared-tree-core/sharedTreeCore.ts +18 -6
  414. package/src/simple-tree/arrayNode.ts +49 -22
  415. package/src/simple-tree/leafNodeSchema.ts +1 -1
  416. package/src/simple-tree/mapNode.ts +2 -2
  417. package/src/simple-tree/objectNode.ts +9 -3
  418. package/src/simple-tree/proxies.ts +2 -4
  419. package/src/simple-tree/schemaFactory.ts +37 -2
  420. package/src/simple-tree/schemaTypes.ts +36 -1
  421. package/src/simple-tree/toFlexSchema.ts +5 -4
  422. package/src/simple-tree/tree.ts +65 -4
  423. package/src/simple-tree/treeNodeApi.ts +15 -15
  424. package/src/simple-tree/types.ts +60 -30
  425. package/src/util/breakable.ts +214 -0
  426. package/src/util/index.ts +10 -0
  427. package/src/util/nestedMap.ts +33 -3
  428. package/src/util/utils.ts +17 -0
  429. package/dist/feature-libraries/node-key/nodeKeyIndex.d.ts +0 -41
  430. package/dist/feature-libraries/node-key/nodeKeyIndex.d.ts.map +0 -1
  431. package/dist/feature-libraries/node-key/nodeKeyIndex.js +0 -101
  432. package/dist/feature-libraries/node-key/nodeKeyIndex.js.map +0 -1
  433. package/lib/feature-libraries/node-key/nodeKeyIndex.d.ts +0 -41
  434. package/lib/feature-libraries/node-key/nodeKeyIndex.d.ts.map +0 -1
  435. package/lib/feature-libraries/node-key/nodeKeyIndex.js +0 -97
  436. package/lib/feature-libraries/node-key/nodeKeyIndex.js.map +0 -1
  437. package/src/feature-libraries/node-key/nodeKeyIndex.ts +0 -132
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.ModularEditBuilder = exports.getChangeHandler = exports.getFieldKind = exports.rebaseRevisionMetadataFromInfo = exports.intoDelta = exports.updateRefreshers = exports.relevantRemovedRoots = exports.ModularChangeFamily = void 0;
7
+ exports.newCrossFieldKeyTable = exports.getFieldsForCrossFieldKey = exports.getParentFieldId = exports.ModularEditBuilder = exports.getChangeHandler = exports.getFieldKind = exports.rebaseRevisionMetadataFromInfo = exports.intoDelta = exports.updateRefreshers = exports.relevantRemovedRoots = exports.ModularChangeFamily = void 0;
8
8
  const internal_1 = require("@fluidframework/core-utils/internal");
9
9
  const sorted_btree_es6_1 = require("@tylerbu/sorted-btree-es6");
10
10
  const index_js_1 = require("../../core/index.js");
@@ -37,27 +37,30 @@ class ModularChangeFamily {
37
37
  */
38
38
  normalizeFieldChanges(change1, change2, genId, revisionMetadata) {
39
39
  // TODO: Handle the case where changes have conflicting field kinds
40
- const kind = change1 !== undefined && change1.fieldKind !== genericFieldKind_js_1.genericFieldKind.identifier
40
+ const kind = change1.fieldKind !== genericFieldKind_js_1.genericFieldKind.identifier
41
41
  ? change1.fieldKind
42
- : change2?.fieldKind ?? genericFieldKind_js_1.genericFieldKind.identifier;
42
+ : change2.fieldKind;
43
43
  if (kind === genericFieldKind_js_1.genericFieldKind.identifier) {
44
- // All the changes are generic
44
+ // Both changes are generic
45
45
  return {
46
- fieldKind: genericFieldKind_js_1.genericFieldKind,
47
- change1: change1?.change,
48
- change2: change2?.change,
46
+ fieldKind: genericFieldKind_js_1.genericFieldKind.identifier,
47
+ changeHandler: genericFieldKind_js_1.genericFieldKind.changeHandler,
48
+ change1: change1.change,
49
+ change2: change2.change,
49
50
  };
50
51
  }
51
52
  const fieldKind = getFieldKind(this.fieldKinds, kind);
52
- const handler = fieldKind.changeHandler;
53
- const normalizedChange1 = this.normalizeFieldChange(change1, handler, genId, revisionMetadata);
54
- const normalizedChange2 = this.normalizeFieldChange(change2, handler, genId, revisionMetadata);
55
- return { fieldKind, change1: normalizedChange1, change2: normalizedChange2 };
53
+ const changeHandler = fieldKind.changeHandler;
54
+ const normalizedChange1 = this.normalizeFieldChange(change1, changeHandler, genId, revisionMetadata);
55
+ const normalizedChange2 = this.normalizeFieldChange(change2, changeHandler, genId, revisionMetadata);
56
+ return {
57
+ fieldKind: kind,
58
+ changeHandler,
59
+ change1: normalizedChange1,
60
+ change2: normalizedChange2,
61
+ };
56
62
  }
57
63
  normalizeFieldChange(fieldChange, handler, genId, revisionMetadata) {
58
- if (fieldChange === undefined) {
59
- return undefined;
60
- }
61
64
  if (fieldChange.fieldKind !== genericFieldKind_js_1.genericFieldKind.identifier) {
62
65
  return fieldChange.change;
63
66
  }
@@ -72,105 +75,227 @@ class ModularChangeFamily {
72
75
  compose(changes) {
73
76
  const { revInfos, maxId } = getRevInfoFromTaggedChanges(changes);
74
77
  const idState = { maxId };
75
- return changes.reduce((change1, change2) => (0, index_js_1.makeAnonChange)(this.composePair(change1, change2, revInfos, idState)), (0, index_js_1.makeAnonChange)({ fieldChanges: new Map(), nodeChanges: new Map() })).change;
78
+ return changes.reduce((change1, change2) => (0, index_js_1.makeAnonChange)(this.composePair(change1, change2, revInfos, idState)), (0, index_js_1.makeAnonChange)({
79
+ fieldChanges: new Map(),
80
+ nodeChanges: new Map(),
81
+ nodeToParent: new Map(),
82
+ nodeAliases: new Map(),
83
+ crossFieldKeys: newCrossFieldKeyTable(),
84
+ })).change;
76
85
  }
77
86
  composePair(change1, change2, revInfos, idState) {
78
- const genId = (0, index_js_2.idAllocatorFromState)(idState);
79
- const revisionMetadata = (0, index_js_1.revisionMetadataSourceFromInfo)(revInfos);
80
- const crossFieldTable = newComposeTable();
81
- const composedFields = this.composeFieldMaps(getActiveFieldChanges(change1.change), getActiveFieldChanges(change2.change), genId, crossFieldTable, revisionMetadata);
82
- const composedNodeChanges = new Map();
83
- for (const [id1, id2] of crossFieldTable.nodeIdPairs) {
84
- this.composeNodesById(change1.change.nodeChanges, change2.change.nodeChanges, composedNodeChanges, id1, id2, genId, crossFieldTable, revisionMetadata);
85
- }
86
- crossFieldTable.nodeIdPairs.length = 0;
87
- while (crossFieldTable.invalidatedFields.size > 0) {
88
- const fieldsToUpdate = crossFieldTable.invalidatedFields;
89
- crossFieldTable.invalidatedFields = new Set();
90
- for (const fieldChange of fieldsToUpdate) {
91
- const context = crossFieldTable.fieldToContext.get(fieldChange);
92
- (0, internal_1.assert)(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
93
- const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
94
- const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
95
- const composeNodes = (child1, child2) => {
96
- if (child2 !== undefined &&
97
- !(0, index_js_2.nestedSetContains)(crossFieldTable.nodeIds, child2.revision, child2.localId)) {
98
- crossFieldTable.nodeIdPairs.push([child1, child2]);
99
- if (child1 !== undefined && child2 !== undefined) {
100
- (0, index_js_2.addToNestedSet)(crossFieldTable.nodeIds, child2.revision, child2.localId);
101
- }
102
- }
103
- return child1 ?? child2 ?? (0, index_js_2.fail)("Should not compose two undefined nodes");
104
- };
105
- const amendedChange = rebaser.compose(fieldChange1, fieldChange2, composeNodes, genId, newCrossFieldManager(crossFieldTable, fieldChange, false), revisionMetadata);
106
- composedChange.change = (0, index_js_2.brand)(amendedChange);
107
- // Process any newly discovered nodes.
108
- for (const [taggedId1, taggedId2] of crossFieldTable.nodeIdPairs) {
109
- this.composeNodesById(change1.change.nodeChanges, change2.change.nodeChanges, composedNodeChanges, taggedId1, taggedId2, genId, crossFieldTable, revisionMetadata);
110
- }
111
- crossFieldTable.nodeIdPairs.length = 0;
112
- }
113
- }
87
+ const { fieldChanges, nodeChanges, nodeToParent, nodeAliases, crossFieldKeys } = this.composeAllFields(change1.change, change2.change, revInfos, idState);
114
88
  const { allBuilds, allDestroys, allRefreshers } = composeBuildsDestroysAndRefreshers([
115
89
  change1,
116
90
  change2,
117
91
  ]);
118
- return makeModularChangeset(this.pruneFieldMap(composedFields, composedNodeChanges), composedNodeChanges, idState.maxId, revInfos, undefined, allBuilds, allDestroys, allRefreshers);
92
+ return makeModularChangeset(this.pruneFieldMap(fieldChanges, nodeChanges), nodeChanges, nodeToParent, nodeAliases, crossFieldKeys, idState.maxId, revInfos, undefined, allBuilds, allDestroys, allRefreshers);
93
+ }
94
+ composeAllFields(change1, change2, revInfos, idState) {
95
+ if (hasConflicts(change1) && hasConflicts(change2)) {
96
+ return {
97
+ fieldChanges: new Map(),
98
+ nodeChanges: new Map(),
99
+ nodeToParent: new Map(),
100
+ nodeAliases: new Map(),
101
+ crossFieldKeys: newBTree(),
102
+ };
103
+ }
104
+ else if (hasConflicts(change1)) {
105
+ return change2;
106
+ }
107
+ else if (hasConflicts(change2)) {
108
+ return change1;
109
+ }
110
+ const genId = (0, index_js_2.idAllocatorFromState)(idState);
111
+ const revisionMetadata = (0, index_js_1.revisionMetadataSourceFromInfo)(revInfos);
112
+ const crossFieldTable = newComposeTable(change1, change2);
113
+ // We merge nodeChanges, nodeToParent, and nodeAliases from the two changesets.
114
+ // The merged tables will have correct entries for all nodes which are only referenced in one of the input changesets.
115
+ // During composeFieldMaps and processInvalidatedElements we will find all nodes referenced in both input changesets
116
+ // and adjust these tables as necessary.
117
+ // Note that when merging these tables we may encounter key collisions and will arbitrarily drop values in that case.
118
+ // A collision for a node ID means that that node is referenced in both changesets
119
+ // (since we assume that if two changesets use the same node ID they are referring to the same node),
120
+ // therefore all collisions will be addressed when processing the intersection of the changesets.
121
+ const composedNodeChanges = mergeNestedMaps(change1.nodeChanges, change2.nodeChanges);
122
+ const composedNodeToParent = mergeNestedMaps(change1.nodeToParent, change2.nodeToParent);
123
+ const composedNodeAliases = mergeNestedMaps(change1.nodeAliases, change2.nodeAliases);
124
+ const composedFields = this.composeFieldMaps(change1.fieldChanges, change2.fieldChanges, genId, crossFieldTable, revisionMetadata);
125
+ this.processInvalidatedElements(crossFieldTable, composedFields, composedNodeChanges, composedNodeToParent, composedNodeAliases, genId, revisionMetadata);
126
+ // Currently no field kinds require making changes to cross-field keys during composition, so we can just merge the two tables.
127
+ const composedCrossFieldKeys = mergeBTrees(change1.crossFieldKeys, change2.crossFieldKeys);
128
+ return {
129
+ fieldChanges: composedFields,
130
+ nodeChanges: composedNodeChanges,
131
+ nodeToParent: composedNodeToParent,
132
+ nodeAliases: composedNodeAliases,
133
+ crossFieldKeys: (0, index_js_2.brand)(composedCrossFieldKeys),
134
+ };
135
+ }
136
+ composeInvalidatedField(fieldChange, crossFieldTable, genId, revisionMetadata) {
137
+ const context = crossFieldTable.fieldToContext.get(fieldChange);
138
+ (0, internal_1.assert)(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
139
+ const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
140
+ const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
141
+ const composeNodes = (child1, child2) => {
142
+ if (child1 !== undefined &&
143
+ child2 !== undefined &&
144
+ (0, index_js_1.getFromChangeAtomIdMap)(crossFieldTable.newToBaseNodeId, child2) === undefined) {
145
+ (0, index_js_1.setInChangeAtomIdMap)(crossFieldTable.newToBaseNodeId, child2, child1);
146
+ crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
147
+ }
148
+ return child1 ?? child2 ?? (0, index_js_2.fail)("Should not compose two undefined nodes");
149
+ };
150
+ const amendedChange = rebaser.compose(fieldChange1, fieldChange2, composeNodes, genId, new ComposeManager(crossFieldTable, fieldChange, false), revisionMetadata);
151
+ composedChange.change = (0, index_js_2.brand)(amendedChange);
152
+ }
153
+ /**
154
+ * Updates everything in the composed output which may no longer be valid.
155
+ * This could be due to
156
+ * - discovering that two node changesets refer to the same node (`nodeIdsToCompose`)
157
+ * - a previously composed field being invalidated by a cross field effect (`invalidatedFields`)
158
+ * - a field which was copied directly from an input changeset being invalidated by a cross field effect
159
+ * (`affectedBaseFields` and `affectedNewFields`)
160
+ *
161
+ * Updating an element may invalidate further elements. This function runs until there is no more invalidation.
162
+ */
163
+ processInvalidatedElements(table, composedFields, composedNodes, composedNodeToParent, nodeAliases, genId, metadata) {
164
+ const pending = table.pendingCompositions;
165
+ while (table.invalidatedFields.size > 0 ||
166
+ pending.nodeIdsToCompose.length > 0 ||
167
+ pending.affectedBaseFields.length > 0 ||
168
+ pending.affectedNewFields.length > 0) {
169
+ // Note that the call to `composeNodesById` can add entries to `crossFieldTable.nodeIdPairs`.
170
+ for (const [id1, id2] of pending.nodeIdsToCompose) {
171
+ this.composeNodesById(table.baseChange.nodeChanges, table.newChange.nodeChanges, composedNodes, composedNodeToParent, nodeAliases, id1, id2, genId, table, metadata);
172
+ }
173
+ pending.nodeIdsToCompose.length = 0;
174
+ this.composeAffectedFields(table, table.baseChange, true, pending.affectedBaseFields, composedFields, composedNodes, genId, metadata);
175
+ this.composeAffectedFields(table, table.newChange, false, pending.affectedNewFields, composedFields, composedNodes, genId, metadata);
176
+ this.processInvalidatedCompositions(table, genId, metadata);
177
+ }
178
+ }
179
+ processInvalidatedCompositions(table, genId, metadata) {
180
+ const fieldsToUpdate = table.invalidatedFields;
181
+ table.invalidatedFields = new Set();
182
+ for (const fieldChange of fieldsToUpdate) {
183
+ this.composeInvalidatedField(fieldChange, table, genId, metadata);
184
+ }
185
+ }
186
+ /**
187
+ * Ensures that each field in `affectedFields` has been updated in the composition output.
188
+ * Any field which has already been composed is ignored.
189
+ * All other fields are optimistically assumed to not have any changes in the other input changeset.
190
+ *
191
+ * @param change - The changeset which contains the affected fields.
192
+ * This should be one of the two changesets being composed.
193
+ * @param areBaseFields - Whether the affected fields are part of the base changeset.
194
+ * If not, they are assumed to be part of the new changeset.
195
+ * @param affectedFields - The set of fields to process.
196
+ */
197
+ composeAffectedFields(table, change, areBaseFields, affectedFields, composedFields, composedNodes, genId, metadata) {
198
+ for (const fieldIdKey of affectedFields.keys()) {
199
+ const fieldId = normalizeFieldId(fieldIdFromFieldIdKey(fieldIdKey), change.nodeAliases);
200
+ const fieldChange = fieldChangeFromId(change.fieldChanges, change.nodeChanges, fieldId);
201
+ if (table.fieldToContext.has(fieldChange) ||
202
+ table.newFieldToBaseField.has(fieldChange)) {
203
+ // This function handles fields which were not part of the intersection of the two changesets but which need to be updated anyway.
204
+ // If we've already processed this field then either it is up to date
205
+ // or there is pending inval which will be handled in processInvalidatedCompositions.
206
+ continue;
207
+ }
208
+ const emptyChange = this.createEmptyFieldChange(fieldChange.fieldKind);
209
+ const [change1, change2] = areBaseFields
210
+ ? [fieldChange, emptyChange]
211
+ : [emptyChange, fieldChange];
212
+ const composedField = this.composeFieldChanges(change1, change2, genId, table, metadata);
213
+ if (fieldId.nodeId === undefined) {
214
+ composedFields.set(fieldId.field, composedField);
215
+ continue;
216
+ }
217
+ const nodeId = (0, index_js_1.getFromChangeAtomIdMap)(table.newToBaseNodeId, fieldId.nodeId) ?? fieldId.nodeId;
218
+ let nodeChangeset = nodeChangeFromId(composedNodes, nodeId);
219
+ if (!table.composedNodes.has(nodeChangeset)) {
220
+ nodeChangeset = cloneNodeChangeset(nodeChangeset);
221
+ (0, index_js_1.setInChangeAtomIdMap)(composedNodes, nodeId, nodeChangeset);
222
+ }
223
+ if (nodeChangeset.fieldChanges === undefined) {
224
+ nodeChangeset.fieldChanges = new Map();
225
+ }
226
+ nodeChangeset.fieldChanges.set(fieldId.field, composedField);
227
+ }
228
+ affectedFields.clear();
119
229
  }
120
230
  composeFieldMaps(change1, change2, genId, crossFieldTable, revisionMetadata) {
121
231
  const composedFields = new Map();
122
- const fields = new Set();
123
- for (const field of change1?.keys() ?? []) {
124
- fields.add(field);
125
- }
126
- for (const field of change2?.keys() ?? []) {
127
- fields.add(field);
128
- }
129
- for (const field of fields) {
130
- const fieldChange1 = change1?.get(field);
131
- const fieldChange2 = change2?.get(field);
132
- const { fieldKind, change1: normalizedFieldChange1, change2: normalizedFieldChange2, } = this.normalizeFieldChanges(fieldChange1, fieldChange2, genId, revisionMetadata);
133
- const manager = newCrossFieldManager(crossFieldTable, fieldChange1 ?? fieldChange2);
134
- const change1Normalized = normalizedFieldChange1 ?? fieldKind.changeHandler.createEmpty();
135
- const change2Normalized = normalizedFieldChange2 ?? fieldKind.changeHandler.createEmpty();
136
- const composedChange = fieldKind.changeHandler.rebaser.compose(change1Normalized, change2Normalized, (child1, child2) => {
137
- crossFieldTable.nodeIdPairs.push([child1, child2]);
138
- if (child2 !== undefined) {
139
- (0, index_js_2.addToNestedSet)(crossFieldTable.nodeIds, child2.revision, child2.localId);
140
- }
141
- return child1 ?? child2 ?? (0, index_js_2.fail)("Should not compose two undefined nodes");
142
- }, genId, manager, revisionMetadata);
143
- const composedField = {
144
- fieldKind: fieldKind.identifier,
145
- change: (0, index_js_2.brand)(composedChange),
146
- };
147
- const fieldKey = fieldChange1 ?? fieldChange2 ?? (0, index_js_2.fail)("At least one field should have changes");
148
- crossFieldTable.fieldToContext.set(fieldKey, {
149
- change1: change1Normalized,
150
- change2: change2Normalized,
151
- composedChange: composedField,
152
- });
153
- // TODO: Could optimize by checking that composedField is non-empty
232
+ if (change1 === undefined || change2 === undefined) {
233
+ return change1 ?? change2 ?? composedFields;
234
+ }
235
+ for (const [field, fieldChange1] of change1) {
236
+ const fieldChange2 = change2.get(field);
237
+ const composedField = fieldChange2 !== undefined
238
+ ? this.composeFieldChanges(fieldChange1, fieldChange2, genId, crossFieldTable, revisionMetadata)
239
+ : fieldChange1;
154
240
  composedFields.set(field, composedField);
155
241
  }
242
+ for (const [field, fieldChange2] of change2) {
243
+ if (change1 === undefined || !change1.has(field)) {
244
+ composedFields.set(field, fieldChange2);
245
+ }
246
+ }
156
247
  return composedFields;
157
248
  }
158
- composeNodesById(nodeChanges1, nodeChanges2, composedNodeChanges, id1, id2, idAllocator, crossFieldTable, revisionMetadata) {
159
- const nodeChangeset1 = id1 !== undefined
160
- ? (0, index_js_2.tryGetFromNestedMap)(nodeChanges1, id1.revision, id1.localId) ??
161
- (0, index_js_2.fail)("Unknown node ID")
162
- : {};
163
- const nodeChangeset2 = id2 !== undefined
164
- ? (0, index_js_2.tryGetFromNestedMap)(nodeChanges2, id2.revision, id2.localId) ??
165
- (0, index_js_2.fail)("Unknown node ID")
166
- : {};
249
+ /**
250
+ * Returns the composition of the two input fields.
251
+ *
252
+ * Any nodes in this field which were modified by both changesets
253
+ * will be added to `crossFieldTable.pendingCompositions.nodeIdsToCompose`.
254
+ *
255
+ * Any fields which had cross-field information sent to them as part of this field composition
256
+ * will be added to either `affectedBaseFields` or `affectedNewFields` in `crossFieldTable.pendingCompositions`.
257
+ *
258
+ * Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
259
+ */
260
+ composeFieldChanges(change1, change2, idAllocator, crossFieldTable, revisionMetadata) {
261
+ const { fieldKind, changeHandler, change1: change1Normalized, change2: change2Normalized, } = this.normalizeFieldChanges(change1, change2, idAllocator, revisionMetadata);
262
+ const manager = new ComposeManager(crossFieldTable, change1);
263
+ const composedChange = changeHandler.rebaser.compose(change1Normalized, change2Normalized, (child1, child2) => {
264
+ if (child1 !== undefined && child2 !== undefined) {
265
+ (0, index_js_1.setInChangeAtomIdMap)(crossFieldTable.newToBaseNodeId, child2, child1);
266
+ crossFieldTable.pendingCompositions.nodeIdsToCompose.push([child1, child2]);
267
+ }
268
+ return child1 ?? child2 ?? (0, index_js_2.fail)("Should not compose two undefined nodes");
269
+ }, idAllocator, manager, revisionMetadata);
270
+ const composedField = {
271
+ fieldKind,
272
+ change: (0, index_js_2.brand)(composedChange),
273
+ };
274
+ crossFieldTable.fieldToContext.set(change1, {
275
+ change1: change1Normalized,
276
+ change2: change2Normalized,
277
+ composedChange: composedField,
278
+ });
279
+ crossFieldTable.newFieldToBaseField.set(change2, change1);
280
+ return composedField;
281
+ }
282
+ composeNodesById(nodeChanges1, nodeChanges2, composedNodes, composedNodeToParent, nodeAliases, id1, id2, idAllocator, crossFieldTable, revisionMetadata) {
283
+ const nodeChangeset1 = nodeChangeFromId(nodeChanges1, id1);
284
+ const nodeChangeset2 = nodeChangeFromId(nodeChanges2, id2);
167
285
  const composedNodeChangeset = this.composeNodeChanges(nodeChangeset1, nodeChangeset2, idAllocator, crossFieldTable, revisionMetadata);
168
- const nodeId = id1 ?? id2 ?? (0, index_js_2.fail)("Should not compose two undefined node IDs");
169
- (0, index_js_2.setInNestedMap)(composedNodeChanges, nodeId.revision, nodeId.localId, composedNodeChangeset);
286
+ (0, index_js_1.setInChangeAtomIdMap)(composedNodes, id1, composedNodeChangeset);
287
+ if (!(0, index_js_1.areEqualChangeAtomIds)(id1, id2)) {
288
+ (0, index_js_2.deleteFromNestedMap)(composedNodes, id2.revision, id2.localId);
289
+ (0, index_js_2.deleteFromNestedMap)(composedNodeToParent, id2.revision, id2.localId);
290
+ (0, index_js_1.setInChangeAtomIdMap)(nodeAliases, id2, id1);
291
+ // We need to delete id1 to avoid forming a cycle in case id1 already had an alias.
292
+ (0, index_js_2.deleteFromNestedMap)(nodeAliases, id1.revision, id1.localId);
293
+ }
294
+ crossFieldTable.composedNodes.add(composedNodeChangeset);
170
295
  }
171
296
  composeNodeChanges(change1, change2, genId, crossFieldTable, revisionMetadata) {
172
- const nodeExistsConstraint = change1?.nodeExistsConstraint ?? change2?.nodeExistsConstraint;
173
- const composedFieldChanges = this.composeFieldMaps(change1?.fieldChanges, change2?.fieldChanges, genId, crossFieldTable, revisionMetadata);
297
+ const nodeExistsConstraint = change1.nodeExistsConstraint ?? change2.nodeExistsConstraint;
298
+ const composedFieldChanges = this.composeFieldMaps(change1.fieldChanges, change2.fieldChanges, genId, crossFieldTable, revisionMetadata);
174
299
  const composedNodeChange = {};
175
300
  if (composedFieldChanges.size > 0) {
176
301
  composedNodeChange.fieldChanges = composedFieldChanges;
@@ -193,23 +318,21 @@ class ModularChangeFamily {
193
318
  // Destroys only occur in rollback changesets, which are never inverted.
194
319
  (0, internal_1.assert)(change.change.destroys === undefined, 0x89a /* Unexpected destroys in change to invert */);
195
320
  if ((change.change.constraintViolationCount ?? 0) > 0) {
196
- return makeModularChangeset(undefined, undefined, change.change.maxId, [], undefined, undefined, destroys);
321
+ return makeModularChangeset(undefined, undefined, undefined, undefined, undefined, change.change.maxId, [], undefined, undefined, destroys);
197
322
  }
198
- const idState = { maxId: change.change.maxId ?? -1 };
199
- // This idState is used for the whole of the IdAllocator's lifetime, which allows
200
- // this function to read the updated idState.maxId after more IDs are allocated.
201
- // TODO: add a getMax function to IdAllocator to make for a clearer contract.
202
- const genId = (0, index_js_2.idAllocatorFromState)(idState);
323
+ const genId = (0, index_js_2.idAllocatorFromMaxId)(change.change.maxId ?? -1);
324
+ const invertedNodeToParent = cloneNestedMap(change.change.nodeToParent);
203
325
  const crossFieldTable = {
204
326
  ...newCrossFieldTable(),
205
327
  originalFieldToContext: new Map(),
328
+ invertedNodeToParent,
206
329
  };
207
330
  const { revInfos } = getRevInfoFromTaggedChanges([change]);
208
331
  const revisionMetadata = (0, index_js_1.revisionMetadataSourceFromInfo)(revInfos);
209
- const invertedFields = this.invertFieldMap(change.change.fieldChanges, isRollback, genId, crossFieldTable, revisionMetadata);
332
+ const invertedFields = this.invertFieldMap(change.change.fieldChanges, undefined, isRollback, genId, crossFieldTable, revisionMetadata);
210
333
  const invertedNodes = new Map();
211
334
  (0, index_js_2.forEachInNestedMap)(change.change.nodeChanges, (nodeChangeset, revision, localId) => {
212
- (0, index_js_2.setInNestedMap)(invertedNodes, revision, localId, this.invertNodeChange(nodeChangeset, isRollback, genId, crossFieldTable, revisionMetadata));
335
+ (0, index_js_2.setInNestedMap)(invertedNodes, revision, localId, this.invertNodeChange(nodeChangeset, { revision, localId }, isRollback, genId, crossFieldTable, revisionMetadata));
213
336
  });
214
337
  if (crossFieldTable.invalidatedFields.size > 0) {
215
338
  const fieldsToUpdate = crossFieldTable.invalidatedFields;
@@ -218,17 +341,19 @@ class ModularChangeFamily {
218
341
  const originalFieldChange = fieldChange.change;
219
342
  const context = crossFieldTable.originalFieldToContext.get(fieldChange);
220
343
  (0, internal_1.assert)(context !== undefined, 0x851 /* Should have context for every invalidated field */);
221
- const { invertedField } = context;
222
- const amendedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(originalFieldChange, isRollback, genId, newCrossFieldManager(crossFieldTable, fieldChange), revisionMetadata);
344
+ const { invertedField, fieldId } = context;
345
+ const amendedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(originalFieldChange, isRollback, genId, new InvertManager(crossFieldTable, fieldChange, fieldId), revisionMetadata);
223
346
  invertedField.change = (0, index_js_2.brand)(amendedChange);
224
347
  }
225
348
  }
226
- return makeModularChangeset(invertedFields, invertedNodes, idState.maxId, [], change.change.constraintViolationCount, undefined, destroys);
349
+ const crossFieldKeys = this.makeCrossFieldKeyTable(invertedFields, invertedNodes);
350
+ return makeModularChangeset(invertedFields, invertedNodes, invertedNodeToParent, change.change.nodeAliases, crossFieldKeys, genId.getMaxId(), [], change.change.constraintViolationCount, undefined, destroys);
227
351
  }
228
- invertFieldMap(changes, isRollback, genId, crossFieldTable, revisionMetadata) {
352
+ invertFieldMap(changes, parentId, isRollback, genId, crossFieldTable, revisionMetadata) {
229
353
  const invertedFields = new Map();
230
354
  for (const [field, fieldChange] of changes) {
231
- const manager = newCrossFieldManager(crossFieldTable, fieldChange);
355
+ const fieldId = { nodeId: parentId, field };
356
+ const manager = new InvertManager(crossFieldTable, fieldChange, fieldId);
232
357
  const invertedChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.invert(fieldChange.change, isRollback, genId, manager, revisionMetadata);
233
358
  const invertedFieldChange = {
234
359
  ...fieldChange,
@@ -236,15 +361,16 @@ class ModularChangeFamily {
236
361
  };
237
362
  invertedFields.set(field, invertedFieldChange);
238
363
  crossFieldTable.originalFieldToContext.set(fieldChange, {
364
+ fieldId,
239
365
  invertedField: invertedFieldChange,
240
366
  });
241
367
  }
242
368
  return invertedFields;
243
369
  }
244
- invertNodeChange(change, isRollback, genId, crossFieldTable, revisionMetadata) {
370
+ invertNodeChange(change, id, isRollback, genId, crossFieldTable, revisionMetadata) {
245
371
  const inverse = {};
246
372
  if (change.fieldChanges !== undefined) {
247
- inverse.fieldChanges = this.invertFieldMap(change.fieldChanges, isRollback, genId, crossFieldTable, revisionMetadata);
373
+ inverse.fieldChanges = this.invertFieldMap(change.fieldChanges, id, isRollback, genId, crossFieldTable, revisionMetadata);
248
374
  }
249
375
  return inverse;
250
376
  }
@@ -255,9 +381,15 @@ class ModularChangeFamily {
255
381
  const genId = (0, index_js_2.idAllocatorFromState)(idState);
256
382
  const crossFieldTable = {
257
383
  ...newCrossFieldTable(),
258
- fieldToContext: new Map(),
259
- rebasedNodeCache: new Map(),
384
+ newChange: change,
385
+ baseChange: over.change,
386
+ baseFieldToContext: new Map(),
387
+ baseToRebasedNodeId: new Map(),
388
+ rebasedFields: new Set(),
389
+ rebasedNodeToParent: cloneNestedMap(change.nodeToParent),
390
+ rebasedCrossFieldKeys: (0, index_js_2.brand)(change.crossFieldKeys.clone()),
260
391
  nodeIdPairs: [],
392
+ affectedBaseFields: newBTree(),
261
393
  };
262
394
  let constraintState = newConstraintState(change.constraintViolationCount ?? 0);
263
395
  const getBaseRevisions = () => revisionInfoFromTaggedChange(over).map((info) => info.revision);
@@ -266,108 +398,198 @@ class ModularChangeFamily {
266
398
  getRevisionToRebase: () => taggedChange.revision,
267
399
  getBaseRevisions,
268
400
  };
269
- const rebasedFields = this.rebaseFieldMap(change.fieldChanges, over.change.fieldChanges, genId, crossFieldTable, rebaseMetadata);
270
- const rebasedNodes = new Map();
271
- for (const [newId, baseId] of crossFieldTable.nodeIdPairs) {
272
- const newNodeChange = newId !== undefined
273
- ? (0, index_js_2.tryGetFromNestedMap)(change.nodeChanges, newId.revision, newId.localId)
274
- : undefined;
275
- const baseNodeChange = baseId !== undefined
276
- ? (0, index_js_2.tryGetFromNestedMap)(over.change.nodeChanges, baseId.revision, baseId.localId) ??
277
- (0, index_js_2.fail)("Unknown node ID")
278
- : {};
279
- const rebasedNode = this.rebaseNodeChange(newNodeChange, baseNodeChange, genId, crossFieldTable, rebaseMetadata);
280
- if (rebasedNode !== undefined) {
281
- const nodeId = newId ?? baseId ?? (0, index_js_2.fail)("Should not have two undefined IDs");
282
- (0, index_js_2.setInNestedMap)(rebasedNodes, nodeId.revision, nodeId.localId, rebasedNode);
283
- }
284
- }
401
+ const rebasedNodes = cloneNestedMap(change.nodeChanges);
402
+ const rebasedFields = this.rebaseIntersectingFields(crossFieldTable, rebasedNodes, genId, constraintState, rebaseMetadata);
403
+ this.rebaseFieldsWithoutNewChanges(rebasedFields, rebasedNodes, crossFieldTable, genId, rebaseMetadata);
285
404
  if (crossFieldTable.invalidatedFields.size > 0) {
286
405
  const fieldsToUpdate = crossFieldTable.invalidatedFields;
287
406
  crossFieldTable.invalidatedFields = new Set();
288
407
  constraintState = newConstraintState(change.constraintViolationCount ?? 0);
289
408
  for (const field of fieldsToUpdate) {
290
- // TODO: Should we copy the context table out before this loop?
291
- const context = crossFieldTable.fieldToContext.get(field);
409
+ const context = crossFieldTable.baseFieldToContext.get(field);
292
410
  (0, internal_1.assert)(context !== undefined, 0x852 /* Every field should have a context */);
293
- const { fieldKind, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(context.newChange, context.baseChange, genId, revisionMetadata);
294
- context.rebasedChange.change = fieldKind.changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, (curr, base, existenceState) => curr ?? base, genId, newCrossFieldManager(crossFieldTable, field), rebaseMetadata);
411
+ const { changeHandler, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(context.newChange, context.baseChange, genId, revisionMetadata);
412
+ const rebaseChild = (curr, base) => {
413
+ if (curr !== undefined) {
414
+ return curr;
415
+ }
416
+ if (base !== undefined) {
417
+ for (const id of context.baseNodeIds) {
418
+ if ((0, index_js_1.areEqualChangeAtomIds)(base, id)) {
419
+ return base;
420
+ }
421
+ }
422
+ }
423
+ return undefined;
424
+ };
425
+ context.rebasedChange.change = (0, index_js_2.brand)(changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, new RebaseManager(crossFieldTable, field, context.fieldId), rebaseMetadata));
295
426
  }
296
427
  }
297
428
  this.updateConstraintsForFields(rebasedFields, fieldChangeHandler_js_1.NodeAttachState.Attached, constraintState, rebasedNodes);
298
- return makeModularChangeset(this.pruneFieldMap(rebasedFields, rebasedNodes), rebasedNodes, idState.maxId, change.revisions, constraintState.violationCount, change.builds, change.destroys, change.refreshers);
429
+ return makeModularChangeset(this.pruneFieldMap(rebasedFields, rebasedNodes), rebasedNodes, crossFieldTable.rebasedNodeToParent, change.nodeAliases, crossFieldTable.rebasedCrossFieldKeys, idState.maxId, change.revisions, constraintState.violationCount, change.builds, change.destroys, change.refreshers);
299
430
  }
300
- rebaseFieldMap(change, over, genId, crossFieldTable, revisionMetadata) {
301
- const rebasedFields = new Map();
302
- // Rebase fields contained in the base changeset
303
- for (const [field, baseChanges] of over) {
304
- const fieldChange = change.get(field) ?? {
305
- fieldKind: genericFieldKind_js_1.genericFieldKind.identifier,
306
- change: (0, index_js_2.brand)((0, genericFieldKind_js_1.newGenericChangeset)()),
431
+ // This performs a first pass on all fields which have both new and base changes.
432
+ // TODO: Can we also handle additional passes in this method?
433
+ rebaseIntersectingFields(crossFieldTable, rebasedNodes, genId, constraintState, metadata) {
434
+ const change = crossFieldTable.newChange;
435
+ const baseChange = crossFieldTable.baseChange;
436
+ const rebasedFields = this.rebaseFieldMap(change.fieldChanges, baseChange.fieldChanges, undefined, genId, crossFieldTable, metadata);
437
+ // This loop processes all fields which have both base and new changes.
438
+ // Note that the call to `rebaseNodeChange` can add entries to `crossFieldTable.nodeIdPairs`.
439
+ for (const [newId, baseId, _attachState] of crossFieldTable.nodeIdPairs) {
440
+ const rebasedNode = this.rebaseNodeChange(newId, baseId, genId, crossFieldTable, metadata, constraintState);
441
+ (0, index_js_1.setInChangeAtomIdMap)(rebasedNodes, newId, rebasedNode);
442
+ }
443
+ return rebasedFields;
444
+ }
445
+ // This processes fields which have no new changes but have been invalidated by another field.
446
+ rebaseFieldsWithoutNewChanges(rebasedFields, rebasedNodes, crossFieldTable, genId, metadata) {
447
+ const baseChange = crossFieldTable.baseChange;
448
+ for (const [revision, localId, fieldKey] of crossFieldTable.affectedBaseFields.keys()) {
449
+ const baseNodeId = localId !== undefined
450
+ ? normalizeNodeId({ revision, localId }, baseChange.nodeAliases)
451
+ : undefined;
452
+ const baseFieldChange = fieldMapFromNodeId(baseChange.fieldChanges, baseChange.nodeChanges, baseNodeId).get(fieldKey);
453
+ (0, internal_1.assert)(baseFieldChange !== undefined, "Cross field key registered for empty field");
454
+ if (crossFieldTable.baseFieldToContext.has(baseFieldChange)) {
455
+ // This field has already been processed because there were changes to rebase.
456
+ continue;
457
+ }
458
+ // This field has no changes in the new changeset, otherwise it would have been added to
459
+ // `crossFieldTable.baseFieldToContext` when processing fields with both base and new changes.
460
+ const rebaseChild = (child, baseChild, stateChange) => {
461
+ (0, internal_1.assert)(child === undefined, "There should be no new changes in this field");
462
+ return undefined;
307
463
  };
308
- const { fieldKind, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(fieldChange, baseChanges, genId, revisionMetadata);
309
- const manager = newCrossFieldManager(crossFieldTable, fieldChange);
310
- const rebaseChild = (child, baseChild, _attachState) => {
311
- crossFieldTable.nodeIdPairs.push([child, baseChild]);
312
- return (child ??
313
- // The fact `child` is undefined means that the changeset to rebase does not include changes for
314
- // this node or its descendants. However, it's possible that it will after rebasing.
315
- // In that case, we will need a NodeId to represent these changes under in the rebased changeset.
316
- // We adopt `baseChild` for this purpose.
317
- baseChild ??
318
- (0, index_js_2.fail)("Should not have two undefined node IDs"));
464
+ const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
465
+ const fieldChange = {
466
+ ...baseFieldChange,
467
+ change: (0, index_js_2.brand)(handler.createEmpty()),
319
468
  };
320
- const rebasedField = fieldKind.changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, manager, revisionMetadata);
469
+ const rebasedNodeId = baseNodeId !== undefined
470
+ ? rebasedNodeIdFromBaseNodeId(crossFieldTable, baseNodeId)
471
+ : undefined;
472
+ const fieldId = { nodeId: rebasedNodeId, field: fieldKey };
473
+ const rebasedField = handler.rebaser.rebase(fieldChange.change, baseFieldChange.change, rebaseChild, genId, new RebaseManager(crossFieldTable, baseFieldChange, fieldId), metadata);
321
474
  const rebasedFieldChange = {
322
- fieldKind: fieldKind.identifier,
475
+ ...baseFieldChange,
323
476
  change: (0, index_js_2.brand)(rebasedField),
324
477
  };
325
- rebasedFields.set(field, rebasedFieldChange);
326
- crossFieldTable.fieldToContext.set(fieldChange, {
327
- baseChange: baseChanges,
478
+ // TODO: Deduplicate
479
+ crossFieldTable.baseFieldToContext.set(baseFieldChange, {
328
480
  newChange: fieldChange,
481
+ baseChange: baseFieldChange,
329
482
  rebasedChange: rebasedFieldChange,
483
+ fieldId,
484
+ baseNodeIds: [],
330
485
  });
486
+ crossFieldTable.rebasedFields.add(rebasedFieldChange);
487
+ this.attachRebasedField(rebasedFields, rebasedNodes, crossFieldTable, rebasedFieldChange, fieldId, genId, metadata);
488
+ }
489
+ }
490
+ attachRebasedField(rebasedFields, rebasedNodes, table, rebasedField, { nodeId, field: fieldKey }, idAllocator, metadata) {
491
+ if (nodeId === undefined) {
492
+ rebasedFields.set(fieldKey, rebasedField);
493
+ return;
494
+ }
495
+ const rebasedNode = (0, index_js_1.getFromChangeAtomIdMap)(rebasedNodes, nodeId);
496
+ if (rebasedNode !== undefined) {
497
+ if (rebasedNode.fieldChanges === undefined) {
498
+ rebasedNode.fieldChanges = new Map([[fieldKey, rebasedField]]);
499
+ return;
500
+ }
501
+ (0, internal_1.assert)(!rebasedNode.fieldChanges.has(fieldKey), "Expected an empty field");
502
+ rebasedNode.fieldChanges.set(fieldKey, rebasedField);
503
+ return;
504
+ }
505
+ const newNode = {
506
+ fieldChanges: new Map([[fieldKey, rebasedField]]),
507
+ };
508
+ (0, index_js_1.setInChangeAtomIdMap)(rebasedNodes, nodeId, newNode);
509
+ (0, index_js_1.setInChangeAtomIdMap)(table.baseToRebasedNodeId, nodeId, nodeId);
510
+ const parentFieldId = getParentFieldId(table.baseChange, nodeId);
511
+ this.attachRebasedNode(rebasedFields, rebasedNodes, table, nodeId, parentFieldId, idAllocator, metadata);
512
+ }
513
+ attachRebasedNode(rebasedFields, rebasedNodes, table, baseNodeId, parentFieldIdBase, idAllocator, metadata) {
514
+ const baseFieldChange = fieldChangeFromId(table.baseChange.fieldChanges, table.baseChange.nodeChanges, parentFieldIdBase);
515
+ const rebasedFieldId = rebasedFieldIdFromBaseId(table, parentFieldIdBase);
516
+ (0, index_js_1.setInChangeAtomIdMap)(table.rebasedNodeToParent, baseNodeId, rebasedFieldId);
517
+ const context = table.baseFieldToContext.get(baseFieldChange);
518
+ if (context !== undefined) {
519
+ // We've already processed this field.
520
+ // The new child node can be attached when processing invalidated fields.
521
+ context.baseNodeIds.push(baseNodeId);
522
+ table.invalidatedFields.add(baseFieldChange);
523
+ return;
331
524
  }
332
- // Rebase the fields of the new changeset which don't have a corresponding base field.
525
+ const handler = getChangeHandler(this.fieldKinds, baseFieldChange.fieldKind);
526
+ const fieldChange = {
527
+ ...baseFieldChange,
528
+ change: (0, index_js_2.brand)(handler.createEmpty()),
529
+ };
530
+ const rebasedChangeset = handler.rebaser.rebase(handler.createEmpty(), baseFieldChange.change, (_idNew, idBase) => idBase !== undefined && (0, index_js_1.areEqualChangeAtomIds)(idBase, baseNodeId)
531
+ ? baseNodeId
532
+ : undefined, idAllocator, new RebaseManager(table, baseFieldChange, rebasedFieldId), metadata);
533
+ const rebasedField = { ...baseFieldChange, change: (0, index_js_2.brand)(rebasedChangeset) };
534
+ table.rebasedFields.add(rebasedField);
535
+ table.baseFieldToContext.set(baseFieldChange, {
536
+ newChange: fieldChange,
537
+ baseChange: baseFieldChange,
538
+ rebasedChange: rebasedField,
539
+ fieldId: rebasedFieldId,
540
+ baseNodeIds: [],
541
+ });
542
+ this.attachRebasedField(rebasedFields, rebasedNodes, table, rebasedField, rebasedFieldId, idAllocator, metadata);
543
+ }
544
+ rebaseFieldMap(change, over, parentId, genId, crossFieldTable, revisionMetadata) {
545
+ const rebasedFields = new Map();
546
+ const rebaseChild = (child, baseChild, stateChange) => {
547
+ if (child !== undefined && baseChild !== undefined) {
548
+ crossFieldTable.nodeIdPairs.push([child, baseChild, stateChange]);
549
+ }
550
+ return child;
551
+ };
333
552
  for (const [field, fieldChange] of change) {
334
- if (!over?.has(field)) {
335
- const baseChanges = {
336
- fieldKind: genericFieldKind_js_1.genericFieldKind.identifier,
337
- change: (0, index_js_2.brand)((0, genericFieldKind_js_1.newGenericChangeset)()),
338
- };
339
- const { fieldKind, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(fieldChange, baseChanges, genId, revisionMetadata);
340
- // TODO: Don't we need to add an entry in the context table?
341
- const manager = newCrossFieldManager(crossFieldTable, fieldChange);
342
- const rebasedChangeset = fieldKind.changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, (child, baseChild) => {
343
- (0, internal_1.assert)(baseChild === undefined, 0x5b6 /* This field should not have any base changes */);
344
- crossFieldTable.nodeIdPairs.push([child, undefined]);
345
- return child;
346
- }, genId, manager, revisionMetadata);
347
- const rebasedFieldChange = {
348
- fieldKind: fieldKind.identifier,
349
- change: (0, index_js_2.brand)(rebasedChangeset),
350
- };
351
- rebasedFields.set(field, rebasedFieldChange);
553
+ const fieldId = { nodeId: parentId, field };
554
+ const baseChange = over.get(field);
555
+ if (baseChange === undefined) {
556
+ rebasedFields.set(field, fieldChange);
557
+ continue;
352
558
  }
559
+ const { fieldKind, changeHandler, change1: fieldChangeset, change2: baseChangeset, } = this.normalizeFieldChanges(fieldChange, baseChange, genId, revisionMetadata);
560
+ const manager = new RebaseManager(crossFieldTable, baseChange, fieldId);
561
+ const rebasedField = changeHandler.rebaser.rebase(fieldChangeset, baseChangeset, rebaseChild, genId, manager, revisionMetadata);
562
+ const rebasedFieldChange = {
563
+ fieldKind,
564
+ change: (0, index_js_2.brand)(rebasedField),
565
+ };
566
+ rebasedFields.set(field, rebasedFieldChange);
567
+ crossFieldTable.baseFieldToContext.set(baseChange, {
568
+ baseChange,
569
+ newChange: fieldChange,
570
+ rebasedChange: rebasedFieldChange,
571
+ fieldId,
572
+ baseNodeIds: [],
573
+ });
574
+ crossFieldTable.rebasedFields.add(rebasedFieldChange);
353
575
  }
354
576
  return rebasedFields;
355
577
  }
356
- rebaseNodeChange(change, over, genId, crossFieldTable, revisionMetadata) {
357
- const key = change ?? over;
358
- if (key === undefined) {
359
- return undefined;
360
- }
578
+ rebaseNodeChange(newId, baseId, genId, crossFieldTable, revisionMetadata, constraintState) {
579
+ const change = nodeChangeFromId(crossFieldTable.newChange.nodeChanges, newId);
580
+ const over = nodeChangeFromId(crossFieldTable.baseChange.nodeChanges, baseId);
361
581
  const baseMap = over?.fieldChanges ?? new Map();
362
- const fieldChanges = this.rebaseFieldMap(change?.fieldChanges ?? new Map(), baseMap, genId, crossFieldTable, revisionMetadata);
582
+ const fieldChanges = change.fieldChanges !== undefined && over.fieldChanges !== undefined
583
+ ? this.rebaseFieldMap(change?.fieldChanges ?? new Map(), baseMap, newId, genId, crossFieldTable, revisionMetadata)
584
+ : change.fieldChanges;
363
585
  const rebasedChange = {};
364
- if (fieldChanges.size > 0) {
586
+ if (fieldChanges !== undefined && fieldChanges.size > 0) {
365
587
  rebasedChange.fieldChanges = fieldChanges;
366
588
  }
367
589
  if (change?.nodeExistsConstraint !== undefined) {
368
590
  rebasedChange.nodeExistsConstraint = change.nodeExistsConstraint;
369
591
  }
370
- crossFieldTable.rebasedNodeCache.set(key, rebasedChange);
592
+ (0, index_js_1.setInChangeAtomIdMap)(crossFieldTable.baseToRebasedNodeId, baseId, newId);
371
593
  return rebasedChange;
372
594
  }
373
595
  updateConstraintsForFields(fields, parentAttachState, constraintState, nodes) {
@@ -399,6 +621,9 @@ class ModularChangeFamily {
399
621
  }
400
622
  }
401
623
  pruneFieldMap(changeset, nodeMap) {
624
+ if (changeset === undefined) {
625
+ return undefined;
626
+ }
402
627
  const prunedChangeset = new Map();
403
628
  for (const [field, fieldChange] of changeset) {
404
629
  const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
@@ -410,8 +635,7 @@ class ModularChangeFamily {
410
635
  return prunedChangeset.size > 0 ? prunedChangeset : undefined;
411
636
  }
412
637
  pruneNodeChange(nodeId, nodeMap) {
413
- const changeset = (0, index_js_2.tryGetFromNestedMap)(nodeMap, nodeId.revision, nodeId.localId);
414
- (0, internal_1.assert)(changeset !== undefined, 0x930 /* Unknown node ID */);
638
+ const changeset = nodeChangeFromId(nodeMap, nodeId);
415
639
  const prunedFields = changeset.fieldChanges !== undefined
416
640
  ? this.pruneFieldMap(changeset.fieldChanges, nodeMap)
417
641
  : undefined;
@@ -424,7 +648,7 @@ class ModularChangeFamily {
424
648
  return undefined;
425
649
  }
426
650
  else {
427
- (0, index_js_2.setInNestedMap)(nodeMap, nodeId.revision, nodeId.localId, prunedChange);
651
+ (0, index_js_1.setInChangeAtomIdMap)(nodeMap, nodeId, prunedChange);
428
652
  return nodeId;
429
653
  }
430
654
  }
@@ -438,10 +662,19 @@ class ModularChangeFamily {
438
662
  id,
439
663
  this.replaceNodeChangesetRevisions(nodeChangeset, oldRevisions, newRevision),
440
664
  ]));
665
+ const updatedNodeToParent = (0, index_js_2.nestedMapFromFlatList)((0, index_js_2.nestedMapToFlatList)(change.nodeToParent).map(([revision, id, fieldId]) => [
666
+ replaceRevision(revision, oldRevisions, newRevision),
667
+ id,
668
+ replaceFieldIdRevision(normalizeFieldId(fieldId, change.nodeAliases), oldRevisions, newRevision),
669
+ ]));
441
670
  const updated = {
442
671
  ...change,
443
672
  fieldChanges: updatedFields,
444
673
  nodeChanges: updatedNodes,
674
+ nodeToParent: updatedNodeToParent,
675
+ // We've updated all references to old node IDs, so we no longer need an alias table.
676
+ nodeAliases: new Map(),
677
+ crossFieldKeys: replaceCrossFieldKeyTableRevisions(change.crossFieldKeys, oldRevisions, newRevision, change.nodeAliases),
445
678
  };
446
679
  if (change.builds !== undefined) {
447
680
  updated.builds = replaceIdMapRevisions(change.builds, oldRevisions, newRevision);
@@ -474,17 +707,63 @@ class ModularChangeFamily {
474
707
  replaceFieldMapRevisions(fields, oldRevisions, newRevision) {
475
708
  const updatedFields = new Map();
476
709
  for (const [field, fieldChange] of fields) {
477
- const updatedFieldChange = getFieldKind(this.fieldKinds, fieldChange.fieldKind).changeHandler.rebaser.replaceRevisions(fieldChange.change, oldRevisions, newRevision);
478
- updatedFields.set(field, { ...fieldChange, change: updatedFieldChange });
710
+ const updatedFieldChange = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).rebaser.replaceRevisions(fieldChange.change, oldRevisions, newRevision);
711
+ updatedFields.set(field, { ...fieldChange, change: (0, index_js_2.brand)(updatedFieldChange) });
479
712
  }
480
713
  return updatedFields;
481
714
  }
715
+ makeCrossFieldKeyTable(fields, nodes) {
716
+ const keys = newCrossFieldKeyTable();
717
+ this.populateCrossFieldKeyTableForFieldMap(keys, fields, undefined);
718
+ (0, index_js_2.forEachInNestedMap)(nodes, (node, revision, localId) => {
719
+ if (node.fieldChanges !== undefined) {
720
+ this.populateCrossFieldKeyTableForFieldMap(keys, node.fieldChanges, {
721
+ revision,
722
+ localId,
723
+ });
724
+ }
725
+ });
726
+ return keys;
727
+ }
728
+ populateCrossFieldKeyTableForFieldMap(table, fields, parent) {
729
+ for (const [fieldKey, fieldChange] of fields) {
730
+ const keys = getChangeHandler(this.fieldKinds, fieldChange.fieldKind).getCrossFieldKeys(fieldChange.change);
731
+ for (const key of keys) {
732
+ table.set(key, { nodeId: parent, field: fieldKey });
733
+ }
734
+ }
735
+ }
482
736
  buildEditor(changeReceiver) {
483
- return new ModularEditBuilder(this, changeReceiver);
737
+ return new ModularEditBuilder(this, this.fieldKinds, changeReceiver);
738
+ }
739
+ createEmptyFieldChange(fieldKind) {
740
+ const emptyChange = getChangeHandler(this.fieldKinds, fieldKind).createEmpty();
741
+ return { fieldKind, change: (0, index_js_2.brand)(emptyChange) };
484
742
  }
485
743
  }
486
744
  exports.ModularChangeFamily = ModularChangeFamily;
487
745
  ModularChangeFamily.emptyChange = makeModularChangeset();
746
+ function replaceCrossFieldKeyTableRevisions(table, oldRevisions, newRevision, nodeAliases) {
747
+ const updated = newBTree();
748
+ table.forEachPair(([target, revision, id, count], field) => {
749
+ const updatedKey = [
750
+ target,
751
+ replaceRevision(revision, oldRevisions, newRevision),
752
+ id,
753
+ count,
754
+ ];
755
+ const normalizedFieldId = normalizeFieldId(field, nodeAliases);
756
+ const updatedNodeId = normalizedFieldId.nodeId !== undefined
757
+ ? (0, index_js_1.replaceAtomRevisions)(normalizedFieldId.nodeId, oldRevisions, newRevision)
758
+ : undefined;
759
+ const updatedValue = {
760
+ ...normalizedFieldId,
761
+ nodeId: updatedNodeId,
762
+ };
763
+ updated.set(updatedKey, updatedValue);
764
+ });
765
+ return updated;
766
+ }
488
767
  function replaceRevision(revision, oldRevisions, newRevision) {
489
768
  return oldRevisions.has(revision) ? newRevision : revision;
490
769
  }
@@ -600,8 +879,7 @@ function* relevantRemovedRootsFromFields(change, nodeChanges, fieldKinds) {
600
879
  for (const [_, fieldChange] of change) {
601
880
  const handler = getChangeHandler(fieldKinds, fieldChange.fieldKind);
602
881
  const delegate = function* (node) {
603
- const nodeChangeset = (0, index_js_2.tryGetFromNestedMap)(nodeChanges, node.revision, node.localId);
604
- (0, internal_1.assert)(nodeChangeset !== undefined, 0x931 /* Unknown node ID */);
882
+ const nodeChangeset = nodeChangeFromId(nodeChanges, node);
605
883
  if (nodeChangeset.fieldChanges !== undefined) {
606
884
  yield* relevantRemovedRootsFromFields(nodeChangeset.fieldChanges, nodeChanges, fieldKinds);
607
885
  }
@@ -655,7 +933,7 @@ function updateRefreshers(change, getDetachedNode, removedRoots, requireRefreshe
655
933
  }
656
934
  }
657
935
  const { fieldChanges, nodeChanges, maxId, revisions, constraintViolationCount, builds, destroys, } = change;
658
- return makeModularChangeset(fieldChanges, nodeChanges, maxId, revisions, constraintViolationCount, builds, destroys, refreshers);
936
+ return makeModularChangeset(fieldChanges, nodeChanges, change.nodeToParent, change.nodeAliases, change.crossFieldKeys, maxId, revisions, constraintViolationCount, builds, destroys, refreshers);
659
937
  }
660
938
  exports.updateRefreshers = updateRefreshers;
661
939
  /**
@@ -713,8 +991,7 @@ function intoDeltaImpl(change, nodeChanges, idAllocator, fieldKinds) {
713
991
  const delta = new Map();
714
992
  for (const [field, fieldChange] of change) {
715
993
  const deltaField = getChangeHandler(fieldKinds, fieldChange.fieldKind).intoDelta(fieldChange.change, (childChange) => {
716
- const nodeChange = (0, index_js_2.tryGetFromNestedMap)(nodeChanges, childChange.revision, childChange.localId);
717
- (0, internal_1.assert)(nodeChange !== undefined, 0x932 /* Unknown node ID */);
994
+ const nodeChange = nodeChangeFromId(nodeChanges, childChange);
718
995
  return deltaFromNodeChange(nodeChange, nodeChanges, idAllocator, fieldKinds);
719
996
  }, idAllocator);
720
997
  if (!(0, index_js_1.isEmptyFieldChanges)(deltaField)) {
@@ -774,12 +1051,20 @@ function getChangeHandler(fieldKinds, kind) {
774
1051
  return getFieldKind(fieldKinds, kind).changeHandler;
775
1052
  }
776
1053
  exports.getChangeHandler = getChangeHandler;
777
- function newComposeTable() {
1054
+ function newComposeTable(baseChange, newChange) {
778
1055
  return {
779
1056
  ...newCrossFieldTable(),
1057
+ baseChange,
1058
+ newChange,
780
1059
  fieldToContext: new Map(),
781
- nodeIds: new Map(),
782
- nodeIdPairs: [],
1060
+ newFieldToBaseField: new Map(),
1061
+ newToBaseNodeId: new Map(),
1062
+ composedNodes: new Set(),
1063
+ pendingCompositions: {
1064
+ nodeIdsToCompose: [],
1065
+ affectedBaseFields: newBTree(),
1066
+ affectedNewFields: newBTree(),
1067
+ },
783
1068
  };
784
1069
  }
785
1070
  function newCrossFieldTable() {
@@ -796,43 +1081,148 @@ function newConstraintState(violationCount) {
796
1081
  violationCount,
797
1082
  };
798
1083
  }
799
- function newCrossFieldManager(crossFieldTable, currentFieldKey, allowInval = true) {
800
- const getMap = (target) => target === crossFieldQueries_js_1.CrossFieldTarget.Source ? crossFieldTable.srcTable : crossFieldTable.dstTable;
801
- const getDependents = (target) => target === crossFieldQueries_js_1.CrossFieldTarget.Source
802
- ? crossFieldTable.srcDependents
803
- : crossFieldTable.dstDependents;
804
- const manager = {
805
- table: crossFieldTable,
806
- set: (target, revision, id, count, newValue, invalidateDependents) => {
807
- if (invalidateDependents && allowInval) {
808
- const lastChangedId = id + count - 1;
809
- let firstId = id;
810
- while (firstId <= lastChangedId) {
811
- const dependentEntry = (0, crossFieldQueries_js_1.getFirstFromCrossFieldMap)(getDependents(target), revision, firstId, lastChangedId - firstId + 1);
812
- if (dependentEntry.value !== undefined) {
813
- crossFieldTable.invalidatedFields.add(dependentEntry.value);
814
- }
815
- firstId = (0, index_js_2.brand)(firstId + dependentEntry.length);
1084
+ class CrossFieldManagerI {
1085
+ constructor(crossFieldTable, currentFieldKey, allowInval = true) {
1086
+ this.crossFieldTable = crossFieldTable;
1087
+ this.currentFieldKey = currentFieldKey;
1088
+ this.allowInval = allowInval;
1089
+ }
1090
+ set(target, revision, id, count, newValue, invalidateDependents) {
1091
+ if (invalidateDependents && this.allowInval) {
1092
+ const lastChangedId = id + count - 1;
1093
+ let firstId = id;
1094
+ while (firstId <= lastChangedId) {
1095
+ const dependentEntry = (0, crossFieldQueries_js_1.getFirstFromCrossFieldMap)(this.getDependents(target), revision, firstId, lastChangedId - firstId + 1);
1096
+ if (dependentEntry.value !== undefined) {
1097
+ this.crossFieldTable.invalidatedFields.add(dependentEntry.value);
816
1098
  }
1099
+ firstId = (0, index_js_2.brand)(firstId + dependentEntry.length);
817
1100
  }
818
- (0, crossFieldQueries_js_1.setInCrossFieldMap)(getMap(target), revision, id, count, newValue);
819
- },
820
- get: (target, revision, id, count, addDependency) => {
821
- if (addDependency) {
822
- // We assume that if there is already an entry for this ID it is because
823
- // a field handler has called compose on the same node multiple times.
824
- // In this case we only want to update the latest version, so we overwrite the dependency.
825
- (0, crossFieldQueries_js_1.setInCrossFieldMap)(getDependents(target), revision, id, count, currentFieldKey);
1101
+ }
1102
+ (0, crossFieldQueries_js_1.setInCrossFieldMap)(this.getMap(target), revision, id, count, newValue);
1103
+ }
1104
+ get(target, revision, id, count, addDependency) {
1105
+ if (addDependency) {
1106
+ // We assume that if there is already an entry for this ID it is because
1107
+ // a field handler has called compose on the same node multiple times.
1108
+ // In this case we only want to update the latest version, so we overwrite the dependency.
1109
+ (0, crossFieldQueries_js_1.setInCrossFieldMap)(this.getDependents(target), revision, id, count, this.currentFieldKey);
1110
+ }
1111
+ return (0, crossFieldQueries_js_1.getFirstFromCrossFieldMap)(this.getMap(target), revision, id, count);
1112
+ }
1113
+ getMap(target) {
1114
+ return target === crossFieldQueries_js_1.CrossFieldTarget.Source
1115
+ ? this.crossFieldTable.srcTable
1116
+ : this.crossFieldTable.dstTable;
1117
+ }
1118
+ getDependents(target) {
1119
+ return target === crossFieldQueries_js_1.CrossFieldTarget.Source
1120
+ ? this.crossFieldTable.srcDependents
1121
+ : this.crossFieldTable.dstDependents;
1122
+ }
1123
+ }
1124
+ class InvertManager extends CrossFieldManagerI {
1125
+ constructor(table, field, fieldId, allowInval = true) {
1126
+ super(table, field, allowInval);
1127
+ this.fieldId = fieldId;
1128
+ }
1129
+ onMoveIn(id) {
1130
+ (0, index_js_1.setInChangeAtomIdMap)(this.table.invertedNodeToParent, id, this.fieldId);
1131
+ }
1132
+ moveKey(target, revision, id, count) {
1133
+ (0, internal_1.assert)(false, "Keys should not be moved manually during invert");
1134
+ }
1135
+ get table() {
1136
+ return this.crossFieldTable;
1137
+ }
1138
+ }
1139
+ class RebaseManager extends CrossFieldManagerI {
1140
+ constructor(table, currentField, fieldId, allowInval = true) {
1141
+ super(table, currentField, allowInval);
1142
+ this.fieldId = fieldId;
1143
+ }
1144
+ set(target, revision, id, count, newValue, invalidateDependents) {
1145
+ if (invalidateDependents && this.allowInval) {
1146
+ const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, [
1147
+ target,
1148
+ revision,
1149
+ id,
1150
+ count,
1151
+ ]);
1152
+ (0, internal_1.assert)(newFieldIds.length === 0, "TODO: Modifying a cross-field key from the new changeset is currently unsupported");
1153
+ const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, [
1154
+ target,
1155
+ revision,
1156
+ id,
1157
+ count,
1158
+ ]);
1159
+ (0, internal_1.assert)(baseFieldIds.length > 0, "Cross field key not registered in base or new change");
1160
+ for (const baseFieldId of baseFieldIds) {
1161
+ this.table.affectedBaseFields.set([baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field], true);
826
1162
  }
827
- return (0, crossFieldQueries_js_1.getFirstFromCrossFieldMap)(getMap(target), revision, id, count);
828
- },
829
- };
830
- return manager;
1163
+ }
1164
+ super.set(target, revision, id, count, newValue, invalidateDependents);
1165
+ }
1166
+ onMoveIn(id) {
1167
+ (0, index_js_1.setInChangeAtomIdMap)(this.table.rebasedNodeToParent, id, this.fieldId);
1168
+ }
1169
+ moveKey(target, revision, id, count) {
1170
+ setInCrossFieldKeyTable(this.table.rebasedCrossFieldKeys, target, revision, id, count, this.fieldId);
1171
+ }
1172
+ get table() {
1173
+ return this.crossFieldTable;
1174
+ }
1175
+ }
1176
+ // TODO: Deduplicate this with RebaseTable
1177
+ class ComposeManager extends CrossFieldManagerI {
1178
+ constructor(table, currentField, allowInval = true) {
1179
+ super(table, currentField, allowInval);
1180
+ }
1181
+ set(target, revision, id, count, newValue, invalidateDependents) {
1182
+ if (invalidateDependents && this.allowInval) {
1183
+ const newFieldIds = getFieldsForCrossFieldKey(this.table.newChange, [
1184
+ target,
1185
+ revision,
1186
+ id,
1187
+ count,
1188
+ ]);
1189
+ if (newFieldIds.length > 0) {
1190
+ for (const newFieldId of newFieldIds) {
1191
+ this.table.pendingCompositions.affectedNewFields.set([newFieldId.nodeId?.revision, newFieldId.nodeId?.localId, newFieldId.field], true);
1192
+ }
1193
+ }
1194
+ else {
1195
+ const baseFieldIds = getFieldsForCrossFieldKey(this.table.baseChange, [
1196
+ target,
1197
+ revision,
1198
+ id,
1199
+ count,
1200
+ ]);
1201
+ (0, internal_1.assert)(baseFieldIds.length > 0, "Cross field key not registered in base or new change");
1202
+ for (const baseFieldId of baseFieldIds) {
1203
+ this.table.pendingCompositions.affectedBaseFields.set([baseFieldId.nodeId?.revision, baseFieldId.nodeId?.localId, baseFieldId.field], true);
1204
+ }
1205
+ }
1206
+ }
1207
+ super.set(target, revision, id, count, newValue, invalidateDependents);
1208
+ }
1209
+ onMoveIn(id) {
1210
+ throw new Error("Method not implemented.");
1211
+ }
1212
+ moveKey(target, revision, id, count) {
1213
+ throw new Error("Method not implemented.");
1214
+ }
1215
+ get table() {
1216
+ return this.crossFieldTable;
1217
+ }
831
1218
  }
832
- function makeModularChangeset(fieldChanges = undefined, nodeChanges = undefined, maxId = -1, revisions = undefined, constraintViolationCount = undefined, builds, destroys, refreshers) {
1219
+ function makeModularChangeset(fieldChanges = undefined, nodeChanges = undefined, nodeToParent = undefined, nodeAliases = undefined, crossFieldKeys = undefined, maxId = -1, revisions = undefined, constraintViolationCount = undefined, builds, destroys, refreshers) {
833
1220
  const changeset = {
834
1221
  fieldChanges: fieldChanges ?? new Map(),
835
1222
  nodeChanges: nodeChanges ?? new Map(),
1223
+ nodeToParent: nodeToParent ?? new Map(),
1224
+ nodeAliases: nodeAliases ?? new Map(),
1225
+ crossFieldKeys: crossFieldKeys ?? newCrossFieldKeyTable(),
836
1226
  };
837
1227
  if (revisions !== undefined && revisions.length > 0) {
838
1228
  changeset.revisions = revisions;
@@ -855,8 +1245,9 @@ function makeModularChangeset(fieldChanges = undefined, nodeChanges = undefined,
855
1245
  return changeset;
856
1246
  }
857
1247
  class ModularEditBuilder extends index_js_1.EditBuilder {
858
- constructor(family, changeReceiver) {
1248
+ constructor(family, fieldKinds, changeReceiver) {
859
1249
  super(family, changeReceiver);
1250
+ this.fieldKinds = fieldKinds;
860
1251
  this.transactionDepth = 0;
861
1252
  this.idAllocator = (0, index_js_2.idAllocatorFromMaxId)();
862
1253
  }
@@ -902,7 +1293,8 @@ class ModularEditBuilder extends index_js_1.EditBuilder {
902
1293
  * @param maxId - the highest `ChangesetLocalId` used in this change
903
1294
  */
904
1295
  submitChange(field, fieldKind, change) {
905
- const modularChange = buildModularChangesetFromField(field, { fieldKind, change }, new Map(), this.idAllocator);
1296
+ const crossFieldKeys = getChangeHandler(this.fieldKinds, fieldKind).getCrossFieldKeys(change);
1297
+ const modularChange = buildModularChangesetFromField(field, { fieldKind, change }, new Map(), new Map(), newCrossFieldKeyTable(), this.idAllocator, crossFieldKeys);
906
1298
  this.applyChange(modularChange);
907
1299
  }
908
1300
  submitChanges(changes) {
@@ -911,11 +1303,11 @@ class ModularEditBuilder extends index_js_1.EditBuilder {
911
1303
  }
912
1304
  buildChanges(changes) {
913
1305
  const changeMaps = changes.map((change) => (0, index_js_1.makeAnonChange)(change.type === "global"
914
- ? makeModularChangeset(undefined, undefined, this.idAllocator.getMaxId(), undefined, undefined, change.builds)
1306
+ ? makeModularChangeset(undefined, undefined, undefined, undefined, undefined, this.idAllocator.getMaxId(), undefined, undefined, change.builds)
915
1307
  : buildModularChangesetFromField(change.field, {
916
1308
  fieldKind: change.fieldKind,
917
1309
  change: change.change,
918
- }, new Map(), this.idAllocator)));
1310
+ }, new Map(), new Map(), newCrossFieldKeyTable(), this.idAllocator, getChangeHandler(this.fieldKinds, change.fieldKind).getCrossFieldKeys(change.change))));
919
1311
  const composedChange = this.changeFamily.rebaser.compose(changeMaps);
920
1312
  const maxId = (0, index_js_2.brand)(this.idAllocator.getMaxId());
921
1313
  if (maxId >= 0) {
@@ -930,29 +1322,47 @@ class ModularEditBuilder extends index_js_1.EditBuilder {
930
1322
  const nodeChange = {
931
1323
  nodeExistsConstraint: { violated: false },
932
1324
  };
933
- this.applyChange(buildModularChangesetFromNode(path, nodeChange, new Map(), this.idAllocator));
1325
+ this.applyChange(buildModularChangesetFromNode(path, nodeChange, new Map(), new Map(), newCrossFieldKeyTable(), this.idAllocator));
934
1326
  }
935
1327
  }
936
1328
  exports.ModularEditBuilder = ModularEditBuilder;
937
- function buildModularChangesetFromField(path, fieldChange, nodeChanges, idAllocator = (0, index_js_2.idAllocatorFromMaxId)()) {
1329
+ function buildModularChangesetFromField(path, fieldChange, nodeChanges, nodeToParent, crossFieldKeys, idAllocator = (0, index_js_2.idAllocatorFromMaxId)(), localCrossFieldKeys = [], childId = undefined) {
938
1330
  const fieldChanges = new Map([[path.field, fieldChange]]);
939
1331
  if (path.parent === undefined) {
940
- return makeModularChangeset(fieldChanges, nodeChanges, idAllocator.getMaxId());
1332
+ for (const key of localCrossFieldKeys) {
1333
+ crossFieldKeys.set(key, { nodeId: undefined, field: path.field });
1334
+ }
1335
+ if (childId !== undefined) {
1336
+ (0, index_js_1.setInChangeAtomIdMap)(nodeToParent, childId, {
1337
+ nodeId: undefined,
1338
+ field: path.field,
1339
+ });
1340
+ }
1341
+ return makeModularChangeset(fieldChanges, nodeChanges, nodeToParent, undefined, crossFieldKeys, idAllocator.getMaxId());
941
1342
  }
942
1343
  const nodeChangeset = {
943
1344
  fieldChanges,
944
1345
  };
945
- return buildModularChangesetFromNode(path.parent, nodeChangeset, nodeChanges, idAllocator);
1346
+ const parentId = { localId: (0, index_js_2.brand)(idAllocator.allocate()) };
1347
+ for (const key of localCrossFieldKeys) {
1348
+ crossFieldKeys.set(key, { nodeId: parentId, field: path.field });
1349
+ }
1350
+ if (childId !== undefined) {
1351
+ (0, index_js_1.setInChangeAtomIdMap)(nodeToParent, childId, {
1352
+ nodeId: parentId,
1353
+ field: path.field,
1354
+ });
1355
+ }
1356
+ return buildModularChangesetFromNode(path.parent, nodeChangeset, nodeChanges, nodeToParent, crossFieldKeys, idAllocator, parentId);
946
1357
  }
947
- function buildModularChangesetFromNode(path, nodeChange, nodeChanges, idAllocator) {
948
- const nodeId = { localId: (0, index_js_2.brand)(idAllocator.allocate()) };
949
- (0, index_js_2.setInNestedMap)(nodeChanges, nodeId.revision, nodeId.localId, nodeChange);
1358
+ function buildModularChangesetFromNode(path, nodeChange, nodeChanges, nodeToParent, crossFieldKeys, idAllocator, nodeId = { localId: (0, index_js_2.brand)(idAllocator.allocate()) }) {
1359
+ (0, index_js_1.setInChangeAtomIdMap)(nodeChanges, nodeId, nodeChange);
950
1360
  const fieldChangeset = genericFieldKind_js_1.genericFieldKind.changeHandler.editor.buildChildChange(path.parentIndex, nodeId);
951
1361
  const fieldChange = {
952
1362
  fieldKind: genericFieldKind_js_1.genericFieldKind.identifier,
953
1363
  change: fieldChangeset,
954
1364
  };
955
- return buildModularChangesetFromField({ parent: path.parent, field: path.parentField }, fieldChange, nodeChanges, idAllocator);
1365
+ return buildModularChangesetFromField({ parent: path.parent, field: path.parentField }, fieldChange, nodeChanges, nodeToParent, crossFieldKeys, idAllocator, [], nodeId);
956
1366
  }
957
1367
  function getRevInfoFromTaggedChanges(changes) {
958
1368
  let maxId = -1;
@@ -1001,9 +1411,184 @@ function revisionFromRevInfos(revInfos) {
1001
1411
  }
1002
1412
  return revInfos[0].revision;
1003
1413
  }
1004
- function getActiveFieldChanges(changes) {
1005
- return (changes.constraintViolationCount ?? 0) === 0
1006
- ? changes.fieldChanges
1007
- : new Map();
1414
+ function mergeBTrees(tree1, tree2) {
1415
+ const result = tree1.clone();
1416
+ tree2.forEachPair((k, v) => {
1417
+ result.set(k, v);
1418
+ });
1419
+ return result;
1420
+ }
1421
+ function mergeNestedMaps(map1, map2) {
1422
+ const merged = new Map();
1423
+ (0, index_js_2.populateNestedMap)(map1, merged, true);
1424
+ (0, index_js_2.populateNestedMap)(map2, merged, true);
1425
+ return merged;
1426
+ }
1427
+ function fieldChangeFromId(fields, nodes, id) {
1428
+ const fieldMap = fieldMapFromNodeId(fields, nodes, id.nodeId);
1429
+ return fieldMap.get(id.field) ?? (0, index_js_2.fail)("No field exists for the given ID");
1430
+ }
1431
+ function fieldMapFromNodeId(rootFieldMap, nodes, nodeId) {
1432
+ if (nodeId === undefined) {
1433
+ return rootFieldMap;
1434
+ }
1435
+ const node = nodeChangeFromId(nodes, nodeId);
1436
+ (0, internal_1.assert)(node.fieldChanges !== undefined, "Expected node to have field changes");
1437
+ return node.fieldChanges;
1438
+ }
1439
+ function rebasedFieldIdFromBaseId(table, baseId) {
1440
+ if (baseId.nodeId === undefined) {
1441
+ return baseId;
1442
+ }
1443
+ return { ...baseId, nodeId: rebasedNodeIdFromBaseNodeId(table, baseId.nodeId) };
1444
+ }
1445
+ function rebasedNodeIdFromBaseNodeId(table, baseId) {
1446
+ return (0, index_js_1.getFromChangeAtomIdMap)(table.baseToRebasedNodeId, baseId) ?? baseId;
1447
+ }
1448
+ function nodeChangeFromId(nodes, id) {
1449
+ const node = (0, index_js_1.getFromChangeAtomIdMap)(nodes, id);
1450
+ (0, internal_1.assert)(node !== undefined, "Unknown node ID");
1451
+ return node;
1452
+ }
1453
+ function fieldIdFromFieldIdKey([revision, localId, field]) {
1454
+ const nodeId = localId !== undefined ? { revision, localId } : undefined;
1455
+ return { nodeId, field };
1456
+ }
1457
+ function cloneNodeChangeset(nodeChangeset) {
1458
+ if (nodeChangeset.fieldChanges !== undefined) {
1459
+ return { ...nodeChangeset, fieldChanges: new Map(nodeChangeset.fieldChanges) };
1460
+ }
1461
+ return { ...nodeChangeset };
1462
+ }
1463
+ function replaceFieldIdRevision(fieldId, oldRevisions, newRevision) {
1464
+ if (fieldId.nodeId === undefined) {
1465
+ return fieldId;
1466
+ }
1467
+ return {
1468
+ ...fieldId,
1469
+ nodeId: (0, index_js_1.replaceAtomRevisions)(fieldId.nodeId, oldRevisions, newRevision),
1470
+ };
1471
+ }
1472
+ function getParentFieldId(changeset, nodeId) {
1473
+ const parentId = (0, index_js_1.getFromChangeAtomIdMap)(changeset.nodeToParent, nodeId);
1474
+ (0, internal_1.assert)(parentId !== undefined, "Parent field should be defined");
1475
+ return normalizeFieldId(parentId, changeset.nodeAliases);
1476
+ }
1477
+ exports.getParentFieldId = getParentFieldId;
1478
+ function getFieldsForCrossFieldKey(changeset, [target, revision, id, count]) {
1479
+ let firstLocalId = id;
1480
+ const lastLocalId = id + count - 1;
1481
+ const fields = [];
1482
+ // eslint-disable-next-line no-constant-condition
1483
+ while (true) {
1484
+ const entry = getFirstIntersectingCrossFieldEntry(changeset.crossFieldKeys, [
1485
+ target,
1486
+ revision,
1487
+ (0, index_js_2.brand)(firstLocalId),
1488
+ count,
1489
+ ]);
1490
+ if (entry === undefined) {
1491
+ return fields;
1492
+ }
1493
+ const [[_target, _revision, entryId, entryCount], fieldId] = entry;
1494
+ fields.push(normalizeFieldId(fieldId, changeset.nodeAliases));
1495
+ const entryLastId = entryId + entryCount - 1;
1496
+ if (entryLastId >= lastLocalId) {
1497
+ return fields;
1498
+ }
1499
+ firstLocalId = entryLastId + 1;
1500
+ }
1501
+ }
1502
+ exports.getFieldsForCrossFieldKey = getFieldsForCrossFieldKey;
1503
+ function getFirstIntersectingCrossFieldEntry(table, [target, revision, id, count]) {
1504
+ const entry = table.nextLowerPair([target, revision, id, Infinity]);
1505
+ if (entry === undefined) {
1506
+ return undefined;
1507
+ }
1508
+ const [entryTarget, entryRevision, entryId, entryCount] = entry[0];
1509
+ if (entryTarget !== target || entryRevision !== revision) {
1510
+ return undefined;
1511
+ }
1512
+ const lastQueryId = id + count - 1;
1513
+ const entryLastId = entryId + entryCount - 1;
1514
+ if (entryId > lastQueryId || entryLastId < id) {
1515
+ return undefined;
1516
+ }
1517
+ return entry;
1518
+ }
1519
+ function setInCrossFieldKeyTable(table, target, revision, id, count, value) {
1520
+ let entry = getFirstIntersectingCrossFieldEntry(table, [target, revision, id, count]);
1521
+ const lastQueryId = id + count - 1;
1522
+ while (entry !== undefined) {
1523
+ const [entryKey, entryValue] = entry;
1524
+ table.delete(entryKey);
1525
+ const [_target, _revision, entryId, entryCount] = entryKey;
1526
+ if (entryId < id) {
1527
+ table.set([target, revision, entryId, id - entryId], entryValue);
1528
+ }
1529
+ const lastEntryId = entryId + entryCount - 1;
1530
+ if (lastEntryId > lastQueryId) {
1531
+ table.set([target, revision, (0, index_js_2.brand)(lastQueryId + 1), lastEntryId - lastQueryId], entryValue);
1532
+ break;
1533
+ }
1534
+ const nextId = (0, index_js_2.brand)(lastEntryId + 1);
1535
+ entry = getFirstIntersectingCrossFieldEntry(table, [
1536
+ target,
1537
+ revision,
1538
+ nextId,
1539
+ lastQueryId - nextId + 1,
1540
+ ]);
1541
+ }
1542
+ table.set([target, revision, id, count], value);
1543
+ }
1544
+ function normalizeFieldId(fieldId, nodeAliases) {
1545
+ return fieldId.nodeId !== undefined
1546
+ ? { ...fieldId, nodeId: normalizeNodeId(fieldId.nodeId, nodeAliases) }
1547
+ : fieldId;
1548
+ }
1549
+ /**
1550
+ * @returns The canonical form of nodeId, according to nodeAliases
1551
+ */
1552
+ function normalizeNodeId(nodeId, nodeAliases) {
1553
+ let currentId = nodeId;
1554
+ // eslint-disable-next-line no-constant-condition
1555
+ while (true) {
1556
+ const dealiased = (0, index_js_1.getFromChangeAtomIdMap)(nodeAliases, currentId);
1557
+ if (dealiased === undefined) {
1558
+ return currentId;
1559
+ }
1560
+ currentId = dealiased;
1561
+ }
1562
+ }
1563
+ function hasConflicts(change) {
1564
+ return (change.constraintViolationCount ?? 0) > 0;
1565
+ }
1566
+ function newCrossFieldKeyTable() {
1567
+ return newBTree();
1568
+ }
1569
+ exports.newCrossFieldKeyTable = newCrossFieldKeyTable;
1570
+ function newBTree() {
1571
+ return (0, index_js_2.brand)(new sorted_btree_es6_1.BTree(undefined, compareTuples));
1572
+ }
1573
+ // This assumes that the arrays are the same length.
1574
+ function compareTuples(arrayA, arrayB) {
1575
+ for (let i = 0; i < arrayA.length; i++) {
1576
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1577
+ const a = arrayA[i];
1578
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1579
+ const b = arrayB[i];
1580
+ if (a < b) {
1581
+ return -1;
1582
+ }
1583
+ else if (a > b) {
1584
+ return 1;
1585
+ }
1586
+ }
1587
+ return 0;
1588
+ }
1589
+ function cloneNestedMap(map) {
1590
+ const cloned = new Map();
1591
+ (0, index_js_2.populateNestedMap)(map, cloned, true);
1592
+ return cloned;
1008
1593
  }
1009
1594
  //# sourceMappingURL=modularChangeFamily.js.map