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