@fluidframework/tree 2.115.0 → 2.116.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/README.md +5 -3
- package/api-report/tree.alpha.api.md +4 -2
- package/api-report/tree.beta.api.md +1 -1
- package/api-report/tree.legacy.beta.api.md +1 -1
- package/dist/codec/codec.d.ts +6 -6
- package/dist/codec/codec.d.ts.map +1 -1
- package/dist/codec/codec.js +3 -3
- package/dist/codec/codec.js.map +1 -1
- package/dist/codec/versioned/codec.d.ts +3 -3
- package/dist/codec/versioned/codec.d.ts.map +1 -1
- package/dist/codec/versioned/codec.js.map +1 -1
- package/dist/core/forest/editableForest.d.ts +2 -0
- package/dist/core/forest/editableForest.d.ts.map +1 -1
- 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 +2 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/tree/index.d.ts +1 -1
- package/dist/core/tree/index.d.ts.map +1 -1
- package/dist/core/tree/index.js +2 -2
- package/dist/core/tree/index.js.map +1 -1
- package/dist/core/tree/visitorUtils.d.ts +24 -3
- package/dist/core/tree/visitorUtils.d.ts.map +1 -1
- package/dist/core/tree/visitorUtils.js +24 -6
- package/dist/core/tree/visitorUtils.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts +30 -6
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +146 -54
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/comparison-forest/comparisonForest.d.ts +2 -1
- package/dist/feature-libraries/comparison-forest/comparisonForest.d.ts.map +1 -1
- package/dist/feature-libraries/comparison-forest/comparisonForest.js +87 -70
- package/dist/feature-libraries/comparison-forest/comparisonForest.js.map +1 -1
- package/dist/feature-libraries/detachedFieldIndexSummarizer.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts +5 -0
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +127 -107
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/forest-summary/summaryTypes.d.ts +2 -2
- package/dist/feature-libraries/forest-summary/summaryTypes.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/summaryTypes.js.map +1 -1
- package/dist/feature-libraries/indexing/anchorTreeIndex.js +1 -1
- package/dist/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
- package/dist/feature-libraries/modular-schema/invert.js +1 -1
- package/dist/feature-libraries/modular-schema/invert.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +2 -11
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeUtils.d.ts +11 -16
- package/dist/feature-libraries/modular-schema/modularChangeUtils.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeUtils.js +10 -3
- package/dist/feature-libraries/modular-schema/modularChangeUtils.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +34 -13
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts +2 -2
- package/dist/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
- package/dist/feature-libraries/sequence-field/rebase.js +1 -1
- package/dist/feature-libraries/sequence-field/rebase.js.map +1 -1
- package/dist/feature-libraries/sequence-field/utils.js +1 -1
- package/dist/feature-libraries/sequence-field/utils.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/shared-tree/independentView.d.ts.map +1 -1
- package/dist/shared-tree/independentView.js +2 -4
- package/dist/shared-tree/independentView.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +30 -5
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +8 -8
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.d.ts +2 -2
- package/dist/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.d.ts +25 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js +24 -0
- package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +21 -10
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +48 -16
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/branchCommitEnricher.d.ts +2 -1
- package/dist/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
- package/dist/shared-tree-core/branchCommitEnricher.js +3 -2
- package/dist/shared-tree-core/branchCommitEnricher.js.map +1 -1
- package/dist/shared-tree-core/changeEnricher.d.ts +3 -2
- package/dist/shared-tree-core/changeEnricher.d.ts.map +1 -1
- package/dist/shared-tree-core/changeEnricher.js.map +1 -1
- package/dist/shared-tree-core/defaultResubmitMachine.d.ts +9 -1
- package/dist/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
- package/dist/shared-tree-core/defaultResubmitMachine.js +7 -2
- package/dist/shared-tree-core/defaultResubmitMachine.js.map +1 -1
- package/dist/shared-tree-core/editManager.d.ts.map +1 -1
- package/dist/shared-tree-core/editManager.js +2 -2
- package/dist/shared-tree-core/editManager.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.d.ts +2 -2
- package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/dist/shared-tree-core/editManagerSummarizer.d.ts +2 -2
- package/dist/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerSummarizer.js.map +1 -1
- package/dist/shared-tree-core/messageCodecs.d.ts +2 -2
- package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecs.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts +11 -2
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +11 -8
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/shared-tree-core/summaryTypes.d.ts +2 -2
- package/dist/shared-tree-core/summaryTypes.d.ts.map +1 -1
- package/dist/shared-tree-core/summaryTypes.js.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/dist/simple-tree/api/storedSchema.d.ts +2 -2
- package/dist/simple-tree/api/storedSchema.d.ts.map +1 -1
- package/dist/simple-tree/api/storedSchema.js.map +1 -1
- package/dist/simple-tree/api/treeChangeEvents.d.ts +5 -1
- package/dist/simple-tree/api/treeChangeEvents.d.ts.map +1 -1
- package/dist/simple-tree/api/treeChangeEvents.js.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +10 -0
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.js +19 -2
- package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/dist/tableSchema.d.ts +1 -1
- package/dist/tableSchema.js +1 -1
- package/dist/tableSchema.js.map +1 -1
- package/dist/treeFactory.d.ts.map +1 -1
- package/dist/treeFactory.js +1 -1
- package/dist/treeFactory.js.map +1 -1
- package/dist/util/breakable.d.ts +14 -2
- package/dist/util/breakable.d.ts.map +1 -1
- package/dist/util/breakable.js +9 -1
- package/dist/util/breakable.js.map +1 -1
- package/dist/util/compressedIds.d.ts +2 -2
- package/dist/util/compressedIds.d.ts.map +1 -1
- package/dist/util/compressedIds.js +1 -6
- package/dist/util/compressedIds.js.map +1 -1
- package/lib/codec/codec.d.ts +6 -6
- package/lib/codec/codec.d.ts.map +1 -1
- package/lib/codec/codec.js +3 -3
- package/lib/codec/codec.js.map +1 -1
- package/lib/codec/versioned/codec.d.ts +3 -3
- package/lib/codec/versioned/codec.d.ts.map +1 -1
- package/lib/codec/versioned/codec.js.map +1 -1
- package/lib/core/forest/editableForest.d.ts +2 -0
- package/lib/core/forest/editableForest.d.ts.map +1 -1
- 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/tree/index.d.ts +1 -1
- package/lib/core/tree/index.d.ts.map +1 -1
- package/lib/core/tree/index.js +1 -1
- package/lib/core/tree/index.js.map +1 -1
- package/lib/core/tree/visitorUtils.d.ts +24 -3
- package/lib/core/tree/visitorUtils.d.ts.map +1 -1
- package/lib/core/tree/visitorUtils.js +22 -4
- package/lib/core/tree/visitorUtils.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts +30 -6
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +146 -56
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/comparison-forest/comparisonForest.d.ts +2 -1
- package/lib/feature-libraries/comparison-forest/comparisonForest.d.ts.map +1 -1
- package/lib/feature-libraries/comparison-forest/comparisonForest.js +88 -70
- package/lib/feature-libraries/comparison-forest/comparisonForest.js.map +1 -1
- package/lib/feature-libraries/detachedFieldIndexSummarizer.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts +5 -0
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +129 -108
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/forest-summary/summaryTypes.d.ts +2 -2
- package/lib/feature-libraries/forest-summary/summaryTypes.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/summaryTypes.js.map +1 -1
- package/lib/feature-libraries/indexing/anchorTreeIndex.js +1 -1
- package/lib/feature-libraries/indexing/anchorTreeIndex.js.map +1 -1
- package/lib/feature-libraries/modular-schema/invert.js +2 -2
- package/lib/feature-libraries/modular-schema/invert.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +2 -11
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeUtils.d.ts +11 -16
- package/lib/feature-libraries/modular-schema/modularChangeUtils.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeUtils.js +10 -3
- package/lib/feature-libraries/modular-schema/modularChangeUtils.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +35 -14
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts +2 -2
- package/lib/feature-libraries/schema-index/schemaSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
- package/lib/feature-libraries/sequence-field/rebase.js +2 -2
- package/lib/feature-libraries/sequence-field/rebase.js.map +1 -1
- package/lib/feature-libraries/sequence-field/utils.js +1 -1
- package/lib/feature-libraries/sequence-field/utils.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/shared-tree/independentView.d.ts.map +1 -1
- package/lib/shared-tree/independentView.js +3 -5
- package/lib/shared-tree/independentView.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +30 -5
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +8 -8
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.d.ts +2 -2
- package/lib/shared-tree/sharedTreeChangeCodecs.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.d.ts +25 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.d.ts.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js +24 -0
- package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +21 -10
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +48 -16
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/branchCommitEnricher.d.ts +2 -1
- package/lib/shared-tree-core/branchCommitEnricher.d.ts.map +1 -1
- package/lib/shared-tree-core/branchCommitEnricher.js +3 -2
- package/lib/shared-tree-core/branchCommitEnricher.js.map +1 -1
- package/lib/shared-tree-core/changeEnricher.d.ts +3 -2
- package/lib/shared-tree-core/changeEnricher.d.ts.map +1 -1
- package/lib/shared-tree-core/changeEnricher.js.map +1 -1
- package/lib/shared-tree-core/defaultResubmitMachine.d.ts +9 -1
- package/lib/shared-tree-core/defaultResubmitMachine.d.ts.map +1 -1
- package/lib/shared-tree-core/defaultResubmitMachine.js +7 -2
- package/lib/shared-tree-core/defaultResubmitMachine.js.map +1 -1
- package/lib/shared-tree-core/editManager.d.ts.map +1 -1
- package/lib/shared-tree-core/editManager.js +2 -2
- package/lib/shared-tree-core/editManager.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.d.ts +2 -2
- package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/lib/shared-tree-core/editManagerSummarizer.d.ts +2 -2
- package/lib/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerSummarizer.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts +2 -2
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts +11 -2
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +11 -8
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/shared-tree-core/summaryTypes.d.ts +2 -2
- package/lib/shared-tree-core/summaryTypes.d.ts.map +1 -1
- package/lib/shared-tree-core/summaryTypes.js.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/lib/simple-tree/api/storedSchema.d.ts +2 -2
- package/lib/simple-tree/api/storedSchema.d.ts.map +1 -1
- package/lib/simple-tree/api/storedSchema.js.map +1 -1
- package/lib/simple-tree/api/treeChangeEvents.d.ts +5 -1
- package/lib/simple-tree/api/treeChangeEvents.d.ts.map +1 -1
- package/lib/simple-tree/api/treeChangeEvents.js.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.js +10 -0
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.js +19 -2
- package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/lib/tableSchema.d.ts +1 -1
- package/lib/tableSchema.js +1 -1
- package/lib/tableSchema.js.map +1 -1
- package/lib/treeFactory.d.ts.map +1 -1
- package/lib/treeFactory.js +1 -1
- package/lib/treeFactory.js.map +1 -1
- package/lib/util/breakable.d.ts +14 -2
- package/lib/util/breakable.d.ts.map +1 -1
- package/lib/util/breakable.js +9 -1
- package/lib/util/breakable.js.map +1 -1
- package/lib/util/compressedIds.d.ts +2 -2
- package/lib/util/compressedIds.d.ts.map +1 -1
- package/lib/util/compressedIds.js +1 -6
- package/lib/util/compressedIds.js.map +1 -1
- package/package.json +27 -27
- package/src/codec/codec.ts +7 -7
- package/src/codec/versioned/codec.ts +5 -5
- package/src/core/forest/editableForest.ts +2 -0
- package/src/core/index.ts +1 -1
- package/src/core/tree/index.ts +1 -1
- package/src/core/tree/visitorUtils.ts +37 -12
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +214 -59
- package/src/feature-libraries/comparison-forest/comparisonForest.ts +6 -1
- package/src/feature-libraries/detachedFieldIndexSummarizer.ts +2 -2
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +10 -0
- package/src/feature-libraries/forest-summary/summaryTypes.ts +2 -2
- package/src/feature-libraries/indexing/anchorTreeIndex.ts +1 -1
- package/src/feature-libraries/modular-schema/invert.ts +2 -2
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +2 -23
- package/src/feature-libraries/modular-schema/modularChangeUtils.ts +12 -21
- package/src/feature-libraries/object-forest/objectForest.ts +48 -14
- package/src/feature-libraries/schema-index/schemaSummarizer.ts +3 -3
- package/src/feature-libraries/sequence-field/rebase.ts +2 -2
- package/src/feature-libraries/sequence-field/utils.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/independentView.ts +5 -12
- package/src/shared-tree/sharedTree.ts +41 -10
- package/src/shared-tree/sharedTreeChangeCodecs.ts +2 -2
- package/src/shared-tree/sharedTreeChangeEnricher.ts +25 -1
- package/src/shared-tree/treeCheckout.ts +45 -16
- package/src/shared-tree-core/branchCommitEnricher.ts +6 -2
- package/src/shared-tree-core/changeEnricher.ts +7 -2
- package/src/shared-tree-core/defaultResubmitMachine.ts +5 -0
- package/src/shared-tree-core/editManager.ts +2 -4
- package/src/shared-tree-core/editManagerCodecs.ts +2 -2
- package/src/shared-tree-core/editManagerSummarizer.ts +3 -3
- package/src/shared-tree-core/messageCodecs.ts +2 -2
- package/src/shared-tree-core/sharedTreeCore.ts +29 -11
- package/src/shared-tree-core/summaryTypes.ts +2 -2
- package/src/simple-tree/api/snapshotCompatibilityChecker.ts +1 -1
- package/src/simple-tree/api/storedSchema.ts +2 -2
- package/src/simple-tree/api/treeChangeEvents.ts +5 -1
- package/src/simple-tree/node-kinds/array/arrayNode.ts +15 -0
- package/src/simple-tree/node-kinds/map/mapNode.ts +23 -2
- package/src/tableSchema.ts +1 -1
- package/src/treeFactory.ts +0 -1
- package/src/util/breakable.ts +9 -1
- package/src/util/compressedIds.ts +7 -13
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
type RevisionInfo,
|
|
13
13
|
type RevisionTag,
|
|
14
14
|
type TaggedChange,
|
|
15
|
-
type TreeChunk,
|
|
16
15
|
} from "../../core/index.js";
|
|
17
16
|
import { brand, type Mutable, type RangeQueryResult } from "../../util/index.js";
|
|
18
17
|
import {
|
|
@@ -33,12 +32,9 @@ import type { FlexFieldKind } from "./fieldKind.js";
|
|
|
33
32
|
import { genericFieldKind } from "./genericFieldKind.js";
|
|
34
33
|
import {
|
|
35
34
|
newCrossFieldKeyTable,
|
|
36
|
-
type CrossFieldKeyTable,
|
|
37
35
|
type FieldChange,
|
|
38
36
|
type FieldChangeMap,
|
|
39
|
-
type FieldId,
|
|
40
37
|
type ModularChangeset,
|
|
41
|
-
type NoChangeConstraint,
|
|
42
38
|
type NodeChangeset,
|
|
43
39
|
type NodeId,
|
|
44
40
|
} from "./modularChangeTypes.js";
|
|
@@ -47,22 +43,17 @@ export function hasConflicts(change: ModularChangeset): boolean {
|
|
|
47
43
|
return (change.constraintViolationCount ?? 0) > 0;
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
builds?: ChangeAtomIdBTree<TreeChunk>;
|
|
62
|
-
destroys?: ChangeAtomIdBTree<number>;
|
|
63
|
-
refreshers?: ChangeAtomIdBTree<TreeChunk>;
|
|
64
|
-
}): ModularChangeset {
|
|
65
|
-
const p = props ?? { maxId: -1 };
|
|
46
|
+
/**
|
|
47
|
+
* Creates a normalized changeset from the provided properties.
|
|
48
|
+
*
|
|
49
|
+
* Required changeset maps are initialized when omitted, and empty optional properties are not
|
|
50
|
+
* included in the returned changeset. `maxId` accepts an unbranded allocator ID; `undefined`
|
|
51
|
+
* indicates that the changeset contains no IDs.
|
|
52
|
+
*/
|
|
53
|
+
export function makeModularChangeset(
|
|
54
|
+
props: Partial<Omit<ModularChangeset, "maxId">> & { maxId?: number } = {},
|
|
55
|
+
): ModularChangeset {
|
|
56
|
+
const p = props;
|
|
66
57
|
const changeset: Mutable<ModularChangeset> = {
|
|
67
58
|
fieldChanges: p.fieldChanges ?? new Map<FieldKey, FieldChange>(),
|
|
68
59
|
nodeChanges: p.nodeChanges ?? newChangeAtomIdBTree(),
|
|
@@ -74,7 +65,7 @@ export function makeModularChangeset(props?: {
|
|
|
74
65
|
if (p.revisions !== undefined && p.revisions.length > 0) {
|
|
75
66
|
changeset.revisions = p.revisions;
|
|
76
67
|
}
|
|
77
|
-
if (p.maxId >= 0) {
|
|
68
|
+
if (p.maxId !== undefined && p.maxId >= 0) {
|
|
78
69
|
changeset.maxId = brand(p.maxId);
|
|
79
70
|
}
|
|
80
71
|
if (p.constraintViolationCount !== undefined && p.constraintViolationCount > 0) {
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
aboveRootPlaceholder,
|
|
39
39
|
combineVisitors,
|
|
40
40
|
deepCopyMapTree,
|
|
41
|
+
makeBreakingVisitor,
|
|
41
42
|
rootFieldKey,
|
|
42
43
|
} from "../../core/index.js";
|
|
43
44
|
import {
|
|
@@ -90,6 +91,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
90
91
|
|
|
91
92
|
readonly #roots: MutableMapTree;
|
|
92
93
|
public get roots(): MapTree {
|
|
94
|
+
this.breaker.use();
|
|
93
95
|
return this.#roots;
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -117,10 +119,12 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
public get isEmpty(): boolean {
|
|
122
|
+
this.breaker.use();
|
|
120
123
|
return this.roots.fields.size === 0;
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
public clone(schema: TreeStoredSchemaSubscription, breaker?: Breakable): ObjectForest {
|
|
127
|
+
this.breaker.use();
|
|
124
128
|
return new ObjectForest(
|
|
125
129
|
breaker ?? this.breaker,
|
|
126
130
|
schema,
|
|
@@ -131,6 +135,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
public chunkField(cursor: ITreeCursorSynchronous): TreeChunk[] {
|
|
138
|
+
this.breaker.use();
|
|
134
139
|
return chunkField(cursor, { idCompressor: undefined, policy: defaultChunkPolicy });
|
|
135
140
|
}
|
|
136
141
|
|
|
@@ -174,6 +179,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
174
179
|
}
|
|
175
180
|
|
|
176
181
|
public acquireVisitor(): DeltaVisitor {
|
|
182
|
+
this.breaker.use();
|
|
177
183
|
assert(
|
|
178
184
|
this.activeVisitor === undefined,
|
|
179
185
|
0x76c /* Must release existing visitor before acquiring another */,
|
|
@@ -228,10 +234,17 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
228
234
|
}
|
|
229
235
|
public destroy(detachedField: FieldKey, count: number): void {
|
|
230
236
|
preEdit();
|
|
237
|
+
const field = this.forest.#roots.fields.get(detachedField);
|
|
238
|
+
assert(field !== undefined, 0xd28 /* Destroyed field must exist */);
|
|
239
|
+
assert(field.length === count, 0xd29 /* Destroy count must match field length */);
|
|
231
240
|
this.forest.delete(detachedField);
|
|
232
241
|
}
|
|
233
242
|
public create(content: readonly ITreeCursorSynchronous[], destination: FieldKey): void {
|
|
234
243
|
preEdit();
|
|
244
|
+
assert(
|
|
245
|
+
!this.forest.#roots.fields.has(destination),
|
|
246
|
+
0xd2a /* Create destination must be a new empty field */,
|
|
247
|
+
);
|
|
235
248
|
this.forest.add(content, destination);
|
|
236
249
|
this.forest.#events.emit("afterRootFieldCreated", destination);
|
|
237
250
|
}
|
|
@@ -253,22 +266,24 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
253
266
|
*/
|
|
254
267
|
private attachEdit(source: FieldKey, count: number, destination: PlaceIndex): void {
|
|
255
268
|
assertNonNegativeSafeInteger(count);
|
|
256
|
-
if (count === 0) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
269
|
const [parent, key] = cursor.getParent();
|
|
260
270
|
assert(
|
|
261
271
|
parent !== this.forest.roots || key !== source,
|
|
262
|
-
|
|
272
|
+
0xd2b /* Attach source field must be different from current field */,
|
|
263
273
|
);
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
const sourceField = this.forest.#roots.fields.get(source) ?? [];
|
|
267
|
-
assert(sourceField !== undefined, 0x7b7 /* Attach source field must exist */);
|
|
274
|
+
const sourceField = this.forest.#roots.fields.get(source);
|
|
275
|
+
assert(sourceField !== undefined, 0xd2c /* Attach source field must exist */);
|
|
268
276
|
assert(
|
|
269
277
|
sourceField.length === count,
|
|
270
|
-
|
|
278
|
+
0xd2d /* Attach must consume all nodes in source field */,
|
|
279
|
+
);
|
|
280
|
+
const existingDestinationField = parent.fields.get(key) ?? [];
|
|
281
|
+
assert(
|
|
282
|
+
destination <= existingDestinationField.length,
|
|
283
|
+
0xd2e /* Attach destination must not exceed field length */,
|
|
271
284
|
);
|
|
285
|
+
assertValidIndex(destination, existingDestinationField, true);
|
|
286
|
+
const currentField = getOrCreateField(parent, key);
|
|
272
287
|
// TODO: this will fail for very large insertions due to argument limits.
|
|
273
288
|
currentField.splice(destination, 0, ...sourceField);
|
|
274
289
|
this.forest.delete(source);
|
|
@@ -284,10 +299,21 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
284
299
|
const [parent, key] = cursor.getParent();
|
|
285
300
|
assert(
|
|
286
301
|
destination === undefined || parent !== this.forest.roots || key !== destination,
|
|
287
|
-
|
|
302
|
+
0xd2f /* Detach destination field must be different from current field */,
|
|
303
|
+
);
|
|
304
|
+
const currentField = parent.fields.get(key) ?? [];
|
|
305
|
+
assert(source.start <= source.end, 0xd30 /* Detach range start must not exceed end */);
|
|
306
|
+
assert(
|
|
307
|
+
source.end <= currentField.length,
|
|
308
|
+
0xd31 /* Detach range must not exceed field length */,
|
|
288
309
|
);
|
|
289
|
-
const currentField = getOrCreateField(parent, key);
|
|
290
310
|
assertValidRange(source, currentField);
|
|
311
|
+
if (destination !== undefined) {
|
|
312
|
+
assert(
|
|
313
|
+
!this.forest.#roots.fields.has(destination),
|
|
314
|
+
0xd32 /* Detach destination must be a new empty field */,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
291
317
|
const content = currentField.splice(source.start, source.end - source.start);
|
|
292
318
|
if (destination !== undefined) {
|
|
293
319
|
this.forest.addFieldAsDetached(content, destination);
|
|
@@ -317,9 +343,12 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
317
343
|
for (const getVisitor of this.deltaVisitors) {
|
|
318
344
|
announcedVisitors.push(getVisitor());
|
|
319
345
|
}
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
346
|
+
const visitor = combineVisitors([
|
|
347
|
+
makeBreakingVisitor(forestVisitor, this.breaker),
|
|
348
|
+
...announcedVisitors,
|
|
349
|
+
]);
|
|
350
|
+
this.activeVisitor = visitor;
|
|
351
|
+
return visitor;
|
|
323
352
|
}
|
|
324
353
|
|
|
325
354
|
public registerAnnouncedVisitor(visitor: () => AnnouncedVisitor): void {
|
|
@@ -354,6 +383,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
354
383
|
}
|
|
355
384
|
|
|
356
385
|
public allocateCursor(source?: string): Cursor {
|
|
386
|
+
this.breaker.use();
|
|
357
387
|
return new Cursor(this, source);
|
|
358
388
|
}
|
|
359
389
|
|
|
@@ -361,6 +391,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
361
391
|
destination: Anchor,
|
|
362
392
|
cursorToMove: ITreeSubscriptionCursor,
|
|
363
393
|
): TreeNavigationResult {
|
|
394
|
+
this.breaker.use();
|
|
364
395
|
const path = this.anchors.locate(destination);
|
|
365
396
|
if (path === undefined) {
|
|
366
397
|
return TreeNavigationResult.NotFound;
|
|
@@ -373,6 +404,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
373
404
|
destination: FieldAnchor,
|
|
374
405
|
cursorToMove: ITreeSubscriptionCursor,
|
|
375
406
|
): TreeNavigationResult {
|
|
407
|
+
this.breaker.use();
|
|
376
408
|
if (destination.parent === undefined) {
|
|
377
409
|
this.moveCursorToPath(undefined, cursorToMove);
|
|
378
410
|
} else {
|
|
@@ -389,6 +421,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
389
421
|
destination: UpPath | undefined,
|
|
390
422
|
cursorToMove: ITreeSubscriptionCursor,
|
|
391
423
|
): void {
|
|
424
|
+
this.breaker.use();
|
|
392
425
|
assert(
|
|
393
426
|
cursorToMove instanceof Cursor,
|
|
394
427
|
0x337 /* ObjectForest must only be given its own Cursor type */,
|
|
@@ -419,6 +452,7 @@ export class ObjectForest implements IEditableForest, WithBreakable {
|
|
|
419
452
|
}
|
|
420
453
|
|
|
421
454
|
public getCursorAboveDetachedFields(): ITreeCursorSynchronous {
|
|
455
|
+
this.breaker.use();
|
|
422
456
|
return cursorForMapTreeNode(this.roots);
|
|
423
457
|
}
|
|
424
458
|
}
|
|
@@ -10,7 +10,7 @@ import { SummaryType } from "@fluidframework/driver-definitions";
|
|
|
10
10
|
import type {
|
|
11
11
|
IExperimentalIncrementalSummaryContext,
|
|
12
12
|
ITelemetryContext,
|
|
13
|
-
|
|
13
|
+
OldestSupportedClientVersion,
|
|
14
14
|
} from "@fluidframework/runtime-definitions/internal";
|
|
15
15
|
import type { SummaryTreeBuilder } from "@fluidframework/runtime-utils/internal";
|
|
16
16
|
|
|
@@ -57,7 +57,7 @@ const supportedVersions = new Set<SchemaSummaryFormatVersion>([
|
|
|
57
57
|
* Returns the summary version to use as per the given minimum version for collab.
|
|
58
58
|
*/
|
|
59
59
|
function minVersionToSchemaSummaryFormatVersion(
|
|
60
|
-
version:
|
|
60
|
+
version: OldestSupportedClientVersion,
|
|
61
61
|
): SchemaSummaryFormatVersion {
|
|
62
62
|
// Currently, version 2 is written which adds metadata blob to the summary.
|
|
63
63
|
return SchemaSummaryFormatVersion.v2;
|
|
@@ -76,7 +76,7 @@ export class SchemaSummarizer
|
|
|
76
76
|
private readonly schema: MutableTreeStoredSchema,
|
|
77
77
|
collabWindow: CollabWindow,
|
|
78
78
|
private readonly codec: IJsonCodec<TreeStoredSchema>,
|
|
79
|
-
minVersionForCollab:
|
|
79
|
+
minVersionForCollab: OldestSupportedClientVersion,
|
|
80
80
|
) {
|
|
81
81
|
super(
|
|
82
82
|
"Schema",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
6
|
+
import { assert, fail, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
7
7
|
|
|
8
8
|
import type { ChangeAtomId, RevisionMetadataSource, RevisionTag } from "../../core/index.js";
|
|
9
9
|
import { type IdAllocator, type Mutable, brand } from "../../util/index.js";
|
|
@@ -252,7 +252,7 @@ function addMovedMarkEffect(mark: Mark, effect: Detach): Mark {
|
|
|
252
252
|
} else if (isTombstone(mark)) {
|
|
253
253
|
return { ...mark, ...effect };
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
fail(0x818 /* Unexpected combination of mark effects at source and destination */);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/**
|
|
@@ -278,7 +278,7 @@ export function compareCellPositionsUsingTombstones(
|
|
|
278
278
|
//
|
|
279
279
|
// The same scenario can arise in the context of compose (just consider composing `old'` and `new'` from
|
|
280
280
|
// the examples above) with the same resolution.
|
|
281
|
-
|
|
281
|
+
fail(0x8a2 /* Invalid cell ordering scenario */);
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
// The absence of metadata for a cell with a defined revision means that the cell is from a revision that
|
package/src/packageVersion.ts
CHANGED
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
type IIdCompressor,
|
|
10
10
|
createIdCompressor,
|
|
11
11
|
} from "@fluidframework/id-compressor/internal";
|
|
12
|
-
import {
|
|
13
|
-
createChildLogger,
|
|
14
|
-
extractTelemetryLoggerExt,
|
|
15
|
-
} from "@fluidframework/telemetry-utils/internal";
|
|
12
|
+
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
16
13
|
|
|
17
14
|
import type { CodecWriteOptions, ICodecOptions } from "../codec/index.js";
|
|
18
15
|
import {
|
|
@@ -224,12 +221,10 @@ export function createIndependentTreeBeta<const TSchema extends ImplicitFieldSch
|
|
|
224
221
|
export function createIndependentTreeAlpha<const TSchema extends ImplicitFieldSchema>(
|
|
225
222
|
options?: CreateIndependentTreeAlphaOptions,
|
|
226
223
|
): ViewableTree & Pick<ITreeAlpha, "exportVerbose" | "exportSimpleSchema"> {
|
|
227
|
-
const logger =
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}),
|
|
232
|
-
);
|
|
224
|
+
const logger = createChildLogger({
|
|
225
|
+
logger: options?.logger,
|
|
226
|
+
namespace: "independentView",
|
|
227
|
+
});
|
|
233
228
|
const breaker = new Breakable("independentView", logger);
|
|
234
229
|
const idCompressor: IIdCompressor =
|
|
235
230
|
options?.idCompressor ?? options?.content?.idCompressor ?? createIdCompressor();
|
|
@@ -250,8 +245,6 @@ export function createIndependentTreeAlpha<const TSchema extends ImplicitFieldSc
|
|
|
250
245
|
const checkout = createTreeCheckout(idCompressor, mintRevisionTag, revisionTagCodec, {
|
|
251
246
|
forest,
|
|
252
247
|
schema: schemaRepository,
|
|
253
|
-
breaker,
|
|
254
|
-
logger,
|
|
255
248
|
codecOptions: options,
|
|
256
249
|
});
|
|
257
250
|
|
|
@@ -7,13 +7,13 @@ import type { ErasedType, IFluidLoadable } from "@fluidframework/core-interfaces
|
|
|
7
7
|
import { assert, fail } from "@fluidframework/core-utils/internal";
|
|
8
8
|
import type { IChannelStorageService } from "@fluidframework/datastore-definitions/internal";
|
|
9
9
|
import type { IIdCompressor, StableId } from "@fluidframework/id-compressor";
|
|
10
|
-
import type {
|
|
10
|
+
import type { OldestSupportedClientVersion } from "@fluidframework/runtime-definitions/internal";
|
|
11
11
|
import type {
|
|
12
12
|
IChannelView,
|
|
13
13
|
IFluidSerializer,
|
|
14
14
|
SharedKernel,
|
|
15
15
|
} from "@fluidframework/shared-object-base/internal";
|
|
16
|
-
import {
|
|
16
|
+
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
type CodecTree,
|
|
@@ -206,7 +206,6 @@ export class SharedTreeKernel
|
|
|
206
206
|
submitLocalMessage: (content: unknown, localOpMetadata?: unknown) => void,
|
|
207
207
|
lastSequenceNumber: () => number | undefined,
|
|
208
208
|
initialSequenceNumber: number,
|
|
209
|
-
private readonly logger: TelemetryLoggerExt | undefined,
|
|
210
209
|
idCompressor: IIdCompressor,
|
|
211
210
|
optionsParam: SharedTreeOptionsInternal,
|
|
212
211
|
) {
|
|
@@ -252,7 +251,7 @@ export class SharedTreeKernel
|
|
|
252
251
|
idCompressor,
|
|
253
252
|
healing:
|
|
254
253
|
options.healUnresolvableIdentifiersOnDecode === true
|
|
255
|
-
? { sharedObjectId: sharedObject.id, logger }
|
|
254
|
+
? { sharedObjectId: sharedObject.id, logger: breaker.logger }
|
|
256
255
|
: undefined,
|
|
257
256
|
});
|
|
258
257
|
const forestSummarizer = new ForestSummarizer(
|
|
@@ -305,7 +304,6 @@ export class SharedTreeKernel
|
|
|
305
304
|
sharedObject,
|
|
306
305
|
serializer,
|
|
307
306
|
submitLocalMessage,
|
|
308
|
-
logger,
|
|
309
307
|
[schemaSummarizer, forestSummarizer, removedRootsSummarizer],
|
|
310
308
|
changeFamily,
|
|
311
309
|
options,
|
|
@@ -324,8 +322,6 @@ export class SharedTreeKernel
|
|
|
324
322
|
fieldBatchCodec,
|
|
325
323
|
removedRoots,
|
|
326
324
|
chunkCompressionStrategy: options.treeEncodeType,
|
|
327
|
-
logger,
|
|
328
|
-
breaker: this.breaker,
|
|
329
325
|
});
|
|
330
326
|
|
|
331
327
|
this.registerSharedBranchForEditing("main", this.checkout);
|
|
@@ -546,14 +542,16 @@ export const changeFormatVersionForMessage = DependentFormatVersion.fromPairs<
|
|
|
546
542
|
[MessageFormatVersion.v6, SharedTreeChangeFormatVersion.v5],
|
|
547
543
|
]);
|
|
548
544
|
|
|
549
|
-
function getCodecTreeForEditManagerFormat(
|
|
545
|
+
function getCodecTreeForEditManagerFormat(
|
|
546
|
+
clientVersion: OldestSupportedClientVersion,
|
|
547
|
+
): CodecTree {
|
|
550
548
|
const editManagerVersion = makeEditManagerCodecBuilder().getCodecTree(clientVersion).version;
|
|
551
549
|
const change = changeFormatVersionForEditManager.lookup(editManagerVersion);
|
|
552
550
|
const changeCodecTree = getCodecTreeForChangeFormat(change, clientVersion);
|
|
553
551
|
return getCodecTreeForEditManagerFormatWithChange(clientVersion, changeCodecTree);
|
|
554
552
|
}
|
|
555
553
|
|
|
556
|
-
function getCodecTreeForMessageFormat(clientVersion:
|
|
554
|
+
function getCodecTreeForMessageFormat(clientVersion: OldestSupportedClientVersion): CodecTree {
|
|
557
555
|
const messageVersion = makeMessageCodecBuilder().getCodecTree(clientVersion).version;
|
|
558
556
|
assert(
|
|
559
557
|
messageVersion !== undefined,
|
|
@@ -565,7 +563,7 @@ function getCodecTreeForMessageFormat(clientVersion: MinimumVersionForCollab): C
|
|
|
565
563
|
}
|
|
566
564
|
|
|
567
565
|
export function getCodecTreeForSharedTreeFormat(
|
|
568
|
-
clientVersion:
|
|
566
|
+
clientVersion: OldestSupportedClientVersion,
|
|
569
567
|
): CodecTree {
|
|
570
568
|
const children: CodecTree[] = [];
|
|
571
569
|
children.push(forestCodecBuilder.getCodecTree(clientVersion));
|
|
@@ -665,6 +663,35 @@ export interface SharedTreeOptions
|
|
|
665
663
|
* lifetime of the client, which increases memory usage over time and should be used with care.
|
|
666
664
|
*/
|
|
667
665
|
readonly retainHistory?: boolean;
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* When `true`, validates that commits being submitted for the first time can be applied without errors to a view.
|
|
669
|
+
* In the event that a commit cannot be applied, SharedTree will throw an error and will enter a "broken" state, preventing the offending commit (and any further commits) from being submitted.
|
|
670
|
+
*
|
|
671
|
+
* This can be enabled (at the cost of performance) to improve safety against document corruption in the event of a bug in the SharedTree code:
|
|
672
|
+
* when the additional validation is enabled, a client will error instead of potentially corrupting the document.
|
|
673
|
+
*
|
|
674
|
+
* @defaultValue `false`
|
|
675
|
+
*
|
|
676
|
+
* @remarks
|
|
677
|
+
* This validation is more expensive than {@link SharedTreeOptions.validateRebasedCommitsBeforeResubmission} because it is likely to be performed more often.
|
|
678
|
+
* We recommend {@link ForestOptions.forest|configuring SharedTree} with the {@link ForestTypeOptimized|optimized forest implementation} to reduce its performance impact.
|
|
679
|
+
*/
|
|
680
|
+
readonly validateCommitsOnFirstSubmission?: boolean;
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* When `true`, validates that the commits being resubmitted can be applied without errors to a view.
|
|
684
|
+
* In the event that a commit cannot be applied, SharedTree will throw an error and will enter a "broken" state, preventing the offending commit (and any further commits) from being submitted.
|
|
685
|
+
*
|
|
686
|
+
* This can be enabled (at the cost of performance) to improve safety against document corruption in the event of a bug in the SharedTree code:
|
|
687
|
+
* when the additional validation is enabled, a client will error instead of potentially corrupting the document.
|
|
688
|
+
*
|
|
689
|
+
* @defaultValue `false`
|
|
690
|
+
*
|
|
691
|
+
* @remarks
|
|
692
|
+
* We recommend {@link ForestOptions.forest|configuring SharedTree} with the {@link ForestTypeOptimized|optimized forest implementation} to reduce the performance impact of this validation.
|
|
693
|
+
*/
|
|
694
|
+
readonly validateRebasedCommitsBeforeResubmission?: boolean;
|
|
668
695
|
}
|
|
669
696
|
|
|
670
697
|
export interface SharedTreeOptionsInternal
|
|
@@ -741,6 +768,7 @@ export const ForestTypeOptimized = toForestType(
|
|
|
741
768
|
makeTreeChunker(schema, defaultSchemaPolicy, shouldEncodeIncrementally),
|
|
742
769
|
undefined,
|
|
743
770
|
idCompressor,
|
|
771
|
+
breaker,
|
|
744
772
|
),
|
|
745
773
|
);
|
|
746
774
|
|
|
@@ -767,6 +795,7 @@ export const ForestTypeExpensiveDebug = toForestType(
|
|
|
767
795
|
makeTreeChunker(schema, defaultSchemaPolicy, shouldEncodeIncrementally),
|
|
768
796
|
undefined,
|
|
769
797
|
idCompressor,
|
|
798
|
+
breaker,
|
|
770
799
|
),
|
|
771
800
|
buildForest(breaker, schema, undefined, true),
|
|
772
801
|
),
|
|
@@ -812,6 +841,8 @@ export const defaultSharedTreeOptions: Required<SharedTreeOptionsInternal> = {
|
|
|
812
841
|
writeVersionOverrides: new Map(),
|
|
813
842
|
allowPossiblyIncompatibleWriteVersionOverrides: false,
|
|
814
843
|
retainHistory: false,
|
|
844
|
+
validateCommitsOnFirstSubmission: false,
|
|
845
|
+
validateRebasedCommitsBeforeResubmission: false,
|
|
815
846
|
};
|
|
816
847
|
|
|
817
848
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { fail } from "@fluidframework/core-utils/internal";
|
|
7
|
-
import type {
|
|
7
|
+
import type { OldestSupportedClientVersion } from "@fluidframework/runtime-definitions/internal";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
type CodecTree,
|
|
@@ -130,7 +130,7 @@ export const dependenciesForChangeFormat = new Map<
|
|
|
130
130
|
|
|
131
131
|
export function getCodecTreeForChangeFormat(
|
|
132
132
|
version: SharedTreeChangeFormatVersion,
|
|
133
|
-
clientVersion:
|
|
133
|
+
clientVersion: OldestSupportedClientVersion,
|
|
134
134
|
): CodecTree {
|
|
135
135
|
const { modularChange } =
|
|
136
136
|
dependenciesForChangeFormat.get(version) ?? fail(0xc78 /* Unknown change format */);
|
|
@@ -40,6 +40,10 @@ interface OwnedState {
|
|
|
40
40
|
readonly removedRoots: DetachedFieldIndex;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* A helper for enriching `SharedTreeChange`s.
|
|
45
|
+
* It lazily forks the borrowed state (which it does not mutate) and lazily applies {@link enqueueChange|changes} to the forked state when {@link enrich|commit enrichment} requires access to detached root data.
|
|
46
|
+
*/
|
|
43
47
|
export class SharedTreeChangeEnricher {
|
|
44
48
|
/**
|
|
45
49
|
* Queue of changes to be applied before querying for detached roots.
|
|
@@ -76,6 +80,13 @@ export class SharedTreeChangeEnricher {
|
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Enriches a change.
|
|
85
|
+
* @param change - The change to enrich (not mutated)
|
|
86
|
+
* @returns An enriched copy of the given `change`.
|
|
87
|
+
* @remarks
|
|
88
|
+
* Invoking this method will flush {@link enqueueChange|queued} changes if the enrichment process requires reading detached roots from the forest.
|
|
89
|
+
*/
|
|
79
90
|
public enrich(change: SharedTreeChange): SharedTreeChange {
|
|
80
91
|
this.onEnrichCommit?.();
|
|
81
92
|
return updateRefreshers(
|
|
@@ -104,13 +115,23 @@ export class SharedTreeChangeEnricher {
|
|
|
104
115
|
return undefined;
|
|
105
116
|
}
|
|
106
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Enqueues a change to be applied before reading detached root data during the next {@link enrich|enrichment}.
|
|
120
|
+
* @param change - The change to apply or a function that returns the change to apply.
|
|
121
|
+
* If a function is provided, it will be invoked during a future call to {@link enrich} (if at all).
|
|
122
|
+
*/
|
|
107
123
|
public enqueueChange(
|
|
108
124
|
change: TaggedChange<SharedTreeChange> | (() => TaggedChange<SharedTreeChange>),
|
|
109
125
|
): void {
|
|
110
126
|
this.changeQueue.push(typeof change === "function" ? change : () => change);
|
|
111
127
|
}
|
|
112
128
|
|
|
113
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Flushes all {@link enqueueChange|queued} changes by applying the data changes to a forked forest and detached field index.
|
|
131
|
+
* @remarks
|
|
132
|
+
* This validates that the data changes are valid.
|
|
133
|
+
*/
|
|
134
|
+
public purgeChangeQueue(): void {
|
|
114
135
|
if (this.changeQueue.length === 0) {
|
|
115
136
|
return;
|
|
116
137
|
}
|
|
@@ -150,6 +171,9 @@ export class SharedTreeChangeEnricher {
|
|
|
150
171
|
this.changeQueue.length = 0;
|
|
151
172
|
}
|
|
152
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Dispose of this object and its resources.
|
|
176
|
+
*/
|
|
153
177
|
public [disposeSymbol](): void {
|
|
154
178
|
// TODO: in the future, forest and/or its AnchorSet may require disposal.
|
|
155
179
|
}
|
|
@@ -334,14 +334,11 @@ export function createTreeCheckout(
|
|
|
334
334
|
fieldBatchCodec?: FieldBatchCodec;
|
|
335
335
|
removedRoots?: DetachedFieldIndex;
|
|
336
336
|
chunkCompressionStrategy?: TreeCompressionStrategy;
|
|
337
|
-
logger?: TelemetryLoggerExt;
|
|
338
|
-
breaker?: Breakable;
|
|
339
337
|
codecOptions?: Partial<CodecWriteOptions>;
|
|
340
338
|
},
|
|
341
339
|
): TreeCheckout {
|
|
342
|
-
const breaker = args?.breaker ?? new Breakable("TreeCheckout", args?.logger);
|
|
343
340
|
const schema = args?.schema ?? new TreeStoredSchemaRepository();
|
|
344
|
-
const forest = args?.forest ?? buildForest(
|
|
341
|
+
const forest = args?.forest ?? buildForest(new Breakable("TreeCheckout"), schema);
|
|
345
342
|
const defaultCodecOptions: CodecWriteOptions = {
|
|
346
343
|
jsonValidator: FormatValidatorNoOp,
|
|
347
344
|
minVersionForCollab: FluidClientVersion.v2_0,
|
|
@@ -380,8 +377,6 @@ export function createTreeCheckout(
|
|
|
380
377
|
revisionTagCodec,
|
|
381
378
|
idCompressor,
|
|
382
379
|
args?.removedRoots,
|
|
383
|
-
args?.logger,
|
|
384
|
-
breaker,
|
|
385
380
|
);
|
|
386
381
|
}
|
|
387
382
|
|
|
@@ -489,6 +484,31 @@ export function assertValidConstraint(
|
|
|
489
484
|
export class TreeCheckout implements ITreeCheckout {
|
|
490
485
|
public disposed = false;
|
|
491
486
|
|
|
487
|
+
/**
|
|
488
|
+
* Logger for telemetry events related to this checkout.
|
|
489
|
+
* @remarks
|
|
490
|
+
* This logger is provided by the {@link Breakable} passed to the constructor, and may be `undefined` if no logger was provided.
|
|
491
|
+
* See the explanation on {@link Breakable.logger} for why this pattern makes sense.
|
|
492
|
+
*/
|
|
493
|
+
private get logger(): TelemetryLoggerExt | undefined {
|
|
494
|
+
return this.breaker.logger;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The breaker for this checkout.
|
|
499
|
+
* @remarks
|
|
500
|
+
* As the forest is part of the mutable state of the checkout,
|
|
501
|
+
* we want to be broken if it is broken, so we use the forest's breaker as our own.
|
|
502
|
+
*
|
|
503
|
+
* This does mean that if something goes wrong in the checkout, we break the forest,
|
|
504
|
+
* which is not strictly necessary, but may be desirable as if something is wrong with the checkout,
|
|
505
|
+
* while the forest might be internally consistent, it likely doesn't contain the correct content
|
|
506
|
+
* (since the checkout is keeping it up to date properly anymore).
|
|
507
|
+
*/
|
|
508
|
+
public get breaker(): Breakable {
|
|
509
|
+
return this.forest.breaker;
|
|
510
|
+
}
|
|
511
|
+
|
|
492
512
|
private editLock: EditLock;
|
|
493
513
|
|
|
494
514
|
/**
|
|
@@ -566,9 +586,6 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
566
586
|
private readonly revisionTagCodec: RevisionTagCodec,
|
|
567
587
|
private readonly idCompressor: IIdCompressor,
|
|
568
588
|
private readonly _removedRoots: DetachedFieldIndex = makeDetachedFieldIndex("repair"),
|
|
569
|
-
/** Optional logger for telemetry. */
|
|
570
|
-
private readonly logger?: TelemetryLoggerExt,
|
|
571
|
-
public readonly breaker: Breakable = new Breakable("TreeCheckout", logger),
|
|
572
589
|
public readonly disposeForksAfterTransaction = true,
|
|
573
590
|
) {
|
|
574
591
|
this.#transaction = this.createTransactionStack(branch);
|
|
@@ -1119,6 +1136,7 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
1119
1136
|
});
|
|
1120
1137
|
};
|
|
1121
1138
|
|
|
1139
|
+
@breakingMethod
|
|
1122
1140
|
private withCombinedVisitor(fn: (visitor: DeltaVisitor) => void): void {
|
|
1123
1141
|
const anchorVisitor = this.forest.anchors.acquireVisitor();
|
|
1124
1142
|
const combinedVisitor = combineVisitors([this.forest.acquireVisitor(), anchorVisitor]);
|
|
@@ -1132,7 +1150,7 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
1132
1150
|
if (usageError !== undefined) {
|
|
1133
1151
|
throw new UsageError(usageError);
|
|
1134
1152
|
}
|
|
1135
|
-
|
|
1153
|
+
fail(0x911 /* Invalid operation on a disposed TreeCheckout */);
|
|
1136
1154
|
}
|
|
1137
1155
|
}
|
|
1138
1156
|
|
|
@@ -1304,8 +1322,6 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
1304
1322
|
this.revisionTagCodec,
|
|
1305
1323
|
this.idCompressor,
|
|
1306
1324
|
this._removedRoots.clone(),
|
|
1307
|
-
this.logger,
|
|
1308
|
-
forkBreaker,
|
|
1309
1325
|
);
|
|
1310
1326
|
this.#events.emit("fork", checkout);
|
|
1311
1327
|
return checkout;
|
|
@@ -1704,6 +1720,7 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
1704
1720
|
public enrich(
|
|
1705
1721
|
context: GraphCommit<SharedTreeChange>,
|
|
1706
1722
|
changes: readonly TaggedChange<SharedTreeChange>[],
|
|
1723
|
+
forceValidation: boolean,
|
|
1707
1724
|
): SharedTreeChange[] {
|
|
1708
1725
|
if (!hasSome(changes)) {
|
|
1709
1726
|
return [];
|
|
@@ -1734,13 +1751,25 @@ export class TreeCheckout implements ITreeCheckout {
|
|
|
1734
1751
|
return makeAnonChange(diff);
|
|
1735
1752
|
});
|
|
1736
1753
|
}
|
|
1737
|
-
const
|
|
1754
|
+
const enrichedChanges: SharedTreeChange[] = [];
|
|
1738
1755
|
for (const change of changes) {
|
|
1739
|
-
|
|
1740
|
-
|
|
1756
|
+
const enrichedChange = enricher.enrich(change.change);
|
|
1757
|
+
enrichedChanges.push(enrichedChange);
|
|
1758
|
+
// We could either apply the original or the enriched change: either should allow subsequent changes to be enriched correctly.
|
|
1759
|
+
// We choose to apply the enriched change in order to more closely replicate what a peer will experience (assuming no concurrent changes).
|
|
1760
|
+
// This makes it more likely that we'll detect a bug in the enrichment logic.
|
|
1761
|
+
// Note that enqueueing the change does not guarantee that it will be applied unless `forceValidation` is enabled.
|
|
1762
|
+
enricher.enqueueChange(tagChange(enrichedChange, change.revision));
|
|
1763
|
+
}
|
|
1764
|
+
if (forceValidation) {
|
|
1765
|
+
// Ensure that all changes can be applied successfully.
|
|
1766
|
+
// This is useful for catching bugs in the enrichment logic and in the rebasing logic that may have been involved in producing the changes.
|
|
1767
|
+
// Note that we cannot guarantee that the enriched change will be applied successfully even in the absence of concurrent changes,
|
|
1768
|
+
// because the checkout state we are applying this enriched change to was derived from the state of the checkout, which may be different from that of a peer.
|
|
1769
|
+
enricher.purgeChangeQueue();
|
|
1741
1770
|
}
|
|
1742
1771
|
enricher[disposeSymbol]();
|
|
1743
|
-
return
|
|
1772
|
+
return enrichedChanges;
|
|
1744
1773
|
}
|
|
1745
1774
|
|
|
1746
1775
|
public get mainBranch(): SharedTreeBranch<
|