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