@fluidframework/tree 2.63.0-359734 → 2.63.0-359962
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api-report/tree.alpha.api.md +35 -0
- package/dist/alpha.d.ts +7 -0
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts +67 -13
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js +70 -35
- package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +5 -5
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.d.ts +23 -0
- package/dist/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.d.ts.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.js +15 -0
- package/dist/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.js.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/index.d.ts +1 -0
- package/dist/feature-libraries/chunked-forest/codec/index.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/index.js +3 -1
- package/dist/feature-libraries/chunked-forest/codec/index.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +3 -2
- package/dist/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/index.d.ts +1 -1
- package/dist/feature-libraries/chunked-forest/index.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/index.js +2 -1
- package/dist/feature-libraries/chunked-forest/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts +3 -3
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +3 -3
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/forest-summary/incrementalSummaryBuilder.d.ts +4 -11
- package/dist/feature-libraries/forest-summary/incrementalSummaryBuilder.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/incrementalSummaryBuilder.js +2 -6
- package/dist/feature-libraries/forest-summary/incrementalSummaryBuilder.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 +2 -1
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/independentView.d.ts.map +1 -1
- package/dist/shared-tree/independentView.js +2 -2
- package/dist/shared-tree/independentView.js.map +1 -1
- package/dist/shared-tree/schematizingTreeView.d.ts +5 -0
- package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/dist/shared-tree/schematizingTreeView.js +37 -7
- package/dist/shared-tree/schematizingTreeView.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +6 -8
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +6 -8
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/tree.js +1 -1
- package/dist/shared-tree/tree.js.map +1 -1
- package/dist/shared-tree/treeAlpha.js +2 -2
- package/dist/shared-tree/treeAlpha.js.map +1 -1
- package/dist/simple-tree/api/index.d.ts +1 -1
- package/dist/simple-tree/api/index.d.ts.map +1 -1
- package/dist/simple-tree/api/index.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryAlpha.d.ts +37 -2
- package/dist/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaFactoryAlpha.js +34 -14
- package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
- package/dist/simple-tree/api/schemaStatics.js +3 -0
- package/dist/simple-tree/api/schemaStatics.js.map +1 -1
- package/dist/simple-tree/api/treeBeta.js +2 -2
- package/dist/simple-tree/api/treeBeta.js.map +1 -1
- package/dist/simple-tree/api/treeNodeApi.js +3 -3
- package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/api/typesUnsafe.d.ts +60 -1
- package/dist/simple-tree/api/typesUnsafe.d.ts.map +1 -1
- package/dist/simple-tree/api/typesUnsafe.js.map +1 -1
- package/dist/simple-tree/core/index.d.ts +1 -1
- package/dist/simple-tree/core/index.d.ts.map +1 -1
- package/dist/simple-tree/core/index.js +2 -2
- package/dist/simple-tree/core/index.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts +7 -11
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +24 -47
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/simple-tree/getTreeNodeForField.js +1 -1
- package/dist/simple-tree/getTreeNodeForField.js.map +1 -1
- package/dist/simple-tree/index.d.ts +2 -2
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +2 -2
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +2 -2
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.js +2 -2
- package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/object/objectNode.js +3 -3
- package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/record/recordNode.js +6 -6
- package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/dist/simple-tree/prepareForInsertion.d.ts +36 -4
- package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
- package/dist/simple-tree/prepareForInsertion.js +43 -15
- package/dist/simple-tree/prepareForInsertion.js.map +1 -1
- package/lib/alpha.d.ts +7 -0
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts +67 -13
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js +68 -32
- package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +5 -5
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.d.ts +23 -0
- package/lib/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.d.ts.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.js +11 -0
- package/lib/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.js.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/index.d.ts +1 -0
- package/lib/feature-libraries/chunked-forest/codec/index.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/index.js +1 -0
- package/lib/feature-libraries/chunked-forest/codec/index.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js +3 -2
- package/lib/feature-libraries/chunked-forest/codec/schemaBasedEncode.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/index.d.ts +1 -1
- package/lib/feature-libraries/chunked-forest/index.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/index.js +1 -1
- package/lib/feature-libraries/chunked-forest/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts +3 -3
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +3 -3
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/forest-summary/incrementalSummaryBuilder.d.ts +4 -11
- package/lib/feature-libraries/forest-summary/incrementalSummaryBuilder.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/incrementalSummaryBuilder.js +2 -6
- package/lib/feature-libraries/forest-summary/incrementalSummaryBuilder.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/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/independentView.d.ts.map +1 -1
- package/lib/shared-tree/independentView.js +3 -3
- package/lib/shared-tree/independentView.js.map +1 -1
- package/lib/shared-tree/schematizingTreeView.d.ts +5 -0
- package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
- package/lib/shared-tree/schematizingTreeView.js +40 -10
- package/lib/shared-tree/schematizingTreeView.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +6 -8
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +7 -9
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/tree.js +2 -2
- package/lib/shared-tree/tree.js.map +1 -1
- package/lib/shared-tree/treeAlpha.js +3 -3
- package/lib/shared-tree/treeAlpha.js.map +1 -1
- package/lib/simple-tree/api/index.d.ts +1 -1
- package/lib/simple-tree/api/index.d.ts.map +1 -1
- package/lib/simple-tree/api/index.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryAlpha.d.ts +37 -2
- package/lib/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaFactoryAlpha.js +34 -14
- package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
- package/lib/simple-tree/api/schemaStatics.js +3 -0
- package/lib/simple-tree/api/schemaStatics.js.map +1 -1
- package/lib/simple-tree/api/treeBeta.js +2 -2
- package/lib/simple-tree/api/treeBeta.js.map +1 -1
- package/lib/simple-tree/api/treeNodeApi.js +4 -4
- package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/api/typesUnsafe.d.ts +60 -1
- package/lib/simple-tree/api/typesUnsafe.d.ts.map +1 -1
- package/lib/simple-tree/api/typesUnsafe.js.map +1 -1
- package/lib/simple-tree/core/index.d.ts +1 -1
- package/lib/simple-tree/core/index.d.ts.map +1 -1
- package/lib/simple-tree/core/index.js +1 -1
- package/lib/simple-tree/core/index.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts +7 -11
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +23 -46
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/simple-tree/getTreeNodeForField.js +1 -1
- package/lib/simple-tree/getTreeNodeForField.js.map +1 -1
- package/lib/simple-tree/index.d.ts +2 -2
- 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/node-kinds/array/arrayNode.js +3 -3
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.js +3 -3
- package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/object/objectNode.js +4 -4
- package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/record/recordNode.js +7 -7
- package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/lib/simple-tree/prepareForInsertion.d.ts +36 -4
- package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
- package/lib/simple-tree/prepareForInsertion.js +45 -17
- package/lib/simple-tree/prepareForInsertion.js.map +1 -1
- package/package.json +20 -20
- package/src/feature-libraries/chunked-forest/chunkTree.ts +112 -45
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +4 -9
- package/src/feature-libraries/chunked-forest/codec/incrementalEncodingPolicy.ts +33 -0
- package/src/feature-libraries/chunked-forest/codec/index.ts +4 -0
- package/src/feature-libraries/chunked-forest/codec/schemaBasedEncode.ts +4 -7
- package/src/feature-libraries/chunked-forest/index.ts +2 -0
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +1 -1
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +8 -12
- package/src/feature-libraries/forest-summary/incrementalSummaryBuilder.ts +3 -12
- package/src/feature-libraries/index.ts +2 -0
- package/src/index.ts +7 -0
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/independentView.ts +3 -0
- package/src/shared-tree/schematizingTreeView.ts +57 -8
- package/src/shared-tree/sharedTree.ts +33 -20
- package/src/shared-tree/tree.ts +2 -2
- package/src/shared-tree/treeAlpha.ts +3 -3
- package/src/simple-tree/api/index.ts +7 -0
- package/src/simple-tree/api/schemaFactoryAlpha.ts +79 -19
- package/src/simple-tree/api/schemaStatics.ts +3 -0
- package/src/simple-tree/api/treeBeta.ts +2 -2
- package/src/simple-tree/api/treeNodeApi.ts +4 -4
- package/src/simple-tree/api/typesUnsafe.ts +81 -0
- package/src/simple-tree/core/TreeNodeBinding.md +14 -70
- package/src/simple-tree/core/index.ts +1 -1
- package/src/simple-tree/core/treeNodeKernel.ts +25 -59
- package/src/simple-tree/getTreeNodeForField.ts +1 -1
- package/src/simple-tree/index.ts +8 -1
- package/src/simple-tree/node-kinds/array/arrayNode.ts +3 -3
- package/src/simple-tree/node-kinds/map/mapNode.ts +3 -3
- package/src/simple-tree/node-kinds/object/objectNode.ts +4 -4
- package/src/simple-tree/node-kinds/record/recordNode.ts +7 -7
- package/src/simple-tree/prepareForInsertion.ts +87 -25
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { getSchemaAndPolicy, FieldKinds, throwOutOfSchema, } from "../feature-libraries/index.js";
|
|
5
|
+
import { getSchemaAndPolicy, FieldKinds, throwOutOfSchema, flexTreeSlot, ContextSlot, getOrCreateHydratedFlexTreeNode, assertFlexTreeEntityNotFreed, } from "../feature-libraries/index.js";
|
|
6
6
|
import { unhydratedFlexTreeFromInsertable, } from "./unhydratedFlexTreeFromInsertable.js";
|
|
7
7
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
import { brand } from "../util/index.js";
|
|
9
9
|
import { getKernel, } from "./core/index.js";
|
|
10
|
-
import { debugAssert, oob } from "@fluidframework/core-utils/internal";
|
|
10
|
+
import { debugAssert, fail, oob } from "@fluidframework/core-utils/internal";
|
|
11
11
|
import { isFieldInSchema } from "../feature-libraries/index.js";
|
|
12
12
|
/**
|
|
13
13
|
* For now, schema validation for inserted content is always enabled.
|
|
@@ -58,10 +58,10 @@ export function prepareArrayContentForInsertion(data, schema, destinationContext
|
|
|
58
58
|
* @remarks
|
|
59
59
|
* Adding this entry point is a workaround for initialize not currently having a context.
|
|
60
60
|
*/
|
|
61
|
-
export function prepareForInsertionContextless(data, schema, schemaAndPolicy, hydratedData, destinationSchema) {
|
|
61
|
+
export function prepareForInsertionContextless(data, schema, schemaAndPolicy, hydratedData, destinationSchema, scheduleHydrationOverride) {
|
|
62
62
|
const mapTree = unhydratedFlexTreeFromInsertable(data, schema);
|
|
63
63
|
const contentArray = mapTree === undefined ? [] : [mapTree];
|
|
64
|
-
validateAndPrepare(schemaAndPolicy, hydratedData, destinationSchema, contentArray);
|
|
64
|
+
validateAndPrepare(schemaAndPolicy, hydratedData, destinationSchema, contentArray, scheduleHydrationOverride);
|
|
65
65
|
return mapTree;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
@@ -70,12 +70,13 @@ export function prepareForInsertionContextless(data, schema, schemaAndPolicy, hy
|
|
|
70
70
|
* @param hydratedData - If specified, the `mapTrees` will be prepared for hydration into this context.
|
|
71
71
|
* `undefined` when `mapTrees` are being inserted into an {@link Unhydrated} tree.
|
|
72
72
|
*/
|
|
73
|
-
function validateAndPrepare(schemaAndPolicy, hydratedData, fieldSchema, mapTrees) {
|
|
73
|
+
function validateAndPrepare(schemaAndPolicy, hydratedData, fieldSchema, mapTrees, scheduleHydrationOverride) {
|
|
74
74
|
if (hydratedData !== undefined) {
|
|
75
75
|
// Run `prepareContentForHydration` before walking the tree in `isFieldInSchema`.
|
|
76
76
|
// This ensures that when `isFieldInSchema` requests identifiers (or any other contextual defaults),
|
|
77
77
|
// they were already creating used the more specific context we have access to from `hydratedData`.
|
|
78
|
-
prepareContentForHydration(mapTrees,
|
|
78
|
+
prepareContentForHydration(mapTrees, scheduleHydrationOverride ??
|
|
79
|
+
((batch, doHydration) => scheduleHydration(batch, hydratedData.checkout.forest, doHydration)), hydratedData);
|
|
79
80
|
// TODO: AB#45723
|
|
80
81
|
// Now that staged schema rely on this validation, its a bit odd we don't do it for insertion into unhydrated contexts.
|
|
81
82
|
// We can't simply enable it for them however due to contextual default fields which would not have been created yet (see comment above).
|
|
@@ -101,7 +102,7 @@ const placeholderKey = brand("placeholder");
|
|
|
101
102
|
* @param content - the content subsequence to be inserted, of which might deeply contain {@link TreeNode}s which need to be hydrated.
|
|
102
103
|
* @param forest - the forest the content is being inserted into.
|
|
103
104
|
*/
|
|
104
|
-
export function prepareContentForHydration(content,
|
|
105
|
+
export function prepareContentForHydration(content, scheduler, context) {
|
|
105
106
|
const batches = [];
|
|
106
107
|
for (const item of content) {
|
|
107
108
|
const batch = {
|
|
@@ -117,7 +118,8 @@ export function prepareContentForHydration(content, forest, context) {
|
|
|
117
118
|
batch.paths.push({ path: p, node });
|
|
118
119
|
}, context);
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
+
const doHydration = hydrator(batches, context.checkout.forest);
|
|
122
|
+
scheduler(batches, doHydration);
|
|
121
123
|
}
|
|
122
124
|
function walkMapTree(root, path, onVisitTreeNode, context) {
|
|
123
125
|
if (root.parentField.parent.parent !== undefined) {
|
|
@@ -154,23 +156,49 @@ function walkMapTree(root, path, onVisitTreeNode, context) {
|
|
|
154
156
|
* Each index in this array expects its content to be added and produce its own `afterRootFieldCreated` event.
|
|
155
157
|
* If array subsequence insertion is optimized to produce a single event, this will not work correctly as is, and will need to be modified to take in a single {@link LocatedNodesBatch}.
|
|
156
158
|
*/
|
|
157
|
-
function scheduleHydration(locatedNodes, forest) {
|
|
159
|
+
function scheduleHydration(locatedNodes, forest, doHydration) {
|
|
158
160
|
// Only subscribe to the event if there is at least one TreeNode tree to hydrate - this is not the case when inserting an empty array [].
|
|
159
161
|
if (locatedNodes.length > 0) {
|
|
160
162
|
// Creating a new array emits one event per element in the array, so listen to the event once for each element
|
|
161
|
-
let
|
|
163
|
+
let index = 0;
|
|
162
164
|
const off = forest.events.on("afterRootFieldCreated", (fieldKey) => {
|
|
163
165
|
// Indexing is safe here because of the length check above. This assumes the array has not been modified which should be the case.
|
|
164
|
-
const batch = locatedNodes[
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
for (const { path, node } of batch.paths) {
|
|
168
|
-
getKernel(node).hydrate(forest.anchors, path);
|
|
169
|
-
}
|
|
170
|
-
if (++i === locatedNodes.length) {
|
|
166
|
+
const batch = locatedNodes[index] ?? oob();
|
|
167
|
+
doHydration(batch, { parent: undefined, parentField: fieldKey, parentIndex: 0 });
|
|
168
|
+
if (++index === locatedNodes.length) {
|
|
171
169
|
off();
|
|
172
170
|
}
|
|
173
171
|
});
|
|
174
172
|
}
|
|
175
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Implementation of {@link Hydrator}.
|
|
176
|
+
*/
|
|
177
|
+
function hydrator(locatedNodes, forest) {
|
|
178
|
+
return (batch, attachedPath) => {
|
|
179
|
+
const context = forest.anchors.slots.get(ContextSlot) ?? fail(0xb41 /* missing context */);
|
|
180
|
+
// Modify paths in batch to point to correct location:
|
|
181
|
+
debugAssert(() => batch.rootPath.parentField === placeholderKey);
|
|
182
|
+
batch.rootPath.parentField = attachedPath.parentField;
|
|
183
|
+
batch.rootPath.parent = attachedPath.parent;
|
|
184
|
+
batch.rootPath.parentIndex = attachedPath.parentIndex;
|
|
185
|
+
// To hydrate a TreeNode, it must be associated with a HydratedFlexTreeNode.
|
|
186
|
+
// Find or create one as necessary.
|
|
187
|
+
for (const { path, node } of batch.paths) {
|
|
188
|
+
const anchor = forest.anchors.track(path);
|
|
189
|
+
const anchorNode = forest.anchors.locate(anchor) ?? fail("missing anchor");
|
|
190
|
+
let flexNode = anchorNode.slots.get(flexTreeSlot);
|
|
191
|
+
if (flexNode === undefined) {
|
|
192
|
+
// the flex node must be created
|
|
193
|
+
const cursor = forest.allocateCursor("getFlexNode");
|
|
194
|
+
forest.moveCursorToPath(anchorNode, cursor);
|
|
195
|
+
flexNode = getOrCreateHydratedFlexTreeNode(context, cursor);
|
|
196
|
+
cursor.free();
|
|
197
|
+
assertFlexTreeEntityNotFreed(flexNode);
|
|
198
|
+
}
|
|
199
|
+
getKernel(node).hydrate(flexNode);
|
|
200
|
+
forest.anchors.forget(anchor);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
176
204
|
//# sourceMappingURL=prepareForInsertion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepareForInsertion.js","sourceRoot":"","sources":["../../src/simple-tree/prepareForInsertion.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tSchemaAndPolicy,\n\tIForestSubscription,\n\tUpPath,\n\tNodeIndex,\n\tFieldKey,\n\tDetachedField,\n\tTreeFieldStoredSchema,\n\tTreeTypeSet,\n} from \"../core/index.js\";\nimport {\n\ttype FlexTreeContext,\n\tgetSchemaAndPolicy,\n\ttype FlexTreeHydratedContextMinimal,\n\tFieldKinds,\n\ttype FlexibleFieldContent,\n\ttype FlexibleNodeContent,\n\tthrowOutOfSchema,\n} from \"../feature-libraries/index.js\";\nimport type { ImplicitFieldSchema } from \"./fieldSchema.js\";\nimport {\n\ttype InsertableContent,\n\tunhydratedFlexTreeFromInsertable,\n} from \"./unhydratedFlexTreeFromInsertable.js\";\nimport { UsageError } from \"@fluidframework/telemetry-utils/internal\";\nimport { brand } from \"../util/index.js\";\nimport {\n\tgetKernel,\n\ttype ImplicitAllowedTypes,\n\ttype TreeNode,\n\ttype UnhydratedFlexTreeNode,\n} from \"./core/index.js\";\nimport { debugAssert, oob } from \"@fluidframework/core-utils/internal\";\nimport { isFieldInSchema } from \"../feature-libraries/index.js\";\n\n/**\n * For now, schema validation for inserted content is always enabled.\n * @remarks\n * If this ends up being too much of a performance overhead, AND nothing depends on it (like staged allowed types likely will),\n * this could be changed.\n */\nconst validateSchema = true;\n\n/**\n * Prepare content from a user for insertion into a tree.\n * @remarks\n * This validates and converts the input, and if necessary invokes {@link prepareContentForHydration}.\n *\n * The next edit made to `destinationContext`'s forest must be the creation of a detached field containing this content,\n * (Triggering {@link ForestEvents.afterRootFieldCreated}) otherwise hydration will break.\n */\nexport function prepareForInsertion<TIn extends InsertableContent | undefined>(\n\tdata: TIn,\n\tschema: ImplicitFieldSchema,\n\tdestinationContext: FlexTreeContext,\n\tdestinationSchema: TreeFieldStoredSchema,\n): TIn extends undefined ? undefined : FlexibleNodeContent {\n\treturn prepareForInsertionContextless(\n\t\tdata,\n\t\tschema,\n\t\tgetSchemaAndPolicy(destinationContext),\n\t\tdestinationContext.isHydrated() ? destinationContext : undefined,\n\t\tdestinationSchema,\n\t);\n}\n\n/**\n * {@link prepareForInsertion} but batched for array content.\n * @remarks\n * This is for inserting items into an array, not a inserting a {@link TreeArrayNode} (that would use {@link prepareForInsertion}).\n *\n * The next edits made to `destinationContext`'s forest must be the creation of a detached field.\n * One edit for each item in `data`, in order.\n *\n * @privateRemarks\n * This has to be done as a single operation for all items in data\n * (as opposed to mapping {@link prepareForInsertion} over the array)\n * due to how the eventing in prepareContentForHydration works.\n */\nexport function prepareArrayContentForInsertion(\n\tdata: readonly InsertableContent[],\n\tschema: ImplicitAllowedTypes,\n\tdestinationContext: FlexTreeContext,\n\tdestinationSchema: TreeTypeSet,\n): FlexibleFieldContent {\n\tconst mapTrees: UnhydratedFlexTreeNode[] = data.map((item) =>\n\t\tunhydratedFlexTreeFromInsertable(item, schema),\n\t);\n\n\tvalidateAndPrepare(\n\t\tgetSchemaAndPolicy(destinationContext),\n\t\tdestinationContext.isHydrated() ? destinationContext : undefined,\n\t\t{\n\t\t\tkind: FieldKinds.sequence.identifier,\n\t\t\ttypes: destinationSchema,\n\t\t\tpersistedMetadata: undefined,\n\t\t},\n\t\tmapTrees,\n\t);\n\n\treturn mapTrees;\n}\n\n/**\n * Split out from {@link prepareForInsertion} as to allow use without a context.\n *\n * @param hydratedData - If specified, the `mapTrees` will be prepared for hydration into this context.\n * `undefined` when `mapTrees` are being inserted into an {@link Unhydrated} tree.\n *\n * @remarks\n * Adding this entry point is a workaround for initialize not currently having a context.\n */\nexport function prepareForInsertionContextless<TIn extends InsertableContent | undefined>(\n\tdata: TIn,\n\tschema: ImplicitFieldSchema,\n\tschemaAndPolicy: SchemaAndPolicy,\n\thydratedData: FlexTreeHydratedContextMinimal | undefined,\n\tdestinationSchema: TreeFieldStoredSchema,\n): TIn extends undefined ? undefined : FlexibleNodeContent {\n\tconst mapTree = unhydratedFlexTreeFromInsertable(data, schema);\n\n\tconst contentArray = mapTree === undefined ? [] : [mapTree];\n\tvalidateAndPrepare(schemaAndPolicy, hydratedData, destinationSchema, contentArray);\n\n\treturn mapTree;\n}\n\n/**\n * If hydrating, do a final validation against the schema and prepare the content for hydration.\n *\n * @param hydratedData - If specified, the `mapTrees` will be prepared for hydration into this context.\n * `undefined` when `mapTrees` are being inserted into an {@link Unhydrated} tree.\n */\nfunction validateAndPrepare(\n\tschemaAndPolicy: SchemaAndPolicy,\n\thydratedData: FlexTreeHydratedContextMinimal | undefined,\n\tfieldSchema: TreeFieldStoredSchema,\n\tmapTrees: readonly UnhydratedFlexTreeNode[],\n): void {\n\tif (hydratedData !== undefined) {\n\t\t// Run `prepareContentForHydration` before walking the tree in `isFieldInSchema`.\n\t\t// This ensures that when `isFieldInSchema` requests identifiers (or any other contextual defaults),\n\t\t// they were already creating used the more specific context we have access to from `hydratedData`.\n\t\tprepareContentForHydration(mapTrees, hydratedData.checkout.forest, hydratedData);\n\t\t// TODO: AB#45723\n\t\t// Now that staged schema rely on this validation, its a bit odd we don't do it for insertion into unhydrated contexts.\n\t\t// We can't simply enable it for them however due to contextual default fields which would not have been created yet (see comment above).\n\t\t// Specifically at least clone can result in unhydrated trees which can end up violating their stored schema (but not view schema) just using the type safe APIs.\n\t\tif (validateSchema === true) {\n\t\t\tisFieldInSchema(mapTrees, fieldSchema, schemaAndPolicy, throwOutOfSchema);\n\t\t}\n\t}\n}\n\n/**\n * An {@link UpPath} that is just index zero in a {@link DetachedField} which can be modified at a later time.\n */\ninterface Root extends UpPath {\n\treadonly parent: undefined;\n\tparentField: DetachedField & FieldKey;\n\treadonly parentIndex: NodeIndex & 0;\n}\n\n/**\n * The path from the included node to the root of the content tree it was inserted as part of.\n */\ninterface RelativeNodePath {\n\treadonly path: UpPath;\n\treadonly node: TreeNode;\n}\n\n/**\n * {@link RelativeNodePath}s for every {@link TreeNode} in the content tree inserted as an atomic operation.\n */\ninterface LocatedNodesBatch {\n\t/**\n\t * UpPath shared by all {@link RelativeNodePath}s in this batch corresponding to the root of the inserted content.\n\t */\n\treadonly rootPath: Root;\n\treadonly paths: RelativeNodePath[];\n}\n\n/**\n * A dummy key value used in {@link LocatedNodesBatch.rootPath} which will be replaced with the actual detached field once it is known.\n */\nconst placeholderKey: DetachedField & FieldKey = brand(\"placeholder\" as const);\n\n/**\n * Records any {@link TreeNode}s in the given `content` tree and does the necessary bookkeeping to ensure they are synchronized with subsequent reads of the tree.\n * Additionally populates any {@link UnhydratedFlexTreeField.pendingDefault}s using the provided `context`.\n *\n * @remarks If the content tree contains has any associated {@link TreeNode}s, this function must be called just prior to inserting the content into the tree.\n * Specifically, no other content may be inserted into the tree between the invocation of this function and the insertion of `content`.\n * The insertion of `content` must occur or else this function will cause memory leaks.\n *\n * Exported for testing purposes: otherwise should not be used outside this module.\n * @param content - the content subsequence to be inserted, of which might deeply contain {@link TreeNode}s which need to be hydrated.\n * @param forest - the forest the content is being inserted into.\n */\nexport function prepareContentForHydration(\n\tcontent: readonly UnhydratedFlexTreeNode[],\n\tforest: IForestSubscription,\n\tcontext: FlexTreeHydratedContextMinimal,\n): void {\n\tconst batches: LocatedNodesBatch[] = [];\n\tfor (const item of content) {\n\t\tconst batch: LocatedNodesBatch = {\n\t\t\trootPath: {\n\t\t\t\tparent: undefined,\n\t\t\t\tparentField: placeholderKey,\n\t\t\t\tparentIndex: 0,\n\t\t\t},\n\t\t\tpaths: [],\n\t\t};\n\t\tbatches.push(batch);\n\t\twalkMapTree(\n\t\t\titem,\n\t\t\tbatch.rootPath,\n\t\t\t(p, node) => {\n\t\t\t\tbatch.paths.push({ path: p, node });\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t}\n\n\tscheduleHydration(batches, forest);\n}\n\nfunction walkMapTree(\n\troot: UnhydratedFlexTreeNode,\n\tpath: UpPath,\n\tonVisitTreeNode: (path: UpPath, treeNode: TreeNode) => void,\n\tcontext: FlexTreeHydratedContextMinimal,\n): void {\n\tif (root.parentField.parent.parent !== undefined) {\n\t\tthrow new UsageError(\n\t\t\t\"Attempted to insert a node which is already under a parent. If this is desired, remove the node from its parent before inserting it elsewhere.\",\n\t\t);\n\t}\n\n\ttype Next = [path: UpPath, tree: UnhydratedFlexTreeNode];\n\tconst nexts: Next[] = [];\n\tfor (let next: Next | undefined = [path, root]; next !== undefined; next = nexts.pop()) {\n\t\tconst [p, node] = next;\n\t\tif (node !== undefined) {\n\t\t\tconst treeNode = node.treeNode;\n\t\t\tif (treeNode !== undefined) {\n\t\t\t\tonVisitTreeNode(p, treeNode);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, field] of node.allFieldsLazy) {\n\t\t\tfield.fillPendingDefaults(context);\n\t\t\tfor (const [i, child] of field.children.entries()) {\n\t\t\t\tnexts.push([\n\t\t\t\t\t{\n\t\t\t\t\t\tparent: p,\n\t\t\t\t\t\tparentField: key,\n\t\t\t\t\t\tparentIndex: i,\n\t\t\t\t\t},\n\t\t\t\t\tchild,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Register events which will hydrate batches of nodes when they are inserted.\n * The next edits to forest must be their insertions, in order, or data corruption can occur.\n * @param locatedNodes - the nodes to register with the forest.\n * Each index in this array expects its content to be added and produce its own `afterRootFieldCreated` event.\n * If array subsequence insertion is optimized to produce a single event, this will not work correctly as is, and will need to be modified to take in a single {@link LocatedNodesBatch}.\n */\nfunction scheduleHydration(\n\tlocatedNodes: readonly LocatedNodesBatch[],\n\tforest: IForestSubscription,\n): void {\n\t// Only subscribe to the event if there is at least one TreeNode tree to hydrate - this is not the case when inserting an empty array [].\n\tif (locatedNodes.length > 0) {\n\t\t// Creating a new array emits one event per element in the array, so listen to the event once for each element\n\t\tlet i = 0;\n\t\tconst off = forest.events.on(\"afterRootFieldCreated\", (fieldKey) => {\n\t\t\t// Indexing is safe here because of the length check above. This assumes the array has not been modified which should be the case.\n\t\t\tconst batch = locatedNodes[i] ?? oob();\n\t\t\tdebugAssert(() => batch.rootPath.parentField === placeholderKey);\n\t\t\tbatch.rootPath.parentField = brand(fieldKey);\n\t\t\tfor (const { path, node } of batch.paths) {\n\t\t\t\tgetKernel(node).hydrate(forest.anchors, path);\n\t\t\t}\n\t\t\tif (++i === locatedNodes.length) {\n\t\t\t\toff();\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prepareForInsertion.js","sourceRoot":"","sources":["../../src/simple-tree/prepareForInsertion.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tSchemaAndPolicy,\n\tIForestSubscription,\n\tUpPath,\n\tFieldKey,\n\tDetachedField,\n\tTreeFieldStoredSchema,\n\tTreeTypeSet,\n} from \"../core/index.js\";\nimport {\n\ttype FlexTreeContext,\n\tgetSchemaAndPolicy,\n\ttype FlexTreeHydratedContextMinimal,\n\tFieldKinds,\n\ttype FlexibleFieldContent,\n\ttype FlexibleNodeContent,\n\tthrowOutOfSchema,\n\tflexTreeSlot,\n\tContextSlot,\n\tgetOrCreateHydratedFlexTreeNode,\n\tassertFlexTreeEntityNotFreed,\n} from \"../feature-libraries/index.js\";\nimport type { ImplicitFieldSchema } from \"./fieldSchema.js\";\nimport {\n\ttype InsertableContent,\n\tunhydratedFlexTreeFromInsertable,\n} from \"./unhydratedFlexTreeFromInsertable.js\";\nimport { UsageError } from \"@fluidframework/telemetry-utils/internal\";\nimport { brand, type Mutable } from \"../util/index.js\";\nimport {\n\tgetKernel,\n\ttype ImplicitAllowedTypes,\n\ttype TreeNode,\n\ttype UnhydratedFlexTreeNode,\n} from \"./core/index.js\";\nimport { debugAssert, fail, oob } from \"@fluidframework/core-utils/internal\";\nimport { isFieldInSchema } from \"../feature-libraries/index.js\";\n\n/**\n * For now, schema validation for inserted content is always enabled.\n * @remarks\n * If this ends up being too much of a performance overhead, AND nothing depends on it (like staged allowed types likely will),\n * this could be changed.\n */\nconst validateSchema = true;\n\n/**\n * Prepare content from a user for insertion into a tree.\n * @remarks\n * This validates and converts the input, and if necessary invokes {@link prepareContentForHydration}.\n *\n * The next edit made to `destinationContext`'s forest must be the creation of a detached field containing this content,\n * (Triggering {@link ForestEvents.afterRootFieldCreated}) otherwise hydration will break.\n */\nexport function prepareForInsertion<TIn extends InsertableContent | undefined>(\n\tdata: TIn,\n\tschema: ImplicitFieldSchema,\n\tdestinationContext: FlexTreeContext,\n\tdestinationSchema: TreeFieldStoredSchema,\n): TIn extends undefined ? undefined : FlexibleNodeContent {\n\treturn prepareForInsertionContextless(\n\t\tdata,\n\t\tschema,\n\t\tgetSchemaAndPolicy(destinationContext),\n\t\tdestinationContext.isHydrated() ? destinationContext : undefined,\n\t\tdestinationSchema,\n\t);\n}\n\n/**\n * {@link prepareForInsertion} but batched for array content.\n * @remarks\n * This is for inserting items into an array, not a inserting a {@link TreeArrayNode} (that would use {@link prepareForInsertion}).\n *\n * The next edits made to `destinationContext`'s forest must be the creation of a detached field.\n * One edit for each item in `data`, in order.\n *\n * @privateRemarks\n * This has to be done as a single operation for all items in data\n * (as opposed to mapping {@link prepareForInsertion} over the array)\n * due to how the eventing in prepareContentForHydration works.\n */\nexport function prepareArrayContentForInsertion(\n\tdata: readonly InsertableContent[],\n\tschema: ImplicitAllowedTypes,\n\tdestinationContext: FlexTreeContext,\n\tdestinationSchema: TreeTypeSet,\n): FlexibleFieldContent {\n\tconst mapTrees: UnhydratedFlexTreeNode[] = data.map((item) =>\n\t\tunhydratedFlexTreeFromInsertable(item, schema),\n\t);\n\n\tvalidateAndPrepare(\n\t\tgetSchemaAndPolicy(destinationContext),\n\t\tdestinationContext.isHydrated() ? destinationContext : undefined,\n\t\t{\n\t\t\tkind: FieldKinds.sequence.identifier,\n\t\t\ttypes: destinationSchema,\n\t\t\tpersistedMetadata: undefined,\n\t\t},\n\t\tmapTrees,\n\t);\n\n\treturn mapTrees;\n}\n\n/**\n * Split out from {@link prepareForInsertion} as to allow use without a context.\n *\n * @param hydratedData - If specified, the `mapTrees` will be prepared for hydration into this context.\n * `undefined` when `mapTrees` are being inserted into an {@link Unhydrated} tree.\n *\n * @remarks\n * Adding this entry point is a workaround for initialize not currently having a context.\n */\nexport function prepareForInsertionContextless<TIn extends InsertableContent | undefined>(\n\tdata: TIn,\n\tschema: ImplicitFieldSchema,\n\tschemaAndPolicy: SchemaAndPolicy,\n\thydratedData: FlexTreeHydratedContextMinimal | undefined,\n\tdestinationSchema: TreeFieldStoredSchema,\n\tscheduleHydrationOverride?: HydrationScheduler,\n): TIn extends undefined ? undefined : FlexibleNodeContent {\n\tconst mapTree = unhydratedFlexTreeFromInsertable(data, schema);\n\n\tconst contentArray = mapTree === undefined ? [] : [mapTree];\n\tvalidateAndPrepare(\n\t\tschemaAndPolicy,\n\t\thydratedData,\n\t\tdestinationSchema,\n\t\tcontentArray,\n\t\tscheduleHydrationOverride,\n\t);\n\n\treturn mapTree;\n}\n\n/**\n * If hydrating, do a final validation against the schema and prepare the content for hydration.\n *\n * @param hydratedData - If specified, the `mapTrees` will be prepared for hydration into this context.\n * `undefined` when `mapTrees` are being inserted into an {@link Unhydrated} tree.\n */\nfunction validateAndPrepare(\n\tschemaAndPolicy: SchemaAndPolicy,\n\thydratedData: FlexTreeHydratedContextMinimal | undefined,\n\tfieldSchema: TreeFieldStoredSchema,\n\tmapTrees: readonly UnhydratedFlexTreeNode[],\n\tscheduleHydrationOverride?: HydrationScheduler,\n): void {\n\tif (hydratedData !== undefined) {\n\t\t// Run `prepareContentForHydration` before walking the tree in `isFieldInSchema`.\n\t\t// This ensures that when `isFieldInSchema` requests identifiers (or any other contextual defaults),\n\t\t// they were already creating used the more specific context we have access to from `hydratedData`.\n\t\tprepareContentForHydration(\n\t\t\tmapTrees,\n\t\t\tscheduleHydrationOverride ??\n\t\t\t\t((batch, doHydration) =>\n\t\t\t\t\tscheduleHydration(batch, hydratedData.checkout.forest, doHydration)),\n\t\t\thydratedData,\n\t\t);\n\t\t// TODO: AB#45723\n\t\t// Now that staged schema rely on this validation, its a bit odd we don't do it for insertion into unhydrated contexts.\n\t\t// We can't simply enable it for them however due to contextual default fields which would not have been created yet (see comment above).\n\t\t// Specifically at least clone can result in unhydrated trees which can end up violating their stored schema (but not view schema) just using the type safe APIs.\n\t\tif (validateSchema === true) {\n\t\t\tisFieldInSchema(mapTrees, fieldSchema, schemaAndPolicy, throwOutOfSchema);\n\t\t}\n\t}\n}\n\n/**\n * The path from the included node to the root of the content tree it was inserted as part of.\n */\ninterface RelativeNodePath {\n\treadonly path: UpPath;\n\treadonly node: TreeNode;\n}\n\n/**\n * {@link RelativeNodePath}s for every {@link TreeNode} in the content tree inserted as an atomic operation.\n */\ninterface LocatedNodesBatch {\n\t/**\n\t * UpPath shared by all {@link RelativeNodePath}s in this batch corresponding to the root of the inserted content.\n\t */\n\treadonly rootPath: Mutable<UpPath>;\n\treadonly paths: RelativeNodePath[];\n}\n\n/**\n * A dummy key value used in {@link LocatedNodesBatch.rootPath} which will be replaced with the actual detached field once it is known.\n */\nconst placeholderKey: DetachedField & FieldKey = brand(\"placeholder\" as const);\n\n/**\n * Records any {@link TreeNode}s in the given `content` tree and does the necessary bookkeeping to ensure they are synchronized with subsequent reads of the tree.\n * Additionally populates any {@link UnhydratedFlexTreeField.pendingDefault}s using the provided `context`.\n *\n * @remarks If the content tree contains has any associated {@link TreeNode}s, this function must be called just prior to inserting the content into the tree.\n * Specifically, no other content may be inserted into the tree between the invocation of this function and the insertion of `content`.\n * The insertion of `content` must occur or else this function will cause memory leaks.\n *\n * Exported for testing purposes: otherwise should not be used outside this module.\n * @param content - the content subsequence to be inserted, of which might deeply contain {@link TreeNode}s which need to be hydrated.\n * @param forest - the forest the content is being inserted into.\n */\nexport function prepareContentForHydration(\n\tcontent: readonly UnhydratedFlexTreeNode[],\n\tscheduler: HydrationScheduler,\n\tcontext: FlexTreeHydratedContextMinimal,\n): void {\n\tconst batches: LocatedNodesBatch[] = [];\n\tfor (const item of content) {\n\t\tconst batch: LocatedNodesBatch = {\n\t\t\trootPath: {\n\t\t\t\tparent: undefined,\n\t\t\t\tparentField: placeholderKey,\n\t\t\t\tparentIndex: 0,\n\t\t\t},\n\t\t\tpaths: [],\n\t\t};\n\t\tbatches.push(batch);\n\t\twalkMapTree(\n\t\t\titem,\n\t\t\tbatch.rootPath,\n\t\t\t(p, node) => {\n\t\t\t\tbatch.paths.push({ path: p, node });\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t}\n\n\tconst doHydration = hydrator(batches, context.checkout.forest);\n\tscheduler(batches, doHydration);\n}\n\nfunction walkMapTree(\n\troot: UnhydratedFlexTreeNode,\n\tpath: UpPath,\n\tonVisitTreeNode: (path: UpPath, treeNode: TreeNode) => void,\n\tcontext: FlexTreeHydratedContextMinimal,\n): void {\n\tif (root.parentField.parent.parent !== undefined) {\n\t\tthrow new UsageError(\n\t\t\t\"Attempted to insert a node which is already under a parent. If this is desired, remove the node from its parent before inserting it elsewhere.\",\n\t\t);\n\t}\n\n\ttype Next = [path: UpPath, tree: UnhydratedFlexTreeNode];\n\tconst nexts: Next[] = [];\n\tfor (let next: Next | undefined = [path, root]; next !== undefined; next = nexts.pop()) {\n\t\tconst [p, node] = next;\n\t\tif (node !== undefined) {\n\t\t\tconst treeNode = node.treeNode;\n\t\t\tif (treeNode !== undefined) {\n\t\t\t\tonVisitTreeNode(p, treeNode);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, field] of node.allFieldsLazy) {\n\t\t\tfield.fillPendingDefaults(context);\n\t\t\tfor (const [i, child] of field.children.entries()) {\n\t\t\t\tnexts.push([\n\t\t\t\t\t{\n\t\t\t\t\t\tparent: p,\n\t\t\t\t\t\tparentField: key,\n\t\t\t\t\t\tparentIndex: i,\n\t\t\t\t\t},\n\t\t\t\t\tchild,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * A function which can schedule hydration of batches of nodes to occur at a later time (which must be after they have been inserted into the tree).\n */\ntype HydrationScheduler = (\n\tlocatedNodes: readonly LocatedNodesBatch[],\n\t/**\n\t * Does the actual hydration. Should be called for each index in `locatedNodes` once the corresponding content has been inserted into the tree.\n\t */\n\tdoHydration: Hydrator,\n) => void;\n\n/**\n * Does the actual hydration.\n * The provided `attachPath` is the path the content is currently under (where it was attached in the tree).\n */\ntype Hydrator = (batch: LocatedNodesBatch, attachPath: UpPath) => void;\n\n/**\n * Register events which will hydrate batches of nodes when they are inserted.\n * The next edits to forest must be their insertions, in order, or data corruption can occur.\n * @param locatedNodes - the nodes to register with the forest.\n * Each index in this array expects its content to be added and produce its own `afterRootFieldCreated` event.\n * If array subsequence insertion is optimized to produce a single event, this will not work correctly as is, and will need to be modified to take in a single {@link LocatedNodesBatch}.\n */\nfunction scheduleHydration(\n\tlocatedNodes: readonly LocatedNodesBatch[],\n\tforest: IForestSubscription,\n\tdoHydration: Hydrator,\n): void {\n\t// Only subscribe to the event if there is at least one TreeNode tree to hydrate - this is not the case when inserting an empty array [].\n\tif (locatedNodes.length > 0) {\n\t\t// Creating a new array emits one event per element in the array, so listen to the event once for each element\n\t\tlet index = 0;\n\t\tconst off = forest.events.on(\"afterRootFieldCreated\", (fieldKey) => {\n\t\t\t// Indexing is safe here because of the length check above. This assumes the array has not been modified which should be the case.\n\t\t\tconst batch = locatedNodes[index] ?? oob();\n\t\t\tdoHydration(batch, { parent: undefined, parentField: fieldKey, parentIndex: 0 });\n\t\t\tif (++index === locatedNodes.length) {\n\t\t\t\toff();\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Implementation of {@link Hydrator}.\n */\nfunction hydrator(\n\tlocatedNodes: readonly LocatedNodesBatch[],\n\tforest: IForestSubscription,\n): (batch: LocatedNodesBatch, attachedPath: UpPath) => void {\n\treturn (batch: LocatedNodesBatch, attachedPath: UpPath) => {\n\t\tconst context = forest.anchors.slots.get(ContextSlot) ?? fail(0xb41 /* missing context */);\n\n\t\t// Modify paths in batch to point to correct location:\n\t\tdebugAssert(() => batch.rootPath.parentField === placeholderKey);\n\t\tbatch.rootPath.parentField = attachedPath.parentField;\n\t\tbatch.rootPath.parent = attachedPath.parent;\n\t\tbatch.rootPath.parentIndex = attachedPath.parentIndex;\n\n\t\t// To hydrate a TreeNode, it must be associated with a HydratedFlexTreeNode.\n\t\t// Find or create one as necessary.\n\t\tfor (const { path, node } of batch.paths) {\n\t\t\tconst anchor = forest.anchors.track(path);\n\t\t\tconst anchorNode = forest.anchors.locate(anchor) ?? fail(\"missing anchor\");\n\n\t\t\tlet flexNode = anchorNode.slots.get(flexTreeSlot);\n\t\t\tif (flexNode === undefined) {\n\t\t\t\t// the flex node must be created\n\t\t\t\tconst cursor = forest.allocateCursor(\"getFlexNode\");\n\t\t\t\tforest.moveCursorToPath(anchorNode, cursor);\n\n\t\t\t\tflexNode = getOrCreateHydratedFlexTreeNode(context, cursor);\n\t\t\t\tcursor.free();\n\t\t\t\tassertFlexTreeEntityNotFreed(flexNode);\n\t\t\t}\n\n\t\t\tgetKernel(node).hydrate(flexNode);\n\t\t\tforest.anchors.forget(anchor);\n\t\t}\n\t};\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/tree",
|
|
3
|
-
"version": "2.63.0-
|
|
3
|
+
"version": "2.63.0-359962",
|
|
4
4
|
"description": "Distributed tree",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -101,17 +101,17 @@
|
|
|
101
101
|
"temp-directory": "nyc/.nyc_output"
|
|
102
102
|
},
|
|
103
103
|
"dependencies": {
|
|
104
|
-
"@fluid-internal/client-utils": "2.63.0-
|
|
105
|
-
"@fluidframework/container-runtime": "2.63.0-
|
|
106
|
-
"@fluidframework/core-interfaces": "2.63.0-
|
|
107
|
-
"@fluidframework/core-utils": "2.63.0-
|
|
108
|
-
"@fluidframework/datastore-definitions": "2.63.0-
|
|
109
|
-
"@fluidframework/driver-definitions": "2.63.0-
|
|
110
|
-
"@fluidframework/id-compressor": "2.63.0-
|
|
111
|
-
"@fluidframework/runtime-definitions": "2.63.0-
|
|
112
|
-
"@fluidframework/runtime-utils": "2.63.0-
|
|
113
|
-
"@fluidframework/shared-object-base": "2.63.0-
|
|
114
|
-
"@fluidframework/telemetry-utils": "2.63.0-
|
|
104
|
+
"@fluid-internal/client-utils": "2.63.0-359962",
|
|
105
|
+
"@fluidframework/container-runtime": "2.63.0-359962",
|
|
106
|
+
"@fluidframework/core-interfaces": "2.63.0-359962",
|
|
107
|
+
"@fluidframework/core-utils": "2.63.0-359962",
|
|
108
|
+
"@fluidframework/datastore-definitions": "2.63.0-359962",
|
|
109
|
+
"@fluidframework/driver-definitions": "2.63.0-359962",
|
|
110
|
+
"@fluidframework/id-compressor": "2.63.0-359962",
|
|
111
|
+
"@fluidframework/runtime-definitions": "2.63.0-359962",
|
|
112
|
+
"@fluidframework/runtime-utils": "2.63.0-359962",
|
|
113
|
+
"@fluidframework/shared-object-base": "2.63.0-359962",
|
|
114
|
+
"@fluidframework/telemetry-utils": "2.63.0-359962",
|
|
115
115
|
"@sinclair/typebox": "^0.34.13",
|
|
116
116
|
"@tylerbu/sorted-btree-es6": "^1.8.0",
|
|
117
117
|
"@types/ungap__structured-clone": "^1.2.0",
|
|
@@ -121,19 +121,19 @@
|
|
|
121
121
|
"devDependencies": {
|
|
122
122
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
123
123
|
"@biomejs/biome": "~1.9.3",
|
|
124
|
-
"@fluid-internal/mocha-test-setup": "2.63.0-
|
|
125
|
-
"@fluid-private/stochastic-test-utils": "2.63.0-
|
|
126
|
-
"@fluid-private/test-dds-utils": "2.63.0-
|
|
127
|
-
"@fluid-private/test-drivers": "2.63.0-
|
|
124
|
+
"@fluid-internal/mocha-test-setup": "2.63.0-359962",
|
|
125
|
+
"@fluid-private/stochastic-test-utils": "2.63.0-359962",
|
|
126
|
+
"@fluid-private/test-dds-utils": "2.63.0-359962",
|
|
127
|
+
"@fluid-private/test-drivers": "2.63.0-359962",
|
|
128
128
|
"@fluid-tools/benchmark": "^0.51.0",
|
|
129
129
|
"@fluid-tools/build-cli": "^0.58.3",
|
|
130
130
|
"@fluidframework/build-common": "^2.0.3",
|
|
131
131
|
"@fluidframework/build-tools": "^0.58.3",
|
|
132
|
-
"@fluidframework/container-definitions": "2.63.0-
|
|
133
|
-
"@fluidframework/container-loader": "2.63.0-
|
|
132
|
+
"@fluidframework/container-definitions": "2.63.0-359962",
|
|
133
|
+
"@fluidframework/container-loader": "2.63.0-359962",
|
|
134
134
|
"@fluidframework/eslint-config-fluid": "^6.0.0",
|
|
135
|
-
"@fluidframework/test-runtime-utils": "2.63.0-
|
|
136
|
-
"@fluidframework/test-utils": "2.63.0-
|
|
135
|
+
"@fluidframework/test-runtime-utils": "2.63.0-359962",
|
|
136
|
+
"@fluidframework/test-utils": "2.63.0-359962",
|
|
137
137
|
"@fluidframework/tree-previous": "npm:@fluidframework/tree@2.62.0",
|
|
138
138
|
"@microsoft/api-extractor": "7.52.11",
|
|
139
139
|
"@types/diff": "^3.5.1",
|
|
@@ -12,10 +12,8 @@ import {
|
|
|
12
12
|
type ITreeCursorSynchronous,
|
|
13
13
|
LeafNodeStoredSchema,
|
|
14
14
|
ObjectNodeStoredSchema,
|
|
15
|
-
type StoredSchemaCollection,
|
|
16
15
|
type TreeFieldStoredSchema,
|
|
17
16
|
type TreeNodeSchemaIdentifier,
|
|
18
|
-
type TreeStoredSchema,
|
|
19
17
|
type TreeStoredSchemaSubscription,
|
|
20
18
|
type TreeValue,
|
|
21
19
|
type Value,
|
|
@@ -24,6 +22,7 @@ import {
|
|
|
24
22
|
ValueSchema,
|
|
25
23
|
type TreeChunk,
|
|
26
24
|
tryGetChunk,
|
|
25
|
+
type SchemaAndPolicy,
|
|
27
26
|
} from "../../core/index.js";
|
|
28
27
|
import { getOrCreate } from "../../util/index.js";
|
|
29
28
|
import type { FullSchemaPolicy } from "../modular-schema/index.js";
|
|
@@ -32,6 +31,7 @@ import { isStableNodeIdentifier } from "../node-identifier/index.js";
|
|
|
32
31
|
import { BasicChunk } from "./basicChunk.js";
|
|
33
32
|
import { SequenceChunk } from "./sequenceChunk.js";
|
|
34
33
|
import { type FieldShape, TreeShape, UniformChunk } from "./uniformChunk.js";
|
|
34
|
+
import type { IncrementalEncodingPolicy } from "./codec/index.js";
|
|
35
35
|
|
|
36
36
|
export interface Disposable {
|
|
37
37
|
/**
|
|
@@ -39,13 +39,13 @@ export interface Disposable {
|
|
|
39
39
|
*/
|
|
40
40
|
dispose(): void;
|
|
41
41
|
}
|
|
42
|
-
|
|
43
42
|
/**
|
|
44
43
|
* Creates a ChunkPolicy which responds to schema changes.
|
|
45
44
|
*/
|
|
46
45
|
export function makeTreeChunker(
|
|
47
46
|
schema: TreeStoredSchemaSubscription,
|
|
48
47
|
policy: FullSchemaPolicy,
|
|
48
|
+
shouldEncodeIncrementally: IncrementalEncodingPolicy,
|
|
49
49
|
): IChunker {
|
|
50
50
|
return new Chunker(
|
|
51
51
|
schema,
|
|
@@ -53,7 +53,16 @@ export function makeTreeChunker(
|
|
|
53
53
|
defaultChunkPolicy.sequenceChunkInlineThreshold,
|
|
54
54
|
defaultChunkPolicy.sequenceChunkInlineThreshold,
|
|
55
55
|
defaultChunkPolicy.uniformChunkNodeCount,
|
|
56
|
-
|
|
56
|
+
(type: TreeNodeSchemaIdentifier, shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>) =>
|
|
57
|
+
tryShapeFromNodeSchema(
|
|
58
|
+
{
|
|
59
|
+
schema,
|
|
60
|
+
policy,
|
|
61
|
+
shouldEncodeIncrementally,
|
|
62
|
+
shapes,
|
|
63
|
+
},
|
|
64
|
+
type,
|
|
65
|
+
),
|
|
57
66
|
);
|
|
58
67
|
}
|
|
59
68
|
|
|
@@ -73,7 +82,7 @@ export interface IChunker extends ChunkPolicy, Disposable {
|
|
|
73
82
|
*
|
|
74
83
|
* @remarks
|
|
75
84
|
* For example, a schema transitively containing a sequence field, optional field, or allowing multiple child types will be Polymorphic.
|
|
76
|
-
* See `
|
|
85
|
+
* See `tryShapeFromNodeSchema` for how to tell if a type is Polymorphic.
|
|
77
86
|
*
|
|
78
87
|
* TODO: cache some of the possible shapes here.
|
|
79
88
|
*/
|
|
@@ -109,9 +118,7 @@ export class Chunker implements IChunker {
|
|
|
109
118
|
public readonly sequenceChunkInlineThreshold: number,
|
|
110
119
|
public readonly uniformChunkNodeCount: number,
|
|
111
120
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
112
|
-
private readonly
|
|
113
|
-
schema: TreeStoredSchema,
|
|
114
|
-
policy: FullSchemaPolicy,
|
|
121
|
+
private readonly tryShapeFromNodeSchema: (
|
|
115
122
|
type: TreeNodeSchemaIdentifier,
|
|
116
123
|
shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>,
|
|
117
124
|
) => ShapeInfo,
|
|
@@ -126,7 +133,7 @@ export class Chunker implements IChunker {
|
|
|
126
133
|
this.sequenceChunkSplitThreshold,
|
|
127
134
|
this.sequenceChunkInlineThreshold,
|
|
128
135
|
this.uniformChunkNodeCount,
|
|
129
|
-
this.
|
|
136
|
+
this.tryShapeFromNodeSchema,
|
|
130
137
|
);
|
|
131
138
|
}
|
|
132
139
|
|
|
@@ -138,7 +145,7 @@ export class Chunker implements IChunker {
|
|
|
138
145
|
this.unregisterSchemaCallback = this.schema.events.on("afterSchemaChange", () =>
|
|
139
146
|
this.schemaChanged(),
|
|
140
147
|
);
|
|
141
|
-
return this.
|
|
148
|
+
return this.tryShapeFromNodeSchema(schema, this.typeShapes);
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
public dispose(): void {
|
|
@@ -226,75 +233,126 @@ export function makePolicy(policy?: Partial<ChunkPolicy>): ChunkPolicy {
|
|
|
226
233
|
return withDefaults;
|
|
227
234
|
}
|
|
228
235
|
|
|
229
|
-
export
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
export interface ShapeFromSchemaParameters extends SchemaAndPolicy {
|
|
237
|
+
/**
|
|
238
|
+
* Policy function to determine if a field should be encoded incrementally.
|
|
239
|
+
* Incrementally encoding requires the subtree to not start in the middle of a larger uniform chunk.
|
|
240
|
+
* Thus returning true from this callback indicates that shapes should not be produced which could
|
|
241
|
+
*contain the incremental portion as a part of a larger shape.
|
|
242
|
+
*/
|
|
243
|
+
readonly shouldEncodeIncrementally: IncrementalEncodingPolicy;
|
|
244
|
+
/**
|
|
245
|
+
* A cache for shapes which may be read and/or updated.
|
|
246
|
+
* As the shape is a function of the other members of `ShapeFromSchemaParameters`,
|
|
247
|
+
* it must be replaced or cleared if any of the properties other than this cache are modified.
|
|
248
|
+
*/
|
|
249
|
+
readonly shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* A TreeFieldStoredSchema with some additional context about where it is in the tree.
|
|
254
|
+
*/
|
|
255
|
+
export interface FieldSchemaWithContext {
|
|
256
|
+
/**
|
|
257
|
+
* The identifier of the specific field schema to analyze for shape uniformity.
|
|
258
|
+
*/
|
|
259
|
+
readonly fieldSchema: TreeFieldStoredSchema;
|
|
260
|
+
/**
|
|
261
|
+
* The identifier of the parent node schema containing this field.
|
|
262
|
+
* If undefined, this is a root field.
|
|
263
|
+
*/
|
|
264
|
+
readonly parentNodeSchema?: TreeNodeSchemaIdentifier;
|
|
265
|
+
/**
|
|
266
|
+
* The field key/name used to identify this field within the parent node.
|
|
267
|
+
*/
|
|
268
|
+
readonly key: FieldKey;
|
|
238
269
|
}
|
|
239
270
|
|
|
240
271
|
/**
|
|
241
|
-
*
|
|
272
|
+
* Analyzes a tree node schema to determine if it has a single, uniform shape that can be optimized for chunking.
|
|
273
|
+
* If the schema defines a tree structure with a deterministic, fixed shape (no optional fields, no sequences,
|
|
274
|
+
* single child types), returns a TreeShape that can be used for efficient uniform chunking. Otherwise,
|
|
275
|
+
* returns Polymorphic to indicate the shape varies and should use basic chunking.
|
|
276
|
+
*
|
|
277
|
+
* @param context - {@link ShapeFromSchemaParameters}.
|
|
278
|
+
* @param nodeSchema - The identifier of the specific node schema to analyze for shape uniformity.
|
|
279
|
+
* @returns TreeShape if the schema has a uniform shape, or Polymorphic if shape varies.
|
|
242
280
|
*
|
|
243
|
-
*
|
|
281
|
+
* @remarks
|
|
282
|
+
* The determination here is conservative. `shouldEncodeIncrementally` is used to split up shapes so incrementally
|
|
283
|
+
* encoded schema are not part of larger shapes. It also does not tolerate optional or sequence fields, nor does it
|
|
284
|
+
* optimize for patterns of specific values.
|
|
244
285
|
*/
|
|
245
|
-
export function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
type: TreeNodeSchemaIdentifier,
|
|
249
|
-
shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>,
|
|
286
|
+
export function tryShapeFromNodeSchema(
|
|
287
|
+
context: ShapeFromSchemaParameters,
|
|
288
|
+
nodeSchema: TreeNodeSchemaIdentifier,
|
|
250
289
|
): ShapeInfo {
|
|
251
|
-
|
|
252
|
-
|
|
290
|
+
const { schema, shapes } = context;
|
|
291
|
+
return getOrCreate(shapes, nodeSchema, () => {
|
|
292
|
+
const treeSchema = schema.nodeSchema.get(nodeSchema) ?? fail(0xaf9 /* missing schema */);
|
|
253
293
|
if (treeSchema instanceof LeafNodeStoredSchema) {
|
|
254
294
|
// Allow all string values (but only string values) to be compressed by the id compressor.
|
|
255
295
|
// This allows compressing all compressible identifiers without requiring additional context to know which values could be identifiers.
|
|
256
296
|
// Attempting to compress other string shouldn't have significant overhead,
|
|
257
297
|
// and if any of them do end up compressing, that's a benefit not a bug.
|
|
258
298
|
return treeSchema.leafValue === ValueSchema.String
|
|
259
|
-
? new TreeShape(
|
|
260
|
-
: new TreeShape(
|
|
299
|
+
? new TreeShape(nodeSchema, true, [], true)
|
|
300
|
+
: new TreeShape(nodeSchema, true, [], false);
|
|
261
301
|
}
|
|
262
302
|
if (treeSchema instanceof ObjectNodeStoredSchema) {
|
|
263
303
|
const fieldsArray: FieldShape[] = [];
|
|
264
|
-
for (const [key,
|
|
265
|
-
const fieldShape = tryShapeFromFieldSchema(
|
|
304
|
+
for (const [key, fieldSchema] of treeSchema.objectNodeFields) {
|
|
305
|
+
const fieldShape = tryShapeFromFieldSchema(context, {
|
|
306
|
+
fieldSchema,
|
|
307
|
+
parentNodeSchema: nodeSchema,
|
|
308
|
+
key,
|
|
309
|
+
});
|
|
266
310
|
if (fieldShape === undefined) {
|
|
267
311
|
return polymorphic;
|
|
268
312
|
}
|
|
269
313
|
fieldsArray.push(fieldShape);
|
|
270
314
|
}
|
|
271
|
-
return new TreeShape(
|
|
315
|
+
return new TreeShape(nodeSchema, false, fieldsArray);
|
|
272
316
|
}
|
|
273
317
|
return polymorphic;
|
|
274
318
|
});
|
|
275
319
|
}
|
|
276
320
|
|
|
277
321
|
/**
|
|
278
|
-
*
|
|
322
|
+
* Same as {@link tryShapeFromNodeSchema} but for fields with {@link FieldSchemaWithContext} instead of a nodeSchema.
|
|
279
323
|
*
|
|
280
|
-
*
|
|
324
|
+
* @param context - {@link ShapeFromFieldSchemaParameters}.
|
|
325
|
+
* @param fieldSchemaWithContext - {@link FieldSchemaWithContext}.
|
|
326
|
+
* @returns FieldShape if the field has a uniform shape, or undefined if the field is polymorphic.
|
|
281
327
|
*/
|
|
282
328
|
export function tryShapeFromFieldSchema(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
type: TreeFieldStoredSchema,
|
|
286
|
-
key: FieldKey,
|
|
287
|
-
shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>,
|
|
329
|
+
context: ShapeFromSchemaParameters,
|
|
330
|
+
fieldSchemaWithContext: FieldSchemaWithContext,
|
|
288
331
|
): FieldShape | undefined {
|
|
289
|
-
const
|
|
332
|
+
const { schema, policy, shouldEncodeIncrementally, shapes } = context;
|
|
333
|
+
const { fieldSchema, parentNodeSchema, key } = fieldSchemaWithContext;
|
|
334
|
+
// If this field should be encoded incrementally, use polymorphic shape so that they
|
|
335
|
+
// are chunked separately and can be re-used across encodings if they do not change.
|
|
336
|
+
if (shouldEncodeIncrementally(parentNodeSchema, key)) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const kind = policy.fieldKinds.get(fieldSchema.kind) ?? fail(0xafa /* missing FieldKind */);
|
|
290
340
|
if (kind.multiplicity !== Multiplicity.Single) {
|
|
291
341
|
return undefined;
|
|
292
342
|
}
|
|
293
|
-
if (
|
|
343
|
+
if (fieldSchema.types?.size !== 1) {
|
|
294
344
|
return undefined;
|
|
295
345
|
}
|
|
296
|
-
const childType = [...
|
|
297
|
-
const childShape =
|
|
346
|
+
const childType = [...fieldSchema.types][0] ?? oob();
|
|
347
|
+
const childShape = tryShapeFromNodeSchema(
|
|
348
|
+
{
|
|
349
|
+
schema,
|
|
350
|
+
policy,
|
|
351
|
+
shouldEncodeIncrementally,
|
|
352
|
+
shapes,
|
|
353
|
+
},
|
|
354
|
+
childType,
|
|
355
|
+
);
|
|
298
356
|
if (childShape instanceof Polymorphic) {
|
|
299
357
|
return undefined;
|
|
300
358
|
}
|
|
@@ -490,7 +548,16 @@ export function chunkRange(
|
|
|
490
548
|
return output;
|
|
491
549
|
}
|
|
492
550
|
/**
|
|
493
|
-
*
|
|
551
|
+
* Extracts values from the current cursor position according to the provided tree shape.
|
|
552
|
+
*
|
|
553
|
+
* Walks through the tree structure defined by the shape, extracting values from leaf nodes
|
|
554
|
+
* and recursively processing child fields. If an ID compressor is provided, compressible
|
|
555
|
+
* string values (stable node identifiers) will be recompressed for optimal storage.
|
|
556
|
+
*
|
|
557
|
+
* @param cursor - Tree cursor positioned at the node to extract values from
|
|
558
|
+
* @param shape - The tree shape defining the structure to extract
|
|
559
|
+
* @param values - Array to append the extracted values to
|
|
560
|
+
* @param idCompressor - Optional compressor used to encode string values that are compressible by the idCompressor for in-memory representation.
|
|
494
561
|
* If the idCompressor is not provided, the values will be the original uncompressed values.
|
|
495
562
|
*/
|
|
496
563
|
export function insertValues(
|
|
@@ -15,11 +15,9 @@ import {
|
|
|
15
15
|
} from "../../../codec/index.js";
|
|
16
16
|
import {
|
|
17
17
|
CursorLocationType,
|
|
18
|
-
type FieldKey,
|
|
19
18
|
type ITreeCursorSynchronous,
|
|
20
19
|
type SchemaAndPolicy,
|
|
21
20
|
type TreeChunk,
|
|
22
|
-
type TreeNodeSchemaIdentifier,
|
|
23
21
|
} from "../../../core/index.js";
|
|
24
22
|
import {
|
|
25
23
|
brandedNumberType,
|
|
@@ -37,6 +35,7 @@ import type { FieldBatch } from "./fieldBatch.js";
|
|
|
37
35
|
import { EncodedFieldBatch, validVersions, type FieldBatchFormatVersion } from "./format.js";
|
|
38
36
|
import { schemaCompressedEncode } from "./schemaBasedEncode.js";
|
|
39
37
|
import { uncompressedEncode } from "./uncompressedEncode.js";
|
|
38
|
+
import type { IncrementalEncodingPolicy } from "./incrementalEncodingPolicy.js";
|
|
40
39
|
|
|
41
40
|
/**
|
|
42
41
|
* Reference ID for a chunk that is incrementally encoded.
|
|
@@ -55,14 +54,10 @@ const ChunkReferenceId = brandedNumberType<ChunkReferenceId>({ multipleOf: 1, mi
|
|
|
55
54
|
*/
|
|
56
55
|
export interface IncrementalEncoder {
|
|
57
56
|
/**
|
|
58
|
-
* Returns whether a field should be incrementally encoded.
|
|
59
|
-
* @
|
|
60
|
-
* @param fieldKey - The key of the field to check.
|
|
57
|
+
* Returns whether a node / field should be incrementally encoded.
|
|
58
|
+
* @remarks See {@link IncrementalEncodingPolicy}.
|
|
61
59
|
*/
|
|
62
|
-
|
|
63
|
-
nodeIdentifier: TreeNodeSchemaIdentifier,
|
|
64
|
-
fieldKey: FieldKey,
|
|
65
|
-
): boolean;
|
|
60
|
+
shouldEncodeIncrementally: IncrementalEncodingPolicy;
|
|
66
61
|
/**
|
|
67
62
|
* Called to encode an incremental field at the cursor.
|
|
68
63
|
* The chunks for this field are encoded separately from the main buffer.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FieldKey, TreeNodeSchemaIdentifier } from "../../../core/index.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Policy to determine whether a node / field should be incrementally encoded.
|
|
10
|
+
* @param nodeIdentifier - The identifier of the node containing the field.
|
|
11
|
+
* If undefined, the field is a root field.
|
|
12
|
+
* @param fieldKey - The key of the field to check.
|
|
13
|
+
* @returns whether the node / field should be incrementally encoded.
|
|
14
|
+
* @remarks
|
|
15
|
+
* Incremental encoding has a significant size overhead,
|
|
16
|
+
* but allows reuse of previously encoded unchanged subtrees.
|
|
17
|
+
* Thus it should only be enabled for large subtrees which are modified infrequently.
|
|
18
|
+
* TODO: AB#9068: Measure the actual overhead.
|
|
19
|
+
*/
|
|
20
|
+
export type IncrementalEncodingPolicy = (
|
|
21
|
+
nodeIdentifier: TreeNodeSchemaIdentifier | undefined,
|
|
22
|
+
fieldKey: FieldKey,
|
|
23
|
+
) => boolean;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default policy for incremental encoding is to not encode incrementally.
|
|
27
|
+
*/
|
|
28
|
+
export const defaultIncrementalEncodingPolicy: IncrementalEncodingPolicy = (
|
|
29
|
+
nodeIdentifier: TreeNodeSchemaIdentifier | undefined,
|
|
30
|
+
fieldKey: FieldKey,
|
|
31
|
+
): boolean => {
|
|
32
|
+
return false;
|
|
33
|
+
};
|