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