@fluidframework/tree 2.1.0-281041 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/Tree.code-workspace +2 -1
- package/CHANGELOG.md +38 -0
- package/README.md +6 -6
- package/api-report/tree.alpha.api.md +1 -1
- package/api-report/tree.beta.api.md +1 -1
- package/api-report/tree.public.api.md +1 -1
- package/dist/core/forest/editableForest.d.ts.map +1 -1
- package/dist/core/forest/editableForest.js +4 -2
- package/dist/core/forest/editableForest.js.map +1 -1
- package/dist/core/tree/anchorSet.d.ts +1 -0
- package/dist/core/tree/anchorSet.d.ts.map +1 -1
- package/dist/core/tree/anchorSet.js +13 -0
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/core/tree/detachedFieldIndex.d.ts +48 -11
- package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndex.js +144 -20
- package/dist/core/tree/detachedFieldIndex.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.js +13 -4
- package/dist/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- package/dist/core/tree/detachedFieldIndexTypes.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexTypes.js.map +1 -1
- package/dist/core/tree/index.d.ts +2 -1
- package/dist/core/tree/index.d.ts.map +1 -1
- package/dist/core/tree/index.js.map +1 -1
- package/dist/core/tree/visitDelta.d.ts +3 -1
- package/dist/core/tree/visitDelta.d.ts.map +1 -1
- package/dist/core/tree/visitDelta.js +31 -15
- package/dist/core/tree/visitDelta.js.map +1 -1
- package/dist/core/tree/visitorUtils.d.ts +3 -3
- package/dist/core/tree/visitorUtils.d.ts.map +1 -1
- package/dist/core/tree/visitorUtils.js +4 -4
- package/dist/core/tree/visitorUtils.js.map +1 -1
- package/dist/feature-libraries/editableTreeBinder.js +1 -1
- package/dist/feature-libraries/editableTreeBinder.js.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -8
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js +0 -52
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -13
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/flex-tree/index.d.ts +2 -1
- package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/index.js +5 -1
- package/dist/feature-libraries/flex-tree/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts +1 -2
- package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +10 -18
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts +1 -4
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +0 -27
- package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +1 -1
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +4 -1
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.js +3 -3
- package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +14 -17
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +5 -4
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +5 -1
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +8 -1
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/dist/shared-tree/treeApi.js +1 -1
- package/dist/shared-tree/treeApi.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +10 -1
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +47 -4
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree/treeView.d.ts.map +1 -1
- package/dist/shared-tree/treeView.js +7 -3
- package/dist/shared-tree/treeView.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +6 -0
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js +2 -0
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts +4 -0
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +6 -0
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/arrayNode.js +1 -1
- package/dist/simple-tree/arrayNode.js.map +1 -1
- package/dist/simple-tree/index.d.ts +3 -3
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +2 -1
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/proxies.d.ts.map +1 -1
- package/dist/simple-tree/proxies.js +7 -21
- package/dist/simple-tree/proxies.js.map +1 -1
- package/dist/simple-tree/proxyBinding.d.ts +4 -0
- package/dist/simple-tree/proxyBinding.d.ts.map +1 -1
- package/dist/simple-tree/proxyBinding.js +23 -1
- package/dist/simple-tree/proxyBinding.js.map +1 -1
- package/dist/simple-tree/tree.d.ts.map +1 -1
- package/dist/simple-tree/tree.js +1 -1
- package/dist/simple-tree/tree.js.map +1 -1
- package/dist/simple-tree/treeNodeApi.d.ts +2 -75
- package/dist/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/dist/simple-tree/treeNodeApi.js +7 -15
- package/dist/simple-tree/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/treeNodeKernel.d.ts +26 -0
- package/dist/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/dist/simple-tree/treeNodeKernel.js +83 -0
- package/dist/simple-tree/treeNodeKernel.js.map +1 -0
- package/dist/simple-tree/types.d.ts +73 -0
- package/dist/simple-tree/types.d.ts.map +1 -1
- package/dist/simple-tree/types.js +89 -1
- package/dist/simple-tree/types.js.map +1 -1
- package/dist/util/breakable.js +1 -1
- package/dist/util/breakable.js.map +1 -1
- package/dist/util/index.d.ts +1 -1
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js.map +1 -1
- package/lib/core/forest/editableForest.d.ts.map +1 -1
- package/lib/core/forest/editableForest.js +4 -2
- package/lib/core/forest/editableForest.js.map +1 -1
- package/lib/core/tree/anchorSet.d.ts +1 -0
- package/lib/core/tree/anchorSet.d.ts.map +1 -1
- package/lib/core/tree/anchorSet.js +13 -0
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/core/tree/detachedFieldIndex.d.ts +48 -11
- package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndex.js +145 -21
- package/lib/core/tree/detachedFieldIndex.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.js +13 -4
- package/lib/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- package/lib/core/tree/detachedFieldIndexTypes.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexTypes.js.map +1 -1
- package/lib/core/tree/index.d.ts +2 -1
- package/lib/core/tree/index.d.ts.map +1 -1
- package/lib/core/tree/index.js.map +1 -1
- package/lib/core/tree/visitDelta.d.ts +3 -1
- package/lib/core/tree/visitDelta.d.ts.map +1 -1
- package/lib/core/tree/visitDelta.js +31 -15
- package/lib/core/tree/visitDelta.js.map +1 -1
- package/lib/core/tree/visitorUtils.d.ts +3 -3
- package/lib/core/tree/visitorUtils.d.ts.map +1 -1
- package/lib/core/tree/visitorUtils.js +4 -4
- package/lib/core/tree/visitorUtils.js.map +1 -1
- package/lib/feature-libraries/editableTreeBinder.js +1 -1
- package/lib/feature-libraries/editableTreeBinder.js.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -8
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js +2 -54
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -13
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/flex-tree/index.d.ts +2 -1
- package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/index.js +2 -1
- package/lib/feature-libraries/flex-tree/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts +1 -2
- package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +12 -20
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts +1 -4
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +3 -30
- package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +1 -1
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +1 -1
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.js +3 -3
- package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +14 -17
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +5 -4
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +5 -1
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +8 -1
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/lib/shared-tree/treeApi.js +1 -1
- package/lib/shared-tree/treeApi.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +10 -1
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +47 -4
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree/treeView.d.ts.map +1 -1
- package/lib/shared-tree/treeView.js +4 -0
- package/lib/shared-tree/treeView.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +6 -0
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js +2 -0
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts +4 -0
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +6 -0
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/arrayNode.js +2 -2
- package/lib/simple-tree/arrayNode.js.map +1 -1
- package/lib/simple-tree/index.d.ts +3 -3
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js +1 -1
- package/lib/simple-tree/index.js.map +1 -1
- package/lib/simple-tree/proxies.d.ts.map +1 -1
- package/lib/simple-tree/proxies.js +7 -21
- package/lib/simple-tree/proxies.js.map +1 -1
- package/lib/simple-tree/proxyBinding.d.ts +4 -0
- package/lib/simple-tree/proxyBinding.d.ts.map +1 -1
- package/lib/simple-tree/proxyBinding.js +19 -0
- package/lib/simple-tree/proxyBinding.js.map +1 -1
- package/lib/simple-tree/tree.d.ts.map +1 -1
- package/lib/simple-tree/tree.js +1 -1
- package/lib/simple-tree/tree.js.map +1 -1
- package/lib/simple-tree/treeNodeApi.d.ts +2 -75
- package/lib/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/lib/simple-tree/treeNodeApi.js +9 -17
- package/lib/simple-tree/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/treeNodeKernel.d.ts +26 -0
- package/lib/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/lib/simple-tree/treeNodeKernel.js +79 -0
- package/lib/simple-tree/treeNodeKernel.js.map +1 -0
- package/lib/simple-tree/types.d.ts +73 -0
- package/lib/simple-tree/types.d.ts.map +1 -1
- package/lib/simple-tree/types.js +90 -2
- package/lib/simple-tree/types.js.map +1 -1
- package/lib/util/breakable.js +1 -1
- package/lib/util/breakable.js.map +1 -1
- package/lib/util/index.d.ts +1 -1
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js.map +1 -1
- package/package.json +22 -22
- package/src/core/forest/editableForest.ts +10 -2
- package/src/core/tree/anchorSet.ts +14 -0
- package/src/core/tree/detachedFieldIndex.ts +217 -35
- package/src/core/tree/detachedFieldIndexCodec.ts +17 -8
- package/src/core/tree/detachedFieldIndexFormat.ts +1 -1
- package/src/core/tree/detachedFieldIndexTypes.ts +41 -5
- package/src/core/tree/index.ts +2 -1
- package/src/core/tree/visitDelta.ts +57 -16
- package/src/core/tree/visitorUtils.ts +7 -4
- package/src/feature-libraries/editableTreeBinder.ts +1 -1
- package/src/feature-libraries/flex-map-tree/mapTreeNode.ts +1 -65
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +0 -19
- package/src/feature-libraries/flex-tree/index.ts +7 -1
- package/src/feature-libraries/flex-tree/lazyEntity.ts +0 -3
- package/src/feature-libraries/flex-tree/lazyField.ts +14 -26
- package/src/feature-libraries/flex-tree/lazyNode.ts +1 -42
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +1 -0
- package/src/feature-libraries/index.ts +3 -0
- package/src/feature-libraries/modular-schema/discrepancies.ts +3 -3
- package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +1 -1
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +22 -20
- package/src/feature-libraries/object-forest/objectForest.ts +7 -3
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/sharedTree.ts +8 -1
- package/src/shared-tree/sharedTreeChangeEnricher.ts +1 -1
- package/src/shared-tree/treeApi.ts +1 -1
- package/src/shared-tree/treeCheckout.ts +56 -5
- package/src/shared-tree/treeView.ts +5 -0
- package/src/shared-tree-core/branch.ts +9 -0
- package/src/shared-tree-core/sharedTreeCore.ts +7 -0
- package/src/simple-tree/arrayNode.ts +2 -2
- package/src/simple-tree/index.ts +3 -3
- package/src/simple-tree/proxies.ts +8 -29
- package/src/simple-tree/proxyBinding.ts +23 -0
- package/src/simple-tree/tree.ts +4 -1
- package/src/simple-tree/treeNodeApi.ts +14 -96
- package/src/simple-tree/treeNodeKernel.ts +91 -0
- package/src/simple-tree/types.ts +233 -2
- package/src/util/breakable.ts +1 -1
- package/src/util/index.ts +1 -0
|
@@ -7,43 +7,66 @@ import { assert } from "@fluidframework/core-utils/internal";
|
|
|
7
7
|
|
|
8
8
|
import { type ICodecOptions, type IJsonCodec, noopValidator } from "../../codec/index.js";
|
|
9
9
|
import {
|
|
10
|
-
type Brand,
|
|
11
10
|
type IdAllocator,
|
|
12
11
|
type JsonCompatibleReadOnly,
|
|
13
12
|
type NestedMap,
|
|
14
13
|
brand,
|
|
15
14
|
deleteFromNestedMap,
|
|
15
|
+
forEachInNestedMap,
|
|
16
16
|
idAllocatorFromMaxId,
|
|
17
17
|
populateNestedMap,
|
|
18
18
|
setInNestedMap,
|
|
19
19
|
tryGetFromNestedMap,
|
|
20
20
|
} from "../../util/index.js";
|
|
21
|
-
import type { RevisionTagCodec } from "../rebase/index.js";
|
|
21
|
+
import type { RevisionTag, RevisionTagCodec } from "../rebase/index.js";
|
|
22
22
|
import type { FieldKey } from "../schema-stored/index.js";
|
|
23
23
|
|
|
24
24
|
import type * as Delta from "./delta.js";
|
|
25
25
|
import { makeDetachedNodeToFieldCodec } from "./detachedFieldIndexCodec.js";
|
|
26
26
|
import type { Format } from "./detachedFieldIndexFormat.js";
|
|
27
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
DetachedField,
|
|
29
|
+
DetachedFieldSummaryData,
|
|
30
|
+
ForestRootId,
|
|
31
|
+
Major,
|
|
32
|
+
Minor,
|
|
33
|
+
} from "./detachedFieldIndexTypes.js";
|
|
28
34
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
29
35
|
|
|
30
|
-
/**
|
|
31
|
-
* ID used to create a detached field key for a removed subtree.
|
|
32
|
-
*
|
|
33
|
-
* TODO: Move to Forest once forests can support multiple roots.
|
|
34
|
-
* @internal
|
|
35
|
-
*/
|
|
36
|
-
export type ForestRootId = Brand<number, "tree.ForestRootId">;
|
|
37
|
-
|
|
38
36
|
/**
|
|
39
37
|
* The tree index records detached field IDs and associates them with a change atom ID.
|
|
40
38
|
*/
|
|
41
39
|
export class DetachedFieldIndex {
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
/**
|
|
41
|
+
* A mapping from detached node ids to detached fields.
|
|
42
|
+
*/
|
|
43
|
+
private detachedNodeToField: NestedMap<Major, Minor, DetachedField> = new Map();
|
|
44
|
+
/**
|
|
45
|
+
* A map from revisions to all detached fields for which the revision is the latest relevant revision.
|
|
46
|
+
* See {@link DetachedField.latestRelevantRevision}.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* undefined revisions are tolerated but any roots not associated with a revision must be disposed manually
|
|
50
|
+
*/
|
|
51
|
+
private latestRelevantRevisionToFields: NestedMap<
|
|
52
|
+
RevisionTag | undefined,
|
|
53
|
+
ForestRootId,
|
|
54
|
+
Delta.DetachedNodeId
|
|
55
|
+
> = new Map();
|
|
56
|
+
|
|
44
57
|
private readonly codec: IJsonCodec<DetachedFieldSummaryData, Format>;
|
|
45
58
|
private readonly options: ICodecOptions;
|
|
46
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The process for loading `DetachedFieldIndex` data from a summary is split into two steps:
|
|
62
|
+
* 1. Call {@link loadData}
|
|
63
|
+
* 2. Call {@link setRevisionsForLoadedData}
|
|
64
|
+
*
|
|
65
|
+
* This flag is only set to `false` after calling `loadData` and is set back to `true` after calling `setRevisionsForLoadedData`.
|
|
66
|
+
* This helps ensure that `setRevisionsForLoadedData` is only called after `loadData` and only called once.
|
|
67
|
+
*/
|
|
68
|
+
private isFullyLoaded = true;
|
|
69
|
+
|
|
47
70
|
/**
|
|
48
71
|
* @param name - A name for the index, used as a prefix for the generated field keys.
|
|
49
72
|
* @param rootIdAllocator - An ID allocator used to generate unique field keys.
|
|
@@ -68,18 +91,31 @@ export class DetachedFieldIndex {
|
|
|
68
91
|
this.options,
|
|
69
92
|
);
|
|
70
93
|
populateNestedMap(this.detachedNodeToField, clone.detachedNodeToField, true);
|
|
94
|
+
populateNestedMap(
|
|
95
|
+
this.latestRelevantRevisionToFields,
|
|
96
|
+
clone.latestRelevantRevisionToFields,
|
|
97
|
+
true,
|
|
98
|
+
);
|
|
71
99
|
return clone;
|
|
72
100
|
}
|
|
73
101
|
|
|
74
|
-
public *entries(): Generator<
|
|
102
|
+
public *entries(): Generator<
|
|
103
|
+
{ root: ForestRootId; latestRelevantRevision?: RevisionTag } & {
|
|
104
|
+
id: Delta.DetachedNodeId;
|
|
105
|
+
}
|
|
106
|
+
> {
|
|
75
107
|
for (const [major, innerMap] of this.detachedNodeToField) {
|
|
76
108
|
if (major !== undefined) {
|
|
77
|
-
for (const [minor,
|
|
78
|
-
yield
|
|
109
|
+
for (const [minor, { root, latestRelevantRevision }] of innerMap) {
|
|
110
|
+
yield latestRelevantRevision !== undefined
|
|
111
|
+
? { id: { major, minor }, root, latestRelevantRevision }
|
|
112
|
+
: { id: { major, minor }, root };
|
|
79
113
|
}
|
|
80
114
|
} else {
|
|
81
|
-
for (const [minor,
|
|
82
|
-
yield
|
|
115
|
+
for (const [minor, { root, latestRelevantRevision }] of innerMap) {
|
|
116
|
+
yield latestRelevantRevision !== undefined
|
|
117
|
+
? { id: { minor }, root, latestRelevantRevision }
|
|
118
|
+
: { id: { minor }, root };
|
|
83
119
|
}
|
|
84
120
|
}
|
|
85
121
|
}
|
|
@@ -90,22 +126,61 @@ export class DetachedFieldIndex {
|
|
|
90
126
|
*/
|
|
91
127
|
public purge(): void {
|
|
92
128
|
this.detachedNodeToField.clear();
|
|
129
|
+
this.latestRelevantRevisionToFields.clear();
|
|
93
130
|
}
|
|
94
131
|
|
|
95
132
|
public updateMajor(current: Major, updated: Major): void {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
133
|
+
// Update latestRelevantRevision information corresponding to `current`
|
|
134
|
+
{
|
|
135
|
+
const inner = this.latestRelevantRevisionToFields.get(current);
|
|
136
|
+
if (inner !== undefined) {
|
|
137
|
+
for (const nodeId of inner.values()) {
|
|
138
|
+
const entry = tryGetFromNestedMap(
|
|
139
|
+
this.detachedNodeToField,
|
|
140
|
+
nodeId.major,
|
|
141
|
+
nodeId.minor,
|
|
142
|
+
);
|
|
143
|
+
assert(
|
|
144
|
+
entry !== undefined,
|
|
145
|
+
0x9b8 /* Inconsistent data: missing detached node entry */,
|
|
146
|
+
);
|
|
147
|
+
setInNestedMap(this.detachedNodeToField, nodeId.major, nodeId.minor, {
|
|
148
|
+
...entry,
|
|
149
|
+
latestRelevantRevision: updated,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
this.latestRelevantRevisionToFields.delete(current);
|
|
153
|
+
this.latestRelevantRevisionToFields.set(updated, inner);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Update the major keys corresponding to `current`
|
|
158
|
+
{
|
|
159
|
+
const innerCurrent = this.detachedNodeToField.get(current);
|
|
160
|
+
if (innerCurrent !== undefined) {
|
|
161
|
+
this.detachedNodeToField.delete(current);
|
|
162
|
+
const innerUpdated = this.detachedNodeToField.get(updated);
|
|
163
|
+
if (innerUpdated === undefined) {
|
|
164
|
+
this.detachedNodeToField.set(updated, innerCurrent);
|
|
165
|
+
} else {
|
|
166
|
+
for (const [minor, entry] of innerCurrent) {
|
|
167
|
+
assert(
|
|
168
|
+
innerUpdated.get(minor) === undefined,
|
|
169
|
+
0x7a9 /* Collision during index update */,
|
|
170
|
+
);
|
|
171
|
+
innerUpdated.set(minor, entry);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
103
175
|
for (const [minor, entry] of innerCurrent) {
|
|
176
|
+
const entryInLatest = this.latestRelevantRevisionToFields.get(
|
|
177
|
+
entry.latestRelevantRevision,
|
|
178
|
+
);
|
|
104
179
|
assert(
|
|
105
|
-
|
|
106
|
-
|
|
180
|
+
entryInLatest !== undefined,
|
|
181
|
+
0x9b9 /* Inconsistent data: missing node entry in latestRelevantRevision */,
|
|
107
182
|
);
|
|
108
|
-
|
|
183
|
+
entryInLatest.set(entry.root, { major: updated, minor });
|
|
109
184
|
}
|
|
110
185
|
}
|
|
111
186
|
}
|
|
@@ -124,7 +199,7 @@ export class DetachedFieldIndex {
|
|
|
124
199
|
* Returns undefined if no such id is known to the index.
|
|
125
200
|
*/
|
|
126
201
|
public tryGetEntry(id: Delta.DetachedNodeId): ForestRootId | undefined {
|
|
127
|
-
return tryGetFromNestedMap(this.detachedNodeToField, id.major, id.minor);
|
|
202
|
+
return tryGetFromNestedMap(this.detachedNodeToField, id.major, id.minor)?.root;
|
|
128
203
|
}
|
|
129
204
|
|
|
130
205
|
/**
|
|
@@ -137,15 +212,60 @@ export class DetachedFieldIndex {
|
|
|
137
212
|
return key;
|
|
138
213
|
}
|
|
139
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Returns the detached root IDs for all the trees that were detached or last modified by the given revision.
|
|
217
|
+
*/
|
|
218
|
+
public *getRootsLastTouchedByRevision(revision: RevisionTag): Iterable<ForestRootId> {
|
|
219
|
+
const roots = this.latestRelevantRevisionToFields.get(revision);
|
|
220
|
+
if (roots !== undefined) {
|
|
221
|
+
yield* roots.keys();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Removes the detached roots for all the trees that were detached or last modified by the given revision.
|
|
227
|
+
*/
|
|
228
|
+
public deleteRootsLastTouchedByRevision(revision: RevisionTag): void {
|
|
229
|
+
const entries = this.latestRelevantRevisionToFields.get(revision);
|
|
230
|
+
if (entries === undefined) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.latestRelevantRevisionToFields.delete(revision);
|
|
235
|
+
for (const detachedNodeId of entries.values()) {
|
|
236
|
+
const found = deleteFromNestedMap(
|
|
237
|
+
this.detachedNodeToField,
|
|
238
|
+
detachedNodeId.major,
|
|
239
|
+
detachedNodeId.minor,
|
|
240
|
+
);
|
|
241
|
+
assert(found, 0x9ba /* Unable to delete unknown entry */);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
140
245
|
public deleteEntry(nodeId: Delta.DetachedNodeId): void {
|
|
141
|
-
const
|
|
142
|
-
assert(
|
|
246
|
+
const entry = tryGetFromNestedMap(this.detachedNodeToField, nodeId.major, nodeId.minor);
|
|
247
|
+
assert(entry !== undefined, 0x9bb /* Unable to delete unknown entry */);
|
|
248
|
+
deleteFromNestedMap(this.detachedNodeToField, nodeId.major, nodeId.minor);
|
|
249
|
+
deleteFromNestedMap(
|
|
250
|
+
this.latestRelevantRevisionToFields,
|
|
251
|
+
entry.latestRelevantRevision,
|
|
252
|
+
entry.root,
|
|
253
|
+
);
|
|
143
254
|
}
|
|
144
255
|
|
|
145
256
|
/**
|
|
146
257
|
* Associates the DetachedNodeId with a field key and creates an entry for it in the index.
|
|
258
|
+
* @param nodeId - The ID of the detached node.
|
|
259
|
+
* @param revision - The revision that last detached the root.
|
|
260
|
+
* See {@link DetachedField.latestRelevantRevision} for details.
|
|
261
|
+
* @param count - The number of entries to create. These entries will have consecutive minor IDs.
|
|
262
|
+
* @returns The atomic ID that the `DetachedFieldIndex` uses to uniquely identify the first root.
|
|
147
263
|
*/
|
|
148
|
-
public createEntry(
|
|
264
|
+
public createEntry(
|
|
265
|
+
nodeId?: Delta.DetachedNodeId,
|
|
266
|
+
revision?: RevisionTag,
|
|
267
|
+
count: number = 1,
|
|
268
|
+
): ForestRootId {
|
|
149
269
|
const root = this.rootIdAllocator.allocate(count);
|
|
150
270
|
if (nodeId !== undefined) {
|
|
151
271
|
for (let i = 0; i < count; i++) {
|
|
@@ -154,17 +274,46 @@ export class DetachedFieldIndex {
|
|
|
154
274
|
undefined,
|
|
155
275
|
0x7ce /* Detached node ID already exists in index */,
|
|
156
276
|
);
|
|
157
|
-
setInNestedMap(this.detachedNodeToField, nodeId.major, nodeId.minor + i,
|
|
277
|
+
setInNestedMap(this.detachedNodeToField, nodeId.major, nodeId.minor + i, {
|
|
278
|
+
root: brand<ForestRootId>(root + i),
|
|
279
|
+
latestRelevantRevision: revision,
|
|
280
|
+
});
|
|
281
|
+
setInNestedMap(this.latestRelevantRevisionToFields, revision, root, nodeId);
|
|
158
282
|
}
|
|
159
283
|
}
|
|
160
284
|
return root;
|
|
161
285
|
}
|
|
162
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Updates the latest revision that is relevant to the provided root
|
|
289
|
+
*/
|
|
290
|
+
public updateLatestRevision(
|
|
291
|
+
id: Delta.DetachedNodeId,
|
|
292
|
+
revision: RevisionTag | undefined,
|
|
293
|
+
): void {
|
|
294
|
+
const fieldEntry = tryGetFromNestedMap(this.detachedNodeToField, id.major, id.minor);
|
|
295
|
+
assert(
|
|
296
|
+
fieldEntry !== undefined,
|
|
297
|
+
0x9bc /* detached node id does not exist in the detached field index */,
|
|
298
|
+
);
|
|
299
|
+
const { root, latestRelevantRevision: previousRevision } = fieldEntry;
|
|
300
|
+
|
|
301
|
+
// remove this root from the set of roots for the previous latest revision
|
|
302
|
+
deleteFromNestedMap(this.latestRelevantRevisionToFields, previousRevision, root);
|
|
303
|
+
|
|
304
|
+
// add this root to the set of roots for the new latest revision
|
|
305
|
+
setInNestedMap(this.latestRelevantRevisionToFields, revision, root, id);
|
|
306
|
+
setInNestedMap(this.detachedNodeToField, id.major, id.minor, {
|
|
307
|
+
root,
|
|
308
|
+
latestRelevantRevision: revision,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
163
312
|
public encode(): JsonCompatibleReadOnly {
|
|
164
313
|
return this.codec.encode({
|
|
165
314
|
data: this.detachedNodeToField,
|
|
166
315
|
maxId: this.rootIdAllocator.getMaxId(),
|
|
167
|
-
})
|
|
316
|
+
});
|
|
168
317
|
}
|
|
169
318
|
|
|
170
319
|
/**
|
|
@@ -176,6 +325,39 @@ export class DetachedFieldIndex {
|
|
|
176
325
|
this.rootIdAllocator = idAllocatorFromMaxId(
|
|
177
326
|
detachedFieldIndex.maxId,
|
|
178
327
|
) as IdAllocator<ForestRootId>;
|
|
179
|
-
|
|
328
|
+
|
|
329
|
+
this.detachedNodeToField = new Map();
|
|
330
|
+
this.latestRelevantRevisionToFields = new Map();
|
|
331
|
+
this.isFullyLoaded = false;
|
|
332
|
+
const rootMap = new Map<ForestRootId, Delta.DetachedNodeId>();
|
|
333
|
+
forEachInNestedMap(detachedFieldIndex.data, ({ root }, major, minor) => {
|
|
334
|
+
rootMap.set(root, { major, minor });
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
this.latestRelevantRevisionToFields.set(undefined, rootMap);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Sets the latest relevant revision for any roots that have an undefined latest relevant revision.
|
|
342
|
+
* This occurs when the detached field index is loaded from a summary and can only be called once after
|
|
343
|
+
* the summary has been loaded.
|
|
344
|
+
*/
|
|
345
|
+
public setRevisionsForLoadedData(latestRevision: RevisionTag): void {
|
|
346
|
+
assert(
|
|
347
|
+
!this.isFullyLoaded,
|
|
348
|
+
0x9bd /* revisions should only be set once using this function after loading data from a summary */,
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const newDetachedNodeToField = new Map();
|
|
352
|
+
const rootMap = new Map();
|
|
353
|
+
forEachInNestedMap(this.detachedNodeToField, ({ root }, major, minor) => {
|
|
354
|
+
setInNestedMap(newDetachedNodeToField, major, minor, { root, latestRevision });
|
|
355
|
+
rootMap.set(root, { major, minor });
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
this.detachedNodeToField = newDetachedNodeToField;
|
|
359
|
+
this.latestRelevantRevisionToFields.delete(undefined);
|
|
360
|
+
this.latestRelevantRevisionToFields.set(latestRevision, rootMap);
|
|
361
|
+
this.isFullyLoaded = true;
|
|
180
362
|
}
|
|
181
363
|
}
|
|
@@ -12,14 +12,17 @@ import {
|
|
|
12
12
|
} from "../../codec/index.js";
|
|
13
13
|
import type { EncodedRevisionTag, RevisionTagCodec, RevisionTag } from "../rebase/index.js";
|
|
14
14
|
|
|
15
|
-
import type { ForestRootId } from "./detachedFieldIndex.js";
|
|
16
15
|
import {
|
|
17
16
|
type EncodedRootsForRevision,
|
|
18
17
|
Format,
|
|
19
18
|
type RootRanges,
|
|
20
19
|
version,
|
|
21
20
|
} from "./detachedFieldIndexFormat.js";
|
|
22
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
DetachedField,
|
|
23
|
+
DetachedFieldSummaryData,
|
|
24
|
+
Major,
|
|
25
|
+
} from "./detachedFieldIndexTypes.js";
|
|
23
26
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
24
27
|
|
|
25
28
|
class MajorCodec implements IJsonCodec<Major> {
|
|
@@ -76,7 +79,10 @@ export function makeDetachedNodeToFieldCodec(
|
|
|
76
79
|
const rootsForRevisions: EncodedRootsForRevision[] = [];
|
|
77
80
|
for (const [major, innerMap] of data.data) {
|
|
78
81
|
const encodedRevision = majorCodec.encode(major);
|
|
79
|
-
const rootRanges: RootRanges = [
|
|
82
|
+
const rootRanges: RootRanges = [];
|
|
83
|
+
for (const [minor, detachedField] of innerMap) {
|
|
84
|
+
rootRanges.push([minor, detachedField.root]);
|
|
85
|
+
}
|
|
80
86
|
if (rootRanges.length === 1) {
|
|
81
87
|
const rootsForRevision: EncodedRootsForRevision = [
|
|
82
88
|
encodedRevision,
|
|
@@ -99,11 +105,14 @@ export function makeDetachedNodeToFieldCodec(
|
|
|
99
105
|
decode: (parsed: Format): DetachedFieldSummaryData => {
|
|
100
106
|
const map = new Map();
|
|
101
107
|
for (const rootsForRevision of parsed.data) {
|
|
102
|
-
const innerMap = new Map<number,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
const innerMap = new Map<number, DetachedField>();
|
|
109
|
+
if (rootsForRevision.length === 2) {
|
|
110
|
+
for (const [minor, root] of rootsForRevision[1]) {
|
|
111
|
+
innerMap.set(minor, { root });
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
innerMap.set(rootsForRevision[1], { root: rootsForRevision[2] });
|
|
115
|
+
}
|
|
107
116
|
map.set(majorCodec.decode(rootsForRevision[0]), innerMap);
|
|
108
117
|
}
|
|
109
118
|
return {
|
|
@@ -8,7 +8,7 @@ import { type Static, Type } from "@sinclair/typebox";
|
|
|
8
8
|
import { brandedNumberType } from "../../util/index.js";
|
|
9
9
|
import { RevisionTagSchema } from "../rebase/index.js";
|
|
10
10
|
|
|
11
|
-
import type { ForestRootId } from "./
|
|
11
|
+
import type { ForestRootId } from "./detachedFieldIndexTypes.js";
|
|
12
12
|
|
|
13
13
|
export const version = 1.0;
|
|
14
14
|
|
|
@@ -3,15 +3,51 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { Brand, ReadonlyNestedMap } from "../../util/index.js";
|
|
7
7
|
import type { RevisionTag } from "../rebase/index.js";
|
|
8
8
|
|
|
9
|
-
import type { ForestRootId } from "./detachedFieldIndex.js";
|
|
10
|
-
|
|
11
9
|
export type Major = RevisionTag | undefined;
|
|
12
10
|
export type Minor = number;
|
|
13
11
|
|
|
14
12
|
export interface DetachedFieldSummaryData {
|
|
15
|
-
data:
|
|
16
|
-
maxId: ForestRootId;
|
|
13
|
+
readonly data: ReadonlyNestedMap<Major, Minor, DetachedField>;
|
|
14
|
+
readonly maxId: ForestRootId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ID used to create a detached field key for a removed subtree.
|
|
19
|
+
*
|
|
20
|
+
* TODO: Move to Forest once forests can support multiple roots.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export type ForestRootId = Brand<number, "tree.ForestRootId">;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A field that is detached from the main document tree.
|
|
27
|
+
*/
|
|
28
|
+
export interface DetachedField {
|
|
29
|
+
/**
|
|
30
|
+
* The atomic ID that the `DetachedFieldIndex` uses to uniquely identify the first (and only) root in the field.
|
|
31
|
+
* This ID is scoped to the specific `DetachedFieldIndex` from witch this object was retrieved.
|
|
32
|
+
*
|
|
33
|
+
* The current implementation only supports a single root per field.
|
|
34
|
+
* This will be changed in the future for performance reasons.
|
|
35
|
+
*/
|
|
36
|
+
readonly root: ForestRootId;
|
|
37
|
+
/**
|
|
38
|
+
* The revision that last detached the root node or modified its contents (including its descendant's contents).
|
|
39
|
+
*
|
|
40
|
+
* Once this revision is trimmed from the ancestry on which a `TreeCheckout` is moored,
|
|
41
|
+
* the contents of the associated subtree (and the very fact of its past existence) can be erased.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* undefined revisions are tolerated but any roots not associated with a revision must be disposed manually.
|
|
45
|
+
* Current usages of undefined are:
|
|
46
|
+
* - When loading a {@link DetachedFieldIndex} from a snapshot,
|
|
47
|
+
* until {@link DetachedFieldIndex.setRevisionsForLoadedData} is called.
|
|
48
|
+
* - When applying a rollback changeset.
|
|
49
|
+
* This only occurs within the context of {@link DefaultResubmitMachine} whose repair data is GC-ed when its
|
|
50
|
+
* `DetachedField` and `Forest` are GC-ed.
|
|
51
|
+
*/
|
|
52
|
+
readonly latestRelevantRevision?: RevisionTag;
|
|
17
53
|
}
|
package/src/core/tree/index.ts
CHANGED
|
@@ -110,4 +110,5 @@ export {
|
|
|
110
110
|
emptyDelta,
|
|
111
111
|
} from "./deltaUtil.js";
|
|
112
112
|
|
|
113
|
-
export { DetachedFieldIndex
|
|
113
|
+
export { DetachedFieldIndex } from "./detachedFieldIndex.js";
|
|
114
|
+
export { type ForestRootId } from "./detachedFieldIndexTypes.js";
|
|
@@ -21,9 +21,10 @@ import {
|
|
|
21
21
|
isReplaceMark,
|
|
22
22
|
offsetDetachId,
|
|
23
23
|
} from "./deltaUtil.js";
|
|
24
|
-
import type { DetachedFieldIndex
|
|
25
|
-
import type { Major, Minor } from "./detachedFieldIndexTypes.js";
|
|
24
|
+
import type { DetachedFieldIndex } from "./detachedFieldIndex.js";
|
|
25
|
+
import type { ForestRootId, Major, Minor } from "./detachedFieldIndexTypes.js";
|
|
26
26
|
import type { NodeIndex, PlaceIndex, Range } from "./pathTree.js";
|
|
27
|
+
import type { RevisionTag } from "../index.js";
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Implementation notes:
|
|
@@ -74,11 +75,13 @@ import type { NodeIndex, PlaceIndex, Range } from "./pathTree.js";
|
|
|
74
75
|
* @param delta - The delta to be crawled.
|
|
75
76
|
* @param visitor - The object to notify of the changes encountered.
|
|
76
77
|
* @param detachedFieldIndex - Index responsible for keeping track of the existing detached fields.
|
|
78
|
+
* @param latestRevision - The latest revision tag associated with this delta.
|
|
77
79
|
*/
|
|
78
80
|
export function visitDelta(
|
|
79
81
|
delta: Delta.Root,
|
|
80
82
|
visitor: DeltaVisitor,
|
|
81
83
|
detachedFieldIndex: DetachedFieldIndex,
|
|
84
|
+
latestRevision: RevisionTag | undefined,
|
|
82
85
|
): void {
|
|
83
86
|
const detachPassRoots: Map<ForestRootId, Delta.FieldMap> = new Map();
|
|
84
87
|
const attachPassRoots: Map<ForestRootId, Delta.FieldMap> = new Map();
|
|
@@ -93,6 +96,7 @@ export function visitDelta(
|
|
|
93
96
|
});
|
|
94
97
|
const detachConfig: PassConfig = {
|
|
95
98
|
func: detachPass,
|
|
99
|
+
latestRevision,
|
|
96
100
|
refreshers,
|
|
97
101
|
detachedFieldIndex,
|
|
98
102
|
detachPassRoots,
|
|
@@ -103,9 +107,17 @@ export function visitDelta(
|
|
|
103
107
|
processBuilds(delta.build, detachConfig, visitor);
|
|
104
108
|
visitFieldMarks(delta.fields, visitor, detachConfig);
|
|
105
109
|
fixedPointVisitOfRoots(visitor, detachPassRoots, detachConfig);
|
|
106
|
-
transferRoots(
|
|
110
|
+
transferRoots(
|
|
111
|
+
rootTransfers,
|
|
112
|
+
attachPassRoots,
|
|
113
|
+
detachedFieldIndex,
|
|
114
|
+
visitor,
|
|
115
|
+
refreshers,
|
|
116
|
+
latestRevision,
|
|
117
|
+
);
|
|
107
118
|
const attachConfig: PassConfig = {
|
|
108
119
|
func: attachPass,
|
|
120
|
+
latestRevision,
|
|
109
121
|
refreshers,
|
|
110
122
|
detachedFieldIndex,
|
|
111
123
|
detachPassRoots,
|
|
@@ -154,6 +166,10 @@ function fixedPointVisitOfRoots(
|
|
|
154
166
|
|
|
155
167
|
/**
|
|
156
168
|
* Transfers roots from one detached field to another.
|
|
169
|
+
* This occurs in the following circumstances:
|
|
170
|
+
* - A changeset moves then removes a node
|
|
171
|
+
* - A changeset restores then moves a node
|
|
172
|
+
* - A changeset restores then removes a node
|
|
157
173
|
* TODO#5481: update the DetachedFieldIndex instead of moving the nodes around.
|
|
158
174
|
*
|
|
159
175
|
* @param rootTransfers - The transfers to perform.
|
|
@@ -166,6 +182,8 @@ function transferRoots(
|
|
|
166
182
|
mapToUpdate: Map<ForestRootId, unknown>,
|
|
167
183
|
detachedFieldIndex: DetachedFieldIndex,
|
|
168
184
|
visitor: DeltaVisitor,
|
|
185
|
+
refreshers: NestedMap<Major, Minor, ITreeCursorSynchronous>,
|
|
186
|
+
revision?: RevisionTag,
|
|
169
187
|
): void {
|
|
170
188
|
type AtomizedNodeRename = Omit<Delta.DetachedNodeRename, "count">;
|
|
171
189
|
let nextBatch = rootTransfers.flatMap(({ oldId, newId, count }) => {
|
|
@@ -186,7 +204,14 @@ function transferRoots(
|
|
|
186
204
|
const delayed: AtomizedNodeRename[] = [];
|
|
187
205
|
const priorSize = nextBatch.length;
|
|
188
206
|
for (const { oldId, newId } of nextBatch) {
|
|
189
|
-
|
|
207
|
+
let oldRootId = detachedFieldIndex.tryGetEntry(oldId);
|
|
208
|
+
if (oldRootId === undefined) {
|
|
209
|
+
const tree = tryGetFromNestedMap(refreshers, oldId.major, oldId.minor);
|
|
210
|
+
if (tree !== undefined) {
|
|
211
|
+
buildTrees(oldId, [tree], detachedFieldIndex, revision, visitor);
|
|
212
|
+
oldRootId = detachedFieldIndex.getEntry(oldId);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
190
215
|
if (oldRootId === undefined) {
|
|
191
216
|
// The source field is not populated.
|
|
192
217
|
// This can happen when another rename needs to be performed first.
|
|
@@ -200,7 +225,7 @@ function transferRoots(
|
|
|
200
225
|
delayed.push({ oldId, newId });
|
|
201
226
|
continue;
|
|
202
227
|
}
|
|
203
|
-
newRootId = detachedFieldIndex.createEntry(newId);
|
|
228
|
+
newRootId = detachedFieldIndex.createEntry(newId, revision);
|
|
204
229
|
const fields = mapToUpdate.get(oldRootId);
|
|
205
230
|
if (fields !== undefined) {
|
|
206
231
|
mapToUpdate.delete(oldRootId);
|
|
@@ -311,6 +336,13 @@ export interface DeltaVisitor {
|
|
|
311
336
|
|
|
312
337
|
interface PassConfig {
|
|
313
338
|
readonly func: Pass;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* The latest revision tag associated with the given delta. This is used to keep track
|
|
342
|
+
* of when repair data should be garbage collected.
|
|
343
|
+
*/
|
|
344
|
+
readonly latestRevision: RevisionTag | undefined;
|
|
345
|
+
|
|
314
346
|
readonly detachedFieldIndex: DetachedFieldIndex;
|
|
315
347
|
/**
|
|
316
348
|
* A mapping between forest root id and trees that represent refresher data. Each entry is only
|
|
@@ -394,9 +426,11 @@ function detachPass(
|
|
|
394
426
|
if (root === undefined) {
|
|
395
427
|
const tree = tryGetFromNestedMap(config.refreshers, id.major, id.minor);
|
|
396
428
|
assert(tree !== undefined, 0x928 /* refresher data not found */);
|
|
397
|
-
buildTrees(id, [tree], config, visitor);
|
|
429
|
+
buildTrees(id, [tree], config.detachedFieldIndex, config.latestRevision, visitor);
|
|
398
430
|
root = config.detachedFieldIndex.getEntry(id);
|
|
399
431
|
}
|
|
432
|
+
// the revision is updated for any refresher data included in the delta that is used
|
|
433
|
+
config.detachedFieldIndex.updateLatestRevision(id, config.latestRevision);
|
|
400
434
|
config.detachPassRoots.set(root, fields);
|
|
401
435
|
config.attachPassRoots.set(root, fields);
|
|
402
436
|
}
|
|
@@ -416,10 +450,9 @@ function detachPass(
|
|
|
416
450
|
}
|
|
417
451
|
if (isDetachMark(mark)) {
|
|
418
452
|
for (let i = 0; i < mark.count; i += 1) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
);
|
|
453
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
454
|
+
const id = offsetDetachId(mark.detach!, i);
|
|
455
|
+
const root = config.detachedFieldIndex.createEntry(id, config.latestRevision);
|
|
423
456
|
if (mark.fields !== undefined) {
|
|
424
457
|
config.attachPassRoots.set(root, mark.fields);
|
|
425
458
|
}
|
|
@@ -436,15 +469,16 @@ function detachPass(
|
|
|
436
469
|
function buildTrees(
|
|
437
470
|
id: Delta.DetachedNodeId,
|
|
438
471
|
trees: readonly ITreeCursorSynchronous[],
|
|
439
|
-
|
|
472
|
+
detachedFieldIndex: DetachedFieldIndex,
|
|
473
|
+
latestRevision: RevisionTag | undefined,
|
|
440
474
|
visitor: DeltaVisitor,
|
|
441
475
|
): void {
|
|
442
476
|
for (let i = 0; i < trees.length; i += 1) {
|
|
443
477
|
const offsettedId = offsetDetachId(id, i);
|
|
444
|
-
let root =
|
|
478
|
+
let root = detachedFieldIndex.tryGetEntry(offsettedId);
|
|
445
479
|
assert(root === undefined, 0x929 /* Unable to build tree that already exists */);
|
|
446
|
-
root =
|
|
447
|
-
const field =
|
|
480
|
+
root = detachedFieldIndex.createEntry(offsettedId, latestRevision);
|
|
481
|
+
const field = detachedFieldIndex.toFieldKey(root);
|
|
448
482
|
visitor.create([trees[i]], field);
|
|
449
483
|
}
|
|
450
484
|
}
|
|
@@ -456,7 +490,7 @@ function processBuilds(
|
|
|
456
490
|
): void {
|
|
457
491
|
if (builds !== undefined) {
|
|
458
492
|
for (const { id, trees } of builds) {
|
|
459
|
-
buildTrees(id, trees, config, visitor);
|
|
493
|
+
buildTrees(id, trees, config.detachedFieldIndex, config.latestRevision, visitor);
|
|
460
494
|
}
|
|
461
495
|
}
|
|
462
496
|
}
|
|
@@ -496,7 +530,13 @@ function attachPass(
|
|
|
496
530
|
offsetAttachId.minor,
|
|
497
531
|
);
|
|
498
532
|
assert(tree !== undefined, 0x92a /* refresher data not found */);
|
|
499
|
-
buildTrees(
|
|
533
|
+
buildTrees(
|
|
534
|
+
offsetAttachId,
|
|
535
|
+
[tree],
|
|
536
|
+
config.detachedFieldIndex,
|
|
537
|
+
config.latestRevision,
|
|
538
|
+
visitor,
|
|
539
|
+
);
|
|
500
540
|
sourceRoot = config.detachedFieldIndex.getEntry(offsetAttachId);
|
|
501
541
|
}
|
|
502
542
|
const sourceField = config.detachedFieldIndex.toFieldKey(sourceRoot);
|
|
@@ -505,6 +545,7 @@ function attachPass(
|
|
|
505
545
|
const rootDestination = config.detachedFieldIndex.createEntry(
|
|
506
546
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
507
547
|
offsetDetachId(mark.detach!, i),
|
|
548
|
+
config.latestRevision,
|
|
508
549
|
);
|
|
509
550
|
const destinationField = config.detachedFieldIndex.toFieldKey(rootDestination);
|
|
510
551
|
visitor.replace(
|