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