@fluidframework/tree 2.101.0 → 2.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/api-report/tree.alpha.api.md +19 -0
- package/dist/codec/codec.d.ts +22 -53
- package/dist/codec/codec.d.ts.map +1 -1
- package/dist/codec/codec.js +7 -44
- package/dist/codec/codec.js.map +1 -1
- package/dist/codec/index.d.ts +1 -1
- package/dist/codec/index.d.ts.map +1 -1
- package/dist/codec/index.js +2 -2
- package/dist/codec/index.js.map +1 -1
- package/dist/codec/versioned/codec.d.ts +56 -28
- package/dist/codec/versioned/codec.d.ts.map +1 -1
- package/dist/codec/versioned/codec.js +29 -12
- package/dist/codec/versioned/codec.js.map +1 -1
- package/dist/codec/versioned/index.d.ts +1 -1
- package/dist/codec/versioned/index.d.ts.map +1 -1
- package/dist/codec/versioned/index.js +2 -2
- package/dist/codec/versioned/index.js.map +1 -1
- package/dist/core/tree/deltaUtil.d.ts +2 -2
- package/dist/core/tree/deltaUtil.js +2 -2
- package/dist/core/tree/deltaUtil.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV1.js +3 -2
- package/dist/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV2.js +4 -2
- package/dist/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts +3 -3
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts +38 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js +62 -4
- package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +18 -4
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +34 -2
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js +106 -3
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.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 +2 -2
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +7 -7
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.d.ts +25 -5
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.js +9 -3
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.d.ts +105 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.d.ts.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.js +44 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.js.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/format/index.d.ts +2 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.js +5 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.d.ts +38 -4
- package/dist/feature-libraries/chunked-forest/codec/format/versions.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.js +7 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +7 -7
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js +1 -2
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.d.ts +5 -4
- package/dist/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.js +2 -2
- package/dist/feature-libraries/forest-summary/codec.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/dist/feature-libraries/schema-index/codec.d.ts +2 -2
- package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/dist/feature-libraries/schema-index/codec.js +1 -1
- package/dist/feature-libraries/schema-index/codec.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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/isAuditableFromOutcome.d.ts +20 -0
- package/dist/shared-tree/isAuditableFromOutcome.d.ts.map +1 -0
- package/dist/shared-tree/isAuditableFromOutcome.js +36 -0
- package/dist/shared-tree/isAuditableFromOutcome.js.map +1 -0
- package/dist/shared-tree/treeCheckout.d.ts +2 -0
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +37 -8
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.d.ts +3 -3
- package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.js +2 -2
- package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/dist/shared-tree-core/messageCodecs.d.ts +3 -3
- package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecs.js +2 -2
- package/dist/shared-tree-core/messageCodecs.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +25 -11
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/text/codePointUtils.d.ts +48 -0
- package/dist/text/codePointUtils.d.ts.map +1 -0
- package/dist/text/codePointUtils.js +80 -0
- package/dist/text/codePointUtils.js.map +1 -0
- package/dist/text/index.d.ts +1 -0
- package/dist/text/index.d.ts.map +1 -1
- package/dist/text/index.js +4 -1
- package/dist/text/index.js.map +1 -1
- package/dist/text/textDomain.d.ts +93 -1
- package/dist/text/textDomain.d.ts.map +1 -1
- package/dist/text/textDomain.js +65 -8
- package/dist/text/textDomain.js.map +1 -1
- package/dist/text/textDomainFormatted.d.ts +24 -6
- package/dist/text/textDomainFormatted.d.ts.map +1 -1
- package/dist/text/textDomainFormatted.js +29 -1
- package/dist/text/textDomainFormatted.js.map +1 -1
- package/dist/treeFactory.d.ts.map +1 -1
- package/dist/treeFactory.js +9 -7
- package/dist/treeFactory.js.map +1 -1
- package/dist/util/breakable.d.ts +7 -1
- package/dist/util/breakable.d.ts.map +1 -1
- package/dist/util/breakable.js +18 -4
- package/dist/util/breakable.js.map +1 -1
- package/lib/codec/codec.d.ts +22 -53
- package/lib/codec/codec.d.ts.map +1 -1
- package/lib/codec/codec.js +7 -44
- package/lib/codec/codec.js.map +1 -1
- package/lib/codec/index.d.ts +1 -1
- package/lib/codec/index.d.ts.map +1 -1
- package/lib/codec/index.js +1 -1
- package/lib/codec/index.js.map +1 -1
- package/lib/codec/versioned/codec.d.ts +56 -28
- package/lib/codec/versioned/codec.d.ts.map +1 -1
- package/lib/codec/versioned/codec.js +27 -10
- package/lib/codec/versioned/codec.js.map +1 -1
- package/lib/codec/versioned/index.d.ts +1 -1
- package/lib/codec/versioned/index.d.ts.map +1 -1
- package/lib/codec/versioned/index.js +1 -1
- package/lib/codec/versioned/index.js.map +1 -1
- package/lib/core/tree/deltaUtil.d.ts +2 -2
- package/lib/core/tree/deltaUtil.js +2 -2
- package/lib/core/tree/deltaUtil.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV1.js +3 -2
- package/lib/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV2.js +5 -3
- package/lib/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts +3 -3
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.js +2 -2
- package/lib/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts +38 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js +61 -4
- package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +20 -6
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +34 -2
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js +102 -2
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.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 +3 -3
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +7 -7
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.d.ts +25 -5
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.js +8 -2
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.d.ts +105 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.d.ts.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.js +41 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.js.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/format/index.d.ts +2 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.js +2 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/versions.d.ts +38 -4
- package/lib/feature-libraries/chunked-forest/codec/format/versions.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/versions.js +6 -0
- package/lib/feature-libraries/chunked-forest/codec/format/versions.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +7 -7
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js +2 -3
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.d.ts +5 -4
- package/lib/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.js +3 -3
- package/lib/feature-libraries/forest-summary/codec.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/lib/feature-libraries/schema-index/codec.d.ts +2 -2
- package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/lib/feature-libraries/schema-index/codec.js +2 -2
- package/lib/feature-libraries/schema-index/codec.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +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/isAuditableFromOutcome.d.ts +20 -0
- package/lib/shared-tree/isAuditableFromOutcome.d.ts.map +1 -0
- package/lib/shared-tree/isAuditableFromOutcome.js +32 -0
- package/lib/shared-tree/isAuditableFromOutcome.js.map +1 -0
- package/lib/shared-tree/treeCheckout.d.ts +2 -0
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +37 -8
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.d.ts +3 -3
- package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.js +3 -3
- package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts +3 -3
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js +3 -3
- package/lib/shared-tree-core/messageCodecs.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +25 -11
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/text/codePointUtils.d.ts +48 -0
- package/lib/text/codePointUtils.d.ts.map +1 -0
- package/lib/text/codePointUtils.js +75 -0
- package/lib/text/codePointUtils.js.map +1 -0
- package/lib/text/index.d.ts +1 -0
- package/lib/text/index.d.ts.map +1 -1
- package/lib/text/index.js +1 -0
- package/lib/text/index.js.map +1 -1
- package/lib/text/textDomain.d.ts +93 -1
- package/lib/text/textDomain.d.ts.map +1 -1
- package/lib/text/textDomain.js +56 -0
- package/lib/text/textDomain.js.map +1 -1
- package/lib/text/textDomainFormatted.d.ts +24 -6
- package/lib/text/textDomainFormatted.d.ts.map +1 -1
- package/lib/text/textDomainFormatted.js +30 -2
- package/lib/text/textDomainFormatted.js.map +1 -1
- package/lib/treeFactory.d.ts.map +1 -1
- package/lib/treeFactory.js +2 -0
- package/lib/treeFactory.js.map +1 -1
- package/lib/util/breakable.d.ts +7 -1
- package/lib/util/breakable.d.ts.map +1 -1
- package/lib/util/breakable.js +18 -4
- package/lib/util/breakable.js.map +1 -1
- package/package.json +24 -24
- package/src/codec/codec.ts +82 -73
- package/src/codec/index.ts +2 -1
- package/src/codec/versioned/codec.ts +173 -73
- package/src/codec/versioned/index.ts +2 -1
- package/src/core/tree/deltaUtil.ts +2 -2
- package/src/core/tree/detachedFieldIndexCodecV1.ts +3 -2
- package/src/core/tree/detachedFieldIndexCodecV2.ts +5 -3
- package/src/core/tree/detachedFieldIndexCodecs.ts +2 -2
- package/src/feature-libraries/chunked-forest/chunkTree.ts +85 -1
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +27 -7
- package/src/feature-libraries/chunked-forest/codec/chunkDecoding.ts +143 -7
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +30 -28
- package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +7 -7
- package/src/feature-libraries/chunked-forest/codec/format/formatV2.ts +11 -7
- package/src/feature-libraries/chunked-forest/codec/format/formatVText.ts +83 -0
- package/src/feature-libraries/chunked-forest/codec/format/index.ts +6 -1
- package/src/feature-libraries/chunked-forest/codec/format/versions.ts +25 -4
- package/src/feature-libraries/chunked-forest/codec/nodeEncoder.ts +14 -18
- package/src/feature-libraries/forest-summary/codec.ts +9 -4
- package/src/feature-libraries/modular-schema/modularChangeTypes.ts +1 -1
- package/src/feature-libraries/schema-index/codec.ts +2 -5
- package/src/index.ts +6 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/isAuditableFromOutcome.ts +34 -0
- package/src/shared-tree/treeCheckout.ts +52 -9
- package/src/shared-tree-core/editManagerCodecs.ts +4 -6
- package/src/shared-tree-core/messageCodecs.ts +4 -4
- package/src/simple-tree/core/treeNodeKernel.ts +27 -13
- package/src/text/codePointUtils.ts +81 -0
- package/src/text/index.ts +1 -0
- package/src/text/textDomain.ts +155 -2
- package/src/text/textDomainFormatted.ts +73 -2
- package/src/treeFactory.ts +5 -0
- package/src/util/breakable.ts +27 -6
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
type SchemaAndPolicy,
|
|
26
26
|
type SchemaPolicy,
|
|
27
27
|
} from "../../core/index.js";
|
|
28
|
-
import { getOrCreate } from "../../util/index.js";
|
|
28
|
+
import { assertNonNegativeSafeInteger, getOrCreate } from "../../util/index.js";
|
|
29
29
|
import { isStableNodeIdentifier } from "../node-identifier/index.js";
|
|
30
30
|
|
|
31
31
|
import { BasicChunk } from "./basicChunk.js";
|
|
@@ -53,6 +53,7 @@ export function makeTreeChunker(
|
|
|
53
53
|
defaultChunkPolicy.sequenceChunkInlineThreshold,
|
|
54
54
|
defaultChunkPolicy.sequenceChunkInlineThreshold,
|
|
55
55
|
defaultChunkPolicy.uniformChunkNodeCount,
|
|
56
|
+
defaultChunkPolicy.uniformChunkNodeCountDynamicTargetMax,
|
|
56
57
|
(type: TreeNodeSchemaIdentifier, shapes: Map<TreeNodeSchemaIdentifier, ShapeInfo>) =>
|
|
57
58
|
tryShapeFromNodeSchema(
|
|
58
59
|
{
|
|
@@ -117,6 +118,7 @@ export class Chunker implements IChunker {
|
|
|
117
118
|
public readonly sequenceChunkSplitThreshold: number,
|
|
118
119
|
public readonly sequenceChunkInlineThreshold: number,
|
|
119
120
|
public readonly uniformChunkNodeCount: number,
|
|
121
|
+
public readonly uniformChunkNodeCountDynamicTargetMax: number,
|
|
120
122
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
121
123
|
private readonly tryShapeFromNodeSchema: (
|
|
122
124
|
type: TreeNodeSchemaIdentifier,
|
|
@@ -133,6 +135,7 @@ export class Chunker implements IChunker {
|
|
|
133
135
|
this.sequenceChunkSplitThreshold,
|
|
134
136
|
this.sequenceChunkInlineThreshold,
|
|
135
137
|
this.uniformChunkNodeCount,
|
|
138
|
+
this.uniformChunkNodeCountDynamicTargetMax,
|
|
136
139
|
this.tryShapeFromNodeSchema,
|
|
137
140
|
);
|
|
138
141
|
}
|
|
@@ -379,6 +382,7 @@ export const defaultChunkPolicy: ChunkPolicy = {
|
|
|
379
382
|
sequenceChunkInlineThreshold: Number.POSITIVE_INFINITY,
|
|
380
383
|
// Current UniformChunk handling doesn't scale well to large chunks, so set a modest size limit:
|
|
381
384
|
uniformChunkNodeCount: 400,
|
|
385
|
+
uniformChunkNodeCountDynamicTargetMax: 25,
|
|
382
386
|
// Without knowing what the schema is, all shapes are possible.
|
|
383
387
|
// Use `makeTreeChunker` to do better.
|
|
384
388
|
shapeFromSchema: () => polymorphic,
|
|
@@ -388,6 +392,7 @@ export const basicOnlyChunkPolicy: ChunkPolicy = {
|
|
|
388
392
|
sequenceChunkSplitThreshold: Number.POSITIVE_INFINITY,
|
|
389
393
|
sequenceChunkInlineThreshold: Number.POSITIVE_INFINITY,
|
|
390
394
|
uniformChunkNodeCount: 0,
|
|
395
|
+
uniformChunkNodeCountDynamicTargetMax: 0,
|
|
391
396
|
shapeFromSchema: () => polymorphic,
|
|
392
397
|
};
|
|
393
398
|
|
|
@@ -413,6 +418,24 @@ export interface ChunkPolicy {
|
|
|
413
418
|
*/
|
|
414
419
|
readonly uniformChunkNodeCount: number;
|
|
415
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Target maximum top level length for a UniformChunk while a field is being edited.
|
|
423
|
+
*
|
|
424
|
+
* @remarks
|
|
425
|
+
* When {@link splitFieldAtIndex} has to split a chunk to land an attach/detach on a chunk
|
|
426
|
+
* boundary, chunks whose {@link TreeChunk.topLevelLength} exceeds this value are bisected
|
|
427
|
+
* recursively until each piece is at or below it, and only the piece holding the target index
|
|
428
|
+
* is split exactly. This bounds N splits inside an M-sized chunk at the cost of producing a
|
|
429
|
+
* few extra intermediate chunks.
|
|
430
|
+
*
|
|
431
|
+
* Future merge/extend logic for adjacent small chunks could use the same value as the
|
|
432
|
+
* upper bound it tries to stay under, so dynamic chunk sizes settle around this target.
|
|
433
|
+
*
|
|
434
|
+
* Independent of {@link ChunkPolicy.uniformChunkNodeCount}, which only bounds the size of
|
|
435
|
+
* chunks produced by the initial chunking pass.
|
|
436
|
+
*/
|
|
437
|
+
readonly uniformChunkNodeCountDynamicTargetMax: number;
|
|
438
|
+
|
|
416
439
|
/**
|
|
417
440
|
* Returns information about the shapes trees of type `schema` can take.
|
|
418
441
|
*/
|
|
@@ -556,6 +579,67 @@ export function chunkRange(
|
|
|
556
579
|
|
|
557
580
|
return output;
|
|
558
581
|
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Walks the `chunks` array of a field and splits a chunk if needed so that `nodeIndex` sits on
|
|
585
|
+
* a chunk boundary. After the call, inserting a chunk at the returned index would place its
|
|
586
|
+
* first top-level node at index `nodeIndex` when treating `chunks` as a field.
|
|
587
|
+
*
|
|
588
|
+
* @remarks
|
|
589
|
+
* When splitting chunks, large chunks are split evenly so that repeated calls to this method (or similar operations)
|
|
590
|
+
* avoid poor worst-case behavior. See {@link ChunkPolicy.uniformChunkNodeCountDynamicTargetMax} for details.
|
|
591
|
+
*
|
|
592
|
+
* @param chunks - The array of {@link TreeChunk}s for the field to split. Mutated in place.
|
|
593
|
+
* @param nodeIndex - The index to split at, measured in top-level nodes within the field.
|
|
594
|
+
* Must be in `[0, totalNodes]`, where `totalNodes` is the sum of {@link TreeChunk.topLevelLength}
|
|
595
|
+
* across all chunks.
|
|
596
|
+
* @param policy - The {@link ChunkCompressor} to use when splitting chunks and re-chunking each side
|
|
597
|
+
* of the split via {@link chunkRange}.
|
|
598
|
+
*
|
|
599
|
+
* @returns The index in `chunks` (after modifications made by this function) where if a chunk were inserted at that index its first top level node would have index `nodeIndex` when treating `chunks` as a field.
|
|
600
|
+
*/
|
|
601
|
+
export function splitFieldAtIndex(
|
|
602
|
+
chunks: TreeChunk[],
|
|
603
|
+
nodeIndex: number,
|
|
604
|
+
policy: ChunkCompressor,
|
|
605
|
+
): number {
|
|
606
|
+
assertNonNegativeSafeInteger(nodeIndex);
|
|
607
|
+
const bisectThreshold = policy.policy.uniformChunkNodeCountDynamicTargetMax;
|
|
608
|
+
let remaining = nodeIndex;
|
|
609
|
+
let chunkIndex = 0;
|
|
610
|
+
while (chunkIndex < chunks.length) {
|
|
611
|
+
if (remaining === 0) {
|
|
612
|
+
return chunkIndex;
|
|
613
|
+
}
|
|
614
|
+
const chunk = chunks[chunkIndex] ?? oob();
|
|
615
|
+
const total = chunk.topLevelLength;
|
|
616
|
+
if (remaining >= total) {
|
|
617
|
+
// nodeIndex is not in this chunk, so move forward one chunk and continue.
|
|
618
|
+
remaining -= total;
|
|
619
|
+
chunkIndex++;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// nodeIndex falls within this chunk, so split the chunk.
|
|
624
|
+
// This does not move the chunkIndex forward: the next iteration might need to split again at the same index.
|
|
625
|
+
//
|
|
626
|
+
// For chunks above the bisect threshold, cut at the midpoint and let the loop descend
|
|
627
|
+
// into whichever half holds nodeIndex. The other half is left untouched.
|
|
628
|
+
const splitPoint = total > bisectThreshold ? Math.floor(total / 2) : remaining;
|
|
629
|
+
const cursor = chunk.cursor();
|
|
630
|
+
cursor.firstNode();
|
|
631
|
+
const before = chunkRange(cursor, policy, splitPoint, false);
|
|
632
|
+
const after = chunkRange(cursor, policy, total - splitPoint, true);
|
|
633
|
+
// TODO: this could fail for really long chunks being split (due to argument count limits).
|
|
634
|
+
chunks.splice(chunkIndex, 1, ...before, ...after);
|
|
635
|
+
// The spliced-out slot held a ref to the original chunk. The two new chunks come with
|
|
636
|
+
// their own refs from chunkRange, so the slot's ref to the original needs to be released.
|
|
637
|
+
chunk.referenceRemoved();
|
|
638
|
+
}
|
|
639
|
+
assert(remaining === 0, 0xcf9 /* nodeIndex exceeds total node count in field */);
|
|
640
|
+
return chunks.length;
|
|
641
|
+
}
|
|
642
|
+
|
|
559
643
|
/**
|
|
560
644
|
* Extracts values from the current cursor position according to the provided tree shape.
|
|
561
645
|
*
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
type DeltaDetachedNodeId,
|
|
37
37
|
} from "../../core/index.js";
|
|
38
38
|
import {
|
|
39
|
-
assertValidRange,
|
|
40
39
|
brand,
|
|
41
40
|
getLast,
|
|
42
41
|
getOrAddEmptyToMap,
|
|
@@ -45,7 +44,14 @@ import {
|
|
|
45
44
|
} from "../../util/index.js";
|
|
46
45
|
|
|
47
46
|
import { BasicChunk, BasicChunkCursor, type SiblingsOrKey } from "./basicChunk.js";
|
|
48
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
type ChunkCompressor,
|
|
49
|
+
type IChunker,
|
|
50
|
+
basicChunkTree,
|
|
51
|
+
chunkField,
|
|
52
|
+
chunkTree,
|
|
53
|
+
splitFieldAtIndex,
|
|
54
|
+
} from "./chunkTree.js";
|
|
49
55
|
|
|
50
56
|
function makeRoot(): BasicChunk {
|
|
51
57
|
return new BasicChunk(aboveRootPlaceholder, new Map());
|
|
@@ -173,8 +179,12 @@ export class ChunkedForest implements IEditableForest {
|
|
|
173
179
|
|
|
174
180
|
const parent = this.getParent();
|
|
175
181
|
const destinationField = getOrAddEmptyToMap(parent.mutableChunk.fields, parent.key);
|
|
182
|
+
const destinationChunkIndex = splitFieldAtIndex(destinationField, destination, {
|
|
183
|
+
policy: this.forest.chunker,
|
|
184
|
+
idCompressor: this.forest.idCompressor,
|
|
185
|
+
});
|
|
176
186
|
// TODO: this will fail for very large moves due to argument limits.
|
|
177
|
-
destinationField.splice(
|
|
187
|
+
destinationField.splice(destinationChunkIndex, 0, ...sourceField);
|
|
178
188
|
},
|
|
179
189
|
/**
|
|
180
190
|
* Detaches the range from the current field and transfers it to the given destination if any.
|
|
@@ -193,9 +203,19 @@ export class ChunkedForest implements IEditableForest {
|
|
|
193
203
|
this.forest.#events.emit("beforeChange");
|
|
194
204
|
const parent = this.getParent();
|
|
195
205
|
const sourceField = parent.mutableChunk.fields.get(parent.key) ?? [];
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
206
|
+
assert(source.start <= source.end, 0xcf8 /* detach range start must not exceed end */);
|
|
207
|
+
|
|
208
|
+
const policy: ChunkCompressor = {
|
|
209
|
+
policy: this.forest.chunker,
|
|
210
|
+
idCompressor: this.forest.idCompressor,
|
|
211
|
+
};
|
|
212
|
+
// Split start first: splitting end later only expands chunks at positions >= startChunkIndex,
|
|
213
|
+
// which leaves startChunkIndex valid. The reverse order would shift endChunkIndex when
|
|
214
|
+
// source.start and source.end land in different chunks.
|
|
215
|
+
// Performance: It's practical to have a variant of splitFieldAtIndex which can split at multiple locations in a single pass if the performance of this becomes worth optimizing.
|
|
216
|
+
const startChunkIndex = splitFieldAtIndex(sourceField, source.start, policy);
|
|
217
|
+
const endChunkIndex = splitFieldAtIndex(sourceField, source.end, policy);
|
|
218
|
+
const newField = sourceField.splice(startChunkIndex, endChunkIndex - startChunkIndex);
|
|
199
219
|
|
|
200
220
|
if (destination === undefined) {
|
|
201
221
|
for (const child of newField) {
|
|
@@ -225,12 +245,12 @@ export class ChunkedForest implements IEditableForest {
|
|
|
225
245
|
let indexOfChunk = 0;
|
|
226
246
|
let chunk = chunks[indexOfChunk] ?? oob();
|
|
227
247
|
while (indexWithinChunk >= chunk.topLevelLength) {
|
|
228
|
-
chunk = chunks[indexOfChunk] ?? oob();
|
|
229
248
|
indexWithinChunk -= chunk.topLevelLength;
|
|
230
249
|
indexOfChunk++;
|
|
231
250
|
if (indexOfChunk === chunks.length) {
|
|
232
251
|
fail(0xaf7 /* missing edited node */);
|
|
233
252
|
}
|
|
253
|
+
chunk = chunks[indexOfChunk] ?? oob();
|
|
234
254
|
}
|
|
235
255
|
let found = chunks[indexOfChunk] ?? oob();
|
|
236
256
|
if (!(found instanceof BasicChunk)) {
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
OpSpaceCompressedId,
|
|
10
10
|
SessionId,
|
|
11
11
|
} from "@fluidframework/id-compressor";
|
|
12
|
+
import { isFinalId } from "@fluidframework/id-compressor/internal";
|
|
12
13
|
import { v5 as uuidV5 } from "uuid";
|
|
13
14
|
|
|
14
15
|
import { DiscriminatedUnionDispatcher } from "../../../codec/index.js";
|
|
@@ -42,7 +43,7 @@ import {
|
|
|
42
43
|
import type { FieldBatchEncodingContext, IncrementalDecoder } from "./codecs.js";
|
|
43
44
|
import {
|
|
44
45
|
type EncodedAnyShape,
|
|
45
|
-
type
|
|
46
|
+
type EncodedChunkShape,
|
|
46
47
|
type EncodedChunkShapeV2,
|
|
47
48
|
type EncodedFieldBatchV1OrV2,
|
|
48
49
|
type EncodedFieldBatchV2,
|
|
@@ -50,7 +51,9 @@ import {
|
|
|
50
51
|
type EncodedInlineArrayShape,
|
|
51
52
|
type EncodedNestedArrayShape,
|
|
52
53
|
type EncodedNodeShape,
|
|
54
|
+
type EncodedSpecializedNodeShape,
|
|
53
55
|
type EncodedValueShape,
|
|
56
|
+
type ShapeIndex,
|
|
54
57
|
SpecialField,
|
|
55
58
|
supportsIncrementalEncoding,
|
|
56
59
|
} from "./format/index.js";
|
|
@@ -97,9 +100,137 @@ export function decode(
|
|
|
97
100
|
);
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Resolves `shapeIndex` to a fully-resolved {@link EncodedNodeShape}, normalizing away any
|
|
105
|
+
* specialized node shapes (`f`) along the way by applying their overlays via
|
|
106
|
+
* {@link applySpecialization} until a concrete node shape is reached.
|
|
107
|
+
*
|
|
108
|
+
* @param input - The index of the shape to resolve, which must be a concrete or specialized node shape.
|
|
109
|
+
* @param context - The decoding context containing the shape definitions.
|
|
110
|
+
* @param pendingResolution - (Internal) A set of shape indices visited so far in the current resolution chain, used to detect cycles in the specialization chain. Most callers should not provide this argument.
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* Exported for testing.
|
|
114
|
+
*/
|
|
115
|
+
export function normalizeToNodeShape(
|
|
116
|
+
input: EncodedNodeShape | EncodedSpecializedNodeShape,
|
|
117
|
+
context: DecoderContext<EncodedChunkShape>,
|
|
118
|
+
pendingResolution: Set<ShapeIndex> = new Set(),
|
|
119
|
+
): EncodedNodeShape {
|
|
120
|
+
if (!("base" in input)) {
|
|
121
|
+
return input;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const baseIndex = input.base;
|
|
125
|
+
assert(!pendingResolution.has(baseIndex), 0xcfb /* cyclic specialized node shape chain */);
|
|
126
|
+
pendingResolution.add(baseIndex);
|
|
127
|
+
const encoded = context.shapes[baseIndex];
|
|
128
|
+
assert(encoded !== undefined, 0xcfc /* shape index out of bounds */);
|
|
129
|
+
|
|
130
|
+
const baseShape = encoded.c ?? ("f" in encoded ? encoded.f : undefined);
|
|
131
|
+
assert(
|
|
132
|
+
baseShape !== undefined,
|
|
133
|
+
0xcfd /* shape at index must be a concrete (c) or specialized (f) node shape */,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return applySpecialization(
|
|
137
|
+
normalizeToNodeShape(baseShape, context, pendingResolution),
|
|
138
|
+
input,
|
|
139
|
+
context,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Produces a specialized {@link EncodedNodeShape} by overlaying `overrides` onto `base`.
|
|
145
|
+
*
|
|
146
|
+
* See {@link EncodedSpecializedNodeShape} for the override/inherit/clear semantics.
|
|
147
|
+
*
|
|
148
|
+
* @remarks
|
|
149
|
+
* Exported for testing.
|
|
150
|
+
*/
|
|
151
|
+
export function applySpecialization(
|
|
152
|
+
base: EncodedNodeShape,
|
|
153
|
+
overrides: EncodedSpecializedNodeShape,
|
|
154
|
+
context: DecoderContext<EncodedChunkShape>,
|
|
155
|
+
): EncodedNodeShape {
|
|
156
|
+
const fields = [...(base.fields ?? [])];
|
|
157
|
+
const indexFromKey = new Map<FieldKey, number>();
|
|
158
|
+
for (const [i, [keyEncoded]] of fields.entries()) {
|
|
159
|
+
const key = context.identifier<FieldKey>(keyEncoded);
|
|
160
|
+
assert(!indexFromKey.has(key), 0xcfe /* duplicate field key in base node shape */);
|
|
161
|
+
indexFromKey.set(key, i);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Replace fields in base with overrides, append new keys in overrides in the order they are specified.
|
|
165
|
+
const seenOverrideKeys = new Set<FieldKey>();
|
|
166
|
+
for (const [keyEncoded, shapeIndex] of overrides.fields ?? []) {
|
|
167
|
+
const key = context.identifier<FieldKey>(keyEncoded);
|
|
168
|
+
assert(
|
|
169
|
+
!seenOverrideKeys.has(key),
|
|
170
|
+
0xcff /* duplicate field key in specialized node shape */,
|
|
171
|
+
);
|
|
172
|
+
seenOverrideKeys.add(key);
|
|
173
|
+
const existingIndex = indexFromKey.get(key);
|
|
174
|
+
if (existingIndex === undefined) {
|
|
175
|
+
fields.push([keyEncoded, shapeIndex]);
|
|
176
|
+
} else {
|
|
177
|
+
const index = fields[existingIndex];
|
|
178
|
+
assert(index !== undefined, 0xd00 /* expected existing field index */);
|
|
179
|
+
fields[existingIndex] = [index[0], shapeIndex];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
type: base.type,
|
|
185
|
+
value: resolveOverride(overrides.value, base.value),
|
|
186
|
+
fields: fields.length > 0 ? fields : undefined,
|
|
187
|
+
extraFields: resolveOverride(overrides.extraFields, base.extraFields),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Resolves an override against a base value.
|
|
193
|
+
*
|
|
194
|
+
* @param override - `undefined` means the override is absent (inherit from base); `null` is the
|
|
195
|
+
* explicit-clear sentinel needed because JSON.stringify drops `undefined`-valued properties, making
|
|
196
|
+
* property-presence indistinguishable from absent on the wire.
|
|
197
|
+
* @param baseValue - The value to inherit when the override is absent.
|
|
198
|
+
*/
|
|
199
|
+
function resolveOverride<T>(
|
|
200
|
+
// eslint-disable-next-line @rushstack/no-new-null
|
|
201
|
+
override: T | null | undefined,
|
|
202
|
+
baseValue: T | undefined,
|
|
203
|
+
): T | undefined {
|
|
204
|
+
if (override === undefined) {
|
|
205
|
+
return baseValue;
|
|
206
|
+
}
|
|
207
|
+
if (override === null) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return override;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Decoder for {@link EncodedSpecializedNodeShape}s.
|
|
215
|
+
* Applies the specialization's field overrides to the resolved base node shape, then delegates
|
|
216
|
+
* to a {@link NodeDecoder} built from the resulting shape.
|
|
217
|
+
*/
|
|
218
|
+
export class SpecializedNodeDecoder implements ChunkDecoder {
|
|
219
|
+
private readonly inner: NodeDecoder;
|
|
220
|
+
public constructor(
|
|
221
|
+
shape: EncodedSpecializedNodeShape,
|
|
222
|
+
context: DecoderContext<EncodedChunkShape>,
|
|
223
|
+
) {
|
|
224
|
+
this.inner = new NodeDecoder(normalizeToNodeShape(shape, context), context);
|
|
225
|
+
}
|
|
226
|
+
public decode(decoders: readonly ChunkDecoder[], stream: StreamCursor): TreeChunk {
|
|
227
|
+
return this.inner.decode(decoders, stream);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
100
231
|
const decoderLibrary = new DiscriminatedUnionDispatcher<
|
|
101
|
-
|
|
102
|
-
[context: DecoderContext<
|
|
232
|
+
EncodedChunkShape,
|
|
233
|
+
[context: DecoderContext<EncodedChunkShape>],
|
|
103
234
|
ChunkDecoder
|
|
104
235
|
>({
|
|
105
236
|
a(shape: EncodedNestedArrayShape, context): ChunkDecoder {
|
|
@@ -120,6 +251,9 @@ const decoderLibrary = new DiscriminatedUnionDispatcher<
|
|
|
120
251
|
): ChunkDecoder {
|
|
121
252
|
return new IncrementalChunkDecoder(context);
|
|
122
253
|
},
|
|
254
|
+
f(shape: EncodedSpecializedNodeShape, context): ChunkDecoder {
|
|
255
|
+
return new SpecializedNodeDecoder(shape, context);
|
|
256
|
+
},
|
|
123
257
|
});
|
|
124
258
|
|
|
125
259
|
/**
|
|
@@ -153,8 +287,10 @@ export function readValue(
|
|
|
153
287
|
const idCompressor = idDecodingContext.idCompressor;
|
|
154
288
|
// OpSpaceCompressedIds are negative, and require a session-id to compute their value.
|
|
155
289
|
// Due to a bug, we have some special casing for them (see below).
|
|
156
|
-
|
|
157
|
-
|
|
290
|
+
if (
|
|
291
|
+
idDecodingContext.isSummary === true &&
|
|
292
|
+
!isFinalId(streamValue as OpSpaceCompressedId)
|
|
293
|
+
) {
|
|
158
294
|
if (
|
|
159
295
|
idDecodingContext.healUnresolvableIdentifiersOnDecode === true &&
|
|
160
296
|
idDecodingContext.sharedObjectId !== undefined
|
|
@@ -349,7 +485,7 @@ type BasicFieldDecoder = (
|
|
|
349
485
|
* Get a decoder for fields of a provided (via `shape` and `context`).
|
|
350
486
|
*/
|
|
351
487
|
function fieldDecoder(
|
|
352
|
-
context: DecoderContext<
|
|
488
|
+
context: DecoderContext<EncodedChunkShape>,
|
|
353
489
|
key: FieldKey,
|
|
354
490
|
shape: number,
|
|
355
491
|
): BasicFieldDecoder {
|
|
@@ -368,7 +504,7 @@ export class NodeDecoder implements ChunkDecoder {
|
|
|
368
504
|
private readonly fieldDecoders: readonly BasicFieldDecoder[];
|
|
369
505
|
public constructor(
|
|
370
506
|
private readonly shape: EncodedNodeShape,
|
|
371
|
-
private readonly context: DecoderContext<
|
|
507
|
+
private readonly context: DecoderContext<EncodedChunkShape>,
|
|
372
508
|
) {
|
|
373
509
|
this.type = shape.type === undefined ? undefined : context.identifier(shape.type);
|
|
374
510
|
|
|
@@ -9,8 +9,9 @@ import { lowestMinVersionForCollab } from "@fluidframework/runtime-utils/interna
|
|
|
9
9
|
import type { TSchema } from "@sinclair/typebox";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
|
|
12
|
+
VersionDispatchingCodecBuilder,
|
|
13
13
|
type CodecAndSchema,
|
|
14
|
+
type VersionDispatchingCodec,
|
|
14
15
|
FluidClientVersion,
|
|
15
16
|
} from "../../../codec/index.js";
|
|
16
17
|
import {
|
|
@@ -138,7 +139,11 @@ export interface FieldBatchEncodingContext {
|
|
|
138
139
|
* @remarks
|
|
139
140
|
* Fields in this batch currently don't have field schema for the root, which limits optimizations.
|
|
140
141
|
*/
|
|
141
|
-
export type FieldBatchCodec =
|
|
142
|
+
export type FieldBatchCodec = VersionDispatchingCodec<
|
|
143
|
+
FieldBatch,
|
|
144
|
+
FieldBatchEncodingContext,
|
|
145
|
+
FieldBatchFormatVersion
|
|
146
|
+
>;
|
|
142
147
|
|
|
143
148
|
/**
|
|
144
149
|
* Creates the encode/decode functions for a specific FieldBatch format version.
|
|
@@ -231,30 +236,27 @@ function makeFieldBatchCodecForVersion(
|
|
|
231
236
|
}
|
|
232
237
|
|
|
233
238
|
/**
|
|
234
|
-
* {@link
|
|
239
|
+
* {@link VersionDispatchingCodecBuilder} for field batch codecs.
|
|
235
240
|
*/
|
|
236
|
-
export const fieldBatchCodecBuilder =
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
);
|
|
241
|
+
export const fieldBatchCodecBuilder = VersionDispatchingCodecBuilder.build("FieldBatch", [
|
|
242
|
+
{
|
|
243
|
+
minVersionForCollab: lowestMinVersionForCollab,
|
|
244
|
+
formatVersion: FieldBatchFormatVersion.v1,
|
|
245
|
+
codec: makeFieldBatchCodecForVersion(
|
|
246
|
+
FieldBatchFormatVersion.v1,
|
|
247
|
+
uncompressedEncodeV1,
|
|
248
|
+
schemaCompressedEncodeV1,
|
|
249
|
+
EncodedFieldBatchV1,
|
|
250
|
+
),
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
minVersionForCollab: FluidClientVersion.v2_73,
|
|
254
|
+
formatVersion: FieldBatchFormatVersion.v2,
|
|
255
|
+
codec: makeFieldBatchCodecForVersion(
|
|
256
|
+
FieldBatchFormatVersion.v2,
|
|
257
|
+
uncompressedEncodeV2,
|
|
258
|
+
schemaCompressedEncodeV2,
|
|
259
|
+
EncodedFieldBatchV2,
|
|
260
|
+
),
|
|
261
|
+
},
|
|
262
|
+
]);
|
|
@@ -32,7 +32,7 @@ import type { FieldBatch } from "./fieldBatch.js";
|
|
|
32
32
|
import {
|
|
33
33
|
type EncodedAnyShape,
|
|
34
34
|
type EncodedChunkShapeV1,
|
|
35
|
-
type
|
|
35
|
+
type EncodedChunkShape,
|
|
36
36
|
type EncodedChunkShapeV2,
|
|
37
37
|
type EncodedFieldBatchV1OrV2,
|
|
38
38
|
type EncodedNestedArrayShape,
|
|
@@ -64,8 +64,8 @@ export function compressedEncode(
|
|
|
64
64
|
return updateShapesAndIdentifiersEncoding(context.version, batchBuffer);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export type BufferFormat = BufferFormatGeneric<
|
|
68
|
-
export type Shape = ShapeGeneric<
|
|
67
|
+
export type BufferFormat = BufferFormatGeneric<EncodedChunkShape>;
|
|
68
|
+
export type Shape = ShapeGeneric<EncodedChunkShape>;
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Like {@link FieldEncoder}, except data will be prefixed with the key.
|
|
@@ -167,7 +167,7 @@ export function asNodesEncoder(encoder: NodeEncoder): NodesEncoder {
|
|
|
167
167
|
/**
|
|
168
168
|
* Encodes a chunk with {@link EncodedAnyShape} by prefixing the data with its shape.
|
|
169
169
|
*/
|
|
170
|
-
export class AnyShape extends ShapeGeneric<
|
|
170
|
+
export class AnyShape extends ShapeGeneric<EncodedChunkShape> {
|
|
171
171
|
private constructor() {
|
|
172
172
|
super();
|
|
173
173
|
}
|
|
@@ -272,7 +272,7 @@ export const anyFieldEncoder: FieldEncoder = {
|
|
|
272
272
|
* which is an easy way to keep all the related code together without extra objects.
|
|
273
273
|
*/
|
|
274
274
|
export class InlineArrayEncoder
|
|
275
|
-
extends ShapeGeneric<
|
|
275
|
+
extends ShapeGeneric<EncodedChunkShape>
|
|
276
276
|
implements NodesEncoder, FieldEncoder
|
|
277
277
|
{
|
|
278
278
|
public static readonly empty: InlineArrayEncoder = new InlineArrayEncoder(0, {
|
|
@@ -356,7 +356,7 @@ export class InlineArrayEncoder
|
|
|
356
356
|
/**
|
|
357
357
|
* Encodes the shape for a nested array as {@link EncodedNestedArrayShape} shape.
|
|
358
358
|
*/
|
|
359
|
-
export class NestedArrayShape extends ShapeGeneric<
|
|
359
|
+
export class NestedArrayShape extends ShapeGeneric<EncodedChunkShape> {
|
|
360
360
|
/**
|
|
361
361
|
* @param innerShape - The shape of each item in this nested array.
|
|
362
362
|
*/
|
|
@@ -367,7 +367,7 @@ export class NestedArrayShape extends ShapeGeneric<EncodedChunkShapeV1OrV2> {
|
|
|
367
367
|
public encodeShape(
|
|
368
368
|
identifiers: DeduplicationTable<string>,
|
|
369
369
|
shapes: DeduplicationTable<Shape>,
|
|
370
|
-
):
|
|
370
|
+
): EncodedChunkShape {
|
|
371
371
|
const shape: EncodedNestedArrayShape =
|
|
372
372
|
shapes.valueToIndex.get(this.innerShape) ??
|
|
373
373
|
fail(0xb4f /* index for shape not found in table */);
|
|
@@ -16,16 +16,20 @@ import { shapesV1 } from "./formatV1.js";
|
|
|
16
16
|
export type EncodedIncrementalChunkShape = Static<typeof EncodedIncrementalChunkShape>;
|
|
17
17
|
export const EncodedIncrementalChunkShape = Type.Literal(0);
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The chunk shapes supported by the V2 format.
|
|
21
|
+
* @remarks
|
|
22
|
+
* See {@link EncodedChunkShapeV2}.
|
|
23
|
+
*/
|
|
24
|
+
export const shapesV2 = {
|
|
25
|
+
...shapesV1,
|
|
26
|
+
e: Type.Optional(EncodedIncrementalChunkShape),
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* V2 extension of {@link EncodedChunkShapeV1}.
|
|
21
31
|
* @remarks
|
|
22
32
|
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
|
|
23
33
|
*/
|
|
24
34
|
export type EncodedChunkShapeV2 = Static<typeof EncodedChunkShapeV2>;
|
|
25
|
-
export const EncodedChunkShapeV2 = Type.Object(
|
|
26
|
-
{
|
|
27
|
-
...shapesV1,
|
|
28
|
-
e: Type.Optional(EncodedIncrementalChunkShape),
|
|
29
|
-
},
|
|
30
|
-
unionOptions,
|
|
31
|
-
);
|
|
35
|
+
export const EncodedChunkShapeV2 = Type.Object(shapesV2, unionOptions);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
7
|
+
|
|
8
|
+
import { unionOptions } from "../../../../codec/index.js";
|
|
9
|
+
|
|
10
|
+
import { ShapeIndex } from "./formatGeneric.js";
|
|
11
|
+
import { EncodedFieldShape, EncodedValueShape } from "./formatV1.js";
|
|
12
|
+
import { shapesV2 } from "./formatV2.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A node shape that derives from another node shape by overlaying property-level overrides.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Compresses runs of node shapes that differ only in a few properties: a base node shape
|
|
19
|
+
* defines the structural skeleton, and the specialization narrows specific properties.
|
|
20
|
+
*
|
|
21
|
+
* For example, a base `FormatNode` with a variable-boolean `bold` field can be specialized
|
|
22
|
+
* to a shape that pins `bold` to a constant `true` — every node decoded with the
|
|
23
|
+
* specialization contributes zero stream tokens for `bold`.
|
|
24
|
+
*
|
|
25
|
+
* Specialization rules: `type` is always inherited from the resolved base. `fields` overrides
|
|
26
|
+
* apply per-key: entries whose key matches a base field replace that entry's shape index in
|
|
27
|
+
* place; entries with new keys are appended after all base fields. For `value` and
|
|
28
|
+
* `extraFields`: if the property is absent on the wire, the base's value is inherited; if
|
|
29
|
+
* `null`, the resulting shape has no value / no extraFields (explicit clear); any other value
|
|
30
|
+
* replaces the base's.
|
|
31
|
+
*
|
|
32
|
+
* The `null` sentinel exists because JSON does not preserve `undefined`-valued properties,
|
|
33
|
+
* so override-vs-inherit cannot be discriminated by property presence after persistence.
|
|
34
|
+
*
|
|
35
|
+
* Decoded by {@link applySpecialization}.
|
|
36
|
+
*/
|
|
37
|
+
export type EncodedSpecializedNodeShape = Static<typeof EncodedSpecializedNodeShape>;
|
|
38
|
+
export const EncodedSpecializedNodeShape = Type.Object(
|
|
39
|
+
{
|
|
40
|
+
/**
|
|
41
|
+
* Index into the enclosing batch's shapes array of the shape this specializes.
|
|
42
|
+
* Must resolve to either an {@link EncodedNodeShape} or another
|
|
43
|
+
* `EncodedSpecializedNodeShape`; chains are followed transitively until a node shape
|
|
44
|
+
* is reached. This restriction is enforced at runtime, not by the schema.
|
|
45
|
+
*/
|
|
46
|
+
base: ShapeIndex,
|
|
47
|
+
/**
|
|
48
|
+
* Field-level overrides applied to the resolved base's `fields`. Entries whose key
|
|
49
|
+
* matches a base field replace that field's shape index in place; entries with new
|
|
50
|
+
* keys are appended after all base fields, in the order given here. Base field order
|
|
51
|
+
* is preserved — this is the stream consumption order at decode time, so encoders
|
|
52
|
+
* must serialize per-field tokens in the resulting field order, not in this list's order.
|
|
53
|
+
*/
|
|
54
|
+
fields: Type.Optional(Type.Array(EncodedFieldShape)),
|
|
55
|
+
/**
|
|
56
|
+
* If absent, inherits the resolved base's value shape. If `null`, the resulting shape
|
|
57
|
+
* has no value shape (explicit clear). Any other value replaces the base's.
|
|
58
|
+
*/
|
|
59
|
+
value: Type.Optional(Type.Union([EncodedValueShape, Type.Null()])),
|
|
60
|
+
/**
|
|
61
|
+
* If absent, inherits the resolved base's extraFields shape. If `null`, the resulting
|
|
62
|
+
* shape has no extraFields (explicit clear). Any other value replaces the base's.
|
|
63
|
+
*/
|
|
64
|
+
extraFields: Type.Optional(Type.Union([ShapeIndex, Type.Null()])),
|
|
65
|
+
},
|
|
66
|
+
{ additionalProperties: false },
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Experimental extension of {@link EncodedChunkShapeV2}.
|
|
71
|
+
* @remarks
|
|
72
|
+
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
|
|
73
|
+
*/
|
|
74
|
+
export type EncodedChunkShapeVTextExperimental = Static<
|
|
75
|
+
typeof EncodedChunkShapeVTextExperimental
|
|
76
|
+
>;
|
|
77
|
+
export const EncodedChunkShapeVTextExperimental = Type.Object(
|
|
78
|
+
{
|
|
79
|
+
...shapesV2,
|
|
80
|
+
f: Type.Optional(EncodedSpecializedNodeShape),
|
|
81
|
+
},
|
|
82
|
+
unionOptions,
|
|
83
|
+
);
|
|
@@ -16,14 +16,19 @@ export {
|
|
|
16
16
|
SpecialField,
|
|
17
17
|
} from "./formatV1.js";
|
|
18
18
|
export { EncodedIncrementalChunkShape, EncodedChunkShapeV2 } from "./formatV2.js";
|
|
19
|
+
export {
|
|
20
|
+
EncodedChunkShapeVTextExperimental,
|
|
21
|
+
EncodedSpecializedNodeShape,
|
|
22
|
+
} from "./formatVText.js";
|
|
19
23
|
export {
|
|
20
24
|
FieldBatchFormatVersion,
|
|
21
25
|
EncodedFieldBatchV1,
|
|
22
26
|
EncodedFieldBatchV2,
|
|
27
|
+
EncodedFieldBatchVTextExperimental,
|
|
23
28
|
supportsIncrementalEncoding,
|
|
24
29
|
type EncodedFieldBatchV1OrV2,
|
|
25
30
|
type EncodedFieldBatchV1AndV2,
|
|
26
|
-
type
|
|
31
|
+
type EncodedChunkShape,
|
|
27
32
|
} from "./versions.js";
|
|
28
33
|
export type {
|
|
29
34
|
ShapeIndex,
|