@fluidframework/tree 2.82.0 → 2.83.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 +43 -0
- package/README.md +33 -5
- package/api-report/tree.alpha.api.md +25 -21
- package/api-report/tree.beta.api.md +14 -2
- package/api-report/tree.legacy.beta.api.md +14 -2
- package/api-report/tree.legacy.public.api.md +1 -1
- package/api-report/tree.public.api.md +1 -1
- package/dist/alpha.d.ts +3 -3
- package/dist/beta.d.ts +1 -0
- package/dist/codec/codec.d.ts +3 -39
- package/dist/codec/codec.d.ts.map +1 -1
- package/dist/codec/codec.js +5 -50
- 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 +1 -2
- package/dist/codec/index.js.map +1 -1
- package/dist/codec/versioned/codec.d.ts +20 -7
- package/dist/codec/versioned/codec.d.ts.map +1 -1
- package/dist/codec/versioned/codec.js +56 -30
- package/dist/codec/versioned/codec.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js +6 -4
- package/dist/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/dist/extensibleUnionNode.d.ts +97 -0
- package/dist/extensibleUnionNode.d.ts.map +1 -0
- package/dist/{extensibleSchemaUnion.js → extensibleUnionNode.js} +28 -18
- package/dist/extensibleUnionNode.js.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.js +4 -4
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.js +7 -1
- package/dist/feature-libraries/forest-summary/codec.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatCommon.d.ts +3 -3
- package/dist/feature-libraries/forest-summary/formatCommon.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatCommon.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV1.d.ts +2 -3
- package/dist/feature-libraries/forest-summary/formatV1.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV1.js +1 -2
- package/dist/feature-libraries/forest-summary/formatV1.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV2.d.ts +2 -3
- package/dist/feature-libraries/forest-summary/formatV2.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV2.js +1 -2
- package/dist/feature-libraries/forest-summary/formatV2.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +2 -2
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +4 -4
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/dist/feature-libraries/schema-index/codec.js +6 -4
- package/dist/feature-libraries/schema-index/codec.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/dist/shared-tree/tree.d.ts +1 -1
- package/dist/shared-tree/tree.js.map +1 -1
- package/dist/shared-tree/treeAlpha.d.ts +1 -1
- package/dist/shared-tree/treeAlpha.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +2 -4
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsCommons.d.ts +3 -3
- package/dist/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsCommons.js +2 -2
- package/dist/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts +2 -2
- package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.js +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +2 -2
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.js +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
- 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/api/index.d.ts +1 -1
- package/dist/simple-tree/api/index.d.ts.map +1 -1
- package/dist/simple-tree/api/index.js +2 -2
- package/dist/simple-tree/api/index.js.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +148 -29
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js +180 -99
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/dist/simple-tree/api/tree.d.ts +1 -1
- package/dist/simple-tree/api/tree.js.map +1 -1
- package/dist/simple-tree/api/treeBeta.d.ts +1 -1
- package/dist/simple-tree/api/treeBeta.js.map +1 -1
- package/dist/simple-tree/core/allowedTypes.d.ts +1 -1
- package/dist/simple-tree/core/allowedTypes.js.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts +1 -0
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.js +29 -0
- package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/dist/simple-tree/index.d.ts +1 -1
- 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.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +4 -13
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +33 -7
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/dist/text/textDomainFormatted.d.ts +3 -3
- package/dist/text/textDomainFormatted.d.ts.map +1 -1
- package/dist/text/textDomainFormatted.js +48 -32
- package/dist/text/textDomainFormatted.js.map +1 -1
- package/dist/util/bTreeUtils.d.ts.map +1 -1
- package/dist/util/bTreeUtils.js +6 -6
- package/dist/util/bTreeUtils.js.map +1 -1
- package/dist/util/rangeMap.d.ts.map +1 -1
- package/dist/util/rangeMap.js +5 -6
- package/dist/util/rangeMap.js.map +1 -1
- package/lib/alpha.d.ts +3 -3
- package/lib/beta.d.ts +1 -0
- package/lib/codec/codec.d.ts +3 -39
- package/lib/codec/codec.d.ts.map +1 -1
- package/lib/codec/codec.js +4 -47
- 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 +20 -7
- package/lib/codec/versioned/codec.d.ts.map +1 -1
- package/lib/codec/versioned/codec.js +59 -33
- package/lib/codec/versioned/codec.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.js +6 -4
- package/lib/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/lib/extensibleUnionNode.d.ts +97 -0
- package/lib/extensibleUnionNode.d.ts.map +1 -0
- package/lib/{extensibleSchemaUnion.js → extensibleUnionNode.js} +28 -18
- package/lib/extensibleUnionNode.js.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.js +5 -5
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.js +8 -2
- package/lib/feature-libraries/forest-summary/codec.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatCommon.d.ts +3 -3
- package/lib/feature-libraries/forest-summary/formatCommon.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatCommon.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV1.d.ts +2 -3
- package/lib/feature-libraries/forest-summary/formatV1.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV1.js +1 -2
- package/lib/feature-libraries/forest-summary/formatV1.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV2.d.ts +2 -3
- package/lib/feature-libraries/forest-summary/formatV2.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV2.js +1 -2
- package/lib/feature-libraries/forest-summary/formatV2.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +2 -2
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +4 -4
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/lib/feature-libraries/schema-index/codec.js +6 -4
- package/lib/feature-libraries/schema-index/codec.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/lib/shared-tree/tree.d.ts +1 -1
- package/lib/shared-tree/tree.js.map +1 -1
- package/lib/shared-tree/treeAlpha.d.ts +1 -1
- package/lib/shared-tree/treeAlpha.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +2 -4
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsCommons.d.ts +3 -3
- package/lib/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsCommons.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts +2 -2
- package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsV1toV4.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +2 -2
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js +2 -2
- package/lib/shared-tree-core/messageCodecs.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 +1 -1
- package/lib/simple-tree/api/index.js.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +148 -29
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js +179 -98
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/lib/simple-tree/api/tree.d.ts +1 -1
- package/lib/simple-tree/api/tree.js.map +1 -1
- package/lib/simple-tree/api/treeBeta.d.ts +1 -1
- package/lib/simple-tree/api/treeBeta.js.map +1 -1
- package/lib/simple-tree/core/allowedTypes.d.ts +1 -1
- package/lib/simple-tree/core/allowedTypes.js.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts +1 -0
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.js +29 -0
- package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/lib/simple-tree/index.d.ts +1 -1
- 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.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.js +5 -14
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +34 -8
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/lib/text/textDomainFormatted.d.ts +3 -3
- package/lib/text/textDomainFormatted.d.ts.map +1 -1
- package/lib/text/textDomainFormatted.js +30 -14
- package/lib/text/textDomainFormatted.js.map +1 -1
- package/lib/util/bTreeUtils.d.ts.map +1 -1
- package/lib/util/bTreeUtils.js +6 -6
- package/lib/util/bTreeUtils.js.map +1 -1
- package/lib/util/rangeMap.d.ts.map +1 -1
- package/lib/util/rangeMap.js +5 -6
- package/lib/util/rangeMap.js.map +1 -1
- package/package.json +23 -23
- package/src/codec/codec.ts +10 -112
- package/src/codec/index.ts +0 -3
- package/src/codec/versioned/codec.ts +119 -83
- package/src/core/tree/detachedFieldIndexCodecs.ts +6 -4
- package/src/{extensibleSchemaUnion.ts → extensibleUnionNode.ts} +61 -19
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +5 -11
- package/src/feature-libraries/forest-summary/codec.ts +8 -7
- package/src/feature-libraries/forest-summary/formatCommon.ts +5 -3
- package/src/feature-libraries/forest-summary/formatV1.ts +1 -3
- package/src/feature-libraries/forest-summary/formatV2.ts +1 -3
- package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +5 -6
- package/src/feature-libraries/schema-index/codec.ts +6 -4
- package/src/index.ts +3 -3
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/sharedTreeChangeCodecs.ts +2 -2
- package/src/shared-tree/tree.ts +1 -1
- package/src/shared-tree/treeAlpha.ts +1 -1
- package/src/shared-tree/treeCheckout.ts +2 -4
- package/src/shared-tree-core/editManagerCodecsCommons.ts +7 -7
- package/src/shared-tree-core/editManagerCodecsV1toV4.ts +3 -10
- package/src/shared-tree-core/editManagerCodecsVSharedBranches.ts +3 -10
- package/src/shared-tree-core/messageCodecs.ts +2 -6
- package/src/simple-tree/api/index.ts +2 -2
- package/src/simple-tree/api/snapshotCompatibilityChecker.ts +344 -142
- package/src/simple-tree/api/tree.ts +1 -1
- package/src/simple-tree/api/treeBeta.ts +1 -1
- package/src/simple-tree/core/allowedTypes.ts +1 -1
- package/src/simple-tree/core/unhydratedFlexTree.ts +43 -1
- package/src/simple-tree/index.ts +2 -2
- package/src/simple-tree/node-kinds/array/arrayNode.ts +13 -19
- package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +51 -10
- package/src/text/textDomainFormatted.ts +37 -17
- package/src/util/bTreeUtils.ts +10 -6
- package/src/util/rangeMap.ts +9 -6
- package/api-extractor-lint.json +0 -4
- package/dist/extensibleSchemaUnion.d.ts +0 -72
- package/dist/extensibleSchemaUnion.d.ts.map +0 -1
- package/dist/extensibleSchemaUnion.js.map +0 -1
- package/lib/extensibleSchemaUnion.d.ts +0 -72
- package/lib/extensibleSchemaUnion.d.ts.map +0 -1
- package/lib/extensibleSchemaUnion.js.map +0 -1
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
|
+
import { assert, debugAssert } from "@fluidframework/core-utils/internal";
|
|
7
7
|
import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";
|
|
8
8
|
import {
|
|
9
9
|
getConfigForMinVersionForCollabIterable,
|
|
10
|
-
|
|
10
|
+
lowestMinVersionForCollab,
|
|
11
11
|
type MinimumMinorSemanticVersion,
|
|
12
12
|
type SemanticVersion,
|
|
13
13
|
} from "@fluidframework/runtime-utils/internal";
|
|
@@ -27,9 +27,7 @@ import {
|
|
|
27
27
|
withSchemaValidation,
|
|
28
28
|
type FormatVersion,
|
|
29
29
|
type CodecWriteOptions,
|
|
30
|
-
type IMultiFormatCodec,
|
|
31
30
|
type CodecName,
|
|
32
|
-
ensureBinaryEncoding,
|
|
33
31
|
type CodecTree,
|
|
34
32
|
} from "../codec.js";
|
|
35
33
|
|
|
@@ -151,7 +149,7 @@ export function makeVersionDispatchingCodec<TDecoded, TContext>(
|
|
|
151
149
|
family: ICodecFamily<TDecoded, TContext>,
|
|
152
150
|
options: ICodecOptions & { writeVersion: FormatVersion },
|
|
153
151
|
): IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext> {
|
|
154
|
-
const writeCodec = family.resolve(options.writeVersion)
|
|
152
|
+
const writeCodec = family.resolve(options.writeVersion);
|
|
155
153
|
const supportedVersions = new Set(family.getSupportedFormats());
|
|
156
154
|
return makeVersionedCodec(supportedVersions, options, {
|
|
157
155
|
encode(data, context): Versioned {
|
|
@@ -159,7 +157,7 @@ export function makeVersionDispatchingCodec<TDecoded, TContext>(
|
|
|
159
157
|
},
|
|
160
158
|
decode(data: Versioned, context) {
|
|
161
159
|
const codec = family.resolve(data.version);
|
|
162
|
-
return codec.
|
|
160
|
+
return codec.decode(data, context);
|
|
163
161
|
},
|
|
164
162
|
});
|
|
165
163
|
}
|
|
@@ -170,10 +168,9 @@ export function makeVersionDispatchingCodec<TDecoded, TContext>(
|
|
|
170
168
|
* The codec should not perform its own schema validation.
|
|
171
169
|
* The schema validation gets added when normalizing to {@link NormalizedCodecVersion}.
|
|
172
170
|
*/
|
|
173
|
-
export type CodecAndSchema<TDecoded, TContext = void> = {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
171
|
+
export type CodecAndSchema<TDecoded, TContext = void> = {
|
|
172
|
+
readonly schema: TSchema;
|
|
173
|
+
} & IJsonCodec<TDecoded, VersionedJson, JsonCompatibleReadOnly, TContext>;
|
|
177
174
|
|
|
178
175
|
/**
|
|
179
176
|
* A codec alongside its format version and schema.
|
|
@@ -182,6 +179,14 @@ export interface CodecVersionBase<
|
|
|
182
179
|
T = unknown,
|
|
183
180
|
TFormatVersion extends FormatVersion = FormatVersion,
|
|
184
181
|
> {
|
|
182
|
+
/**
|
|
183
|
+
* When `undefined` the codec will never be selected as a write version except via override.
|
|
184
|
+
* @remarks
|
|
185
|
+
* This format will be used for decode if data in it needs to be decoded, regardless of `minVersionForCollab`.
|
|
186
|
+
* `undefined` should be used for unstable codec versions (with string FormatVersions),
|
|
187
|
+
* as well as previously stabilized formats that are discontinued (meaning we always prefer to use some other format for encoding).
|
|
188
|
+
*/
|
|
189
|
+
readonly minVersionForCollab: MinimumVersionForCollab | undefined;
|
|
185
190
|
readonly formatVersion: TFormatVersion;
|
|
186
191
|
readonly codec: T;
|
|
187
192
|
}
|
|
@@ -228,11 +233,8 @@ export interface NormalizedCodecVersion<
|
|
|
228
233
|
* @remarks
|
|
229
234
|
* Produced by {@link ClientVersionDispatchingCodecBuilder.applyOptions}.
|
|
230
235
|
*/
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
TContext,
|
|
234
|
-
TFormatVersion extends FormatVersion,
|
|
235
|
-
> extends CodecVersionBase<
|
|
236
|
+
interface EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion extends FormatVersion>
|
|
237
|
+
extends CodecVersionBase<
|
|
236
238
|
IJsonCodec<TDecoded, VersionedJson, JsonCompatibleReadOnly, TContext>,
|
|
237
239
|
TFormatVersion
|
|
238
240
|
> {}
|
|
@@ -258,17 +260,16 @@ function normalizeCodecVersion<
|
|
|
258
260
|
options: TBuildOptions,
|
|
259
261
|
): IJsonCodec<TDecoded, VersionedJson, JsonCompatibleReadOnly, TContext> => {
|
|
260
262
|
const built = codecBuilder(options);
|
|
261
|
-
// We currently don't expose or use binary formats, but someday we might.
|
|
262
|
-
const multiFormat = ensureBinaryEncoding<TDecoded, TContext, VersionedJson>(built);
|
|
263
263
|
return makeVersionedValidatedCodec(
|
|
264
264
|
options,
|
|
265
265
|
new Set([codecVersion.formatVersion]),
|
|
266
266
|
built.schema,
|
|
267
|
-
|
|
267
|
+
built,
|
|
268
268
|
);
|
|
269
269
|
};
|
|
270
270
|
|
|
271
271
|
return {
|
|
272
|
+
minVersionForCollab: codecVersion.minVersionForCollab,
|
|
272
273
|
formatVersion: codecVersion.formatVersion,
|
|
273
274
|
codec,
|
|
274
275
|
};
|
|
@@ -287,13 +288,20 @@ export class ClientVersionDispatchingCodecBuilder<
|
|
|
287
288
|
TFormatVersion extends FormatVersion,
|
|
288
289
|
TBuildOptions extends CodecWriteOptions,
|
|
289
290
|
> {
|
|
290
|
-
public readonly registry:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
public readonly registry: readonly NormalizedCodecVersion<
|
|
292
|
+
TDecoded,
|
|
293
|
+
TContext,
|
|
294
|
+
TFormatVersion,
|
|
295
|
+
TBuildOptions
|
|
296
|
+
>[];
|
|
294
297
|
|
|
295
298
|
/**
|
|
296
299
|
* Use {@link ClientVersionDispatchingCodecBuilder.build} to create an instance of this class.
|
|
300
|
+
* @remarks
|
|
301
|
+
* Inputs to this are assumed to be constants in the code controlled by the developers of this package,
|
|
302
|
+
* and constructed at least once during tests.
|
|
303
|
+
* Because of this, the validation of these inputs done with debugAssert should be sufficient,
|
|
304
|
+
* and using debugAssert avoids bloating the bundle size for production users.
|
|
297
305
|
*/
|
|
298
306
|
private constructor(
|
|
299
307
|
/**
|
|
@@ -303,9 +311,7 @@ export class ClientVersionDispatchingCodecBuilder<
|
|
|
303
311
|
/**
|
|
304
312
|
* The registry of codecs which this builder can use to encode and decode data.
|
|
305
313
|
*/
|
|
306
|
-
inputRegistry:
|
|
307
|
-
CodecVersion<TDecoded, TContext, TFormatVersion, TBuildOptions>
|
|
308
|
-
>,
|
|
314
|
+
inputRegistry: readonly CodecVersion<TDecoded, TContext, TFormatVersion, TBuildOptions>[],
|
|
309
315
|
) {
|
|
310
316
|
type Normalized = NormalizedCodecVersion<
|
|
311
317
|
TDecoded,
|
|
@@ -313,18 +319,41 @@ export class ClientVersionDispatchingCodecBuilder<
|
|
|
313
319
|
TFormatVersion,
|
|
314
320
|
TBuildOptions
|
|
315
321
|
>;
|
|
316
|
-
const normalizedRegistry =
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
const normalizedRegistry: Normalized[] = [];
|
|
323
|
+
const formats: Set<FormatVersion> = new Set();
|
|
324
|
+
const versions: Set<string | undefined> = new Set();
|
|
325
|
+
|
|
326
|
+
for (const codec of inputRegistry) {
|
|
327
|
+
debugAssert(
|
|
328
|
+
() =>
|
|
329
|
+
!formats.has(codec.formatVersion) ||
|
|
330
|
+
`duplicate codec format ${name} ${codec.formatVersion}`,
|
|
331
|
+
);
|
|
332
|
+
debugAssert(
|
|
333
|
+
() =>
|
|
334
|
+
codec.minVersionForCollab === undefined ||
|
|
335
|
+
typeof codec.formatVersion !== "string" ||
|
|
336
|
+
`unstable format ${JSON.stringify(codec.formatVersion)} (string formats) must not have a minVersionForCollab in ${name}`,
|
|
337
|
+
);
|
|
338
|
+
formats.add(codec.formatVersion);
|
|
324
339
|
const normalizedCodec = normalizeCodecVersion(codec);
|
|
325
|
-
normalizedRegistry.
|
|
340
|
+
normalizedRegistry.push(normalizedCodec);
|
|
341
|
+
if (codec.minVersionForCollab !== undefined) {
|
|
342
|
+
debugAssert(
|
|
343
|
+
() =>
|
|
344
|
+
!versions.has(codec.minVersionForCollab) ||
|
|
345
|
+
`Codec ${name} has multiple entries for version ${JSON.stringify(codec.minVersionForCollab)}`,
|
|
346
|
+
);
|
|
347
|
+
versions.add(codec.minVersionForCollab);
|
|
348
|
+
}
|
|
326
349
|
}
|
|
327
350
|
|
|
351
|
+
debugAssert(
|
|
352
|
+
() =>
|
|
353
|
+
versions.has(lowestMinVersionForCollab) ||
|
|
354
|
+
`Codec ${name} is missing entry for lowestMinVersionForCollab`,
|
|
355
|
+
);
|
|
356
|
+
|
|
328
357
|
this.registry = normalizedRegistry;
|
|
329
358
|
}
|
|
330
359
|
|
|
@@ -336,18 +365,12 @@ export class ClientVersionDispatchingCodecBuilder<
|
|
|
336
365
|
*/
|
|
337
366
|
public applyOptions(
|
|
338
367
|
options: TBuildOptions,
|
|
339
|
-
):
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
{
|
|
346
|
-
formatVersion: codec.formatVersion,
|
|
347
|
-
codec: codec.codec(options),
|
|
348
|
-
},
|
|
349
|
-
] as const,
|
|
350
|
-
);
|
|
368
|
+
): EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion>[] {
|
|
369
|
+
return this.registry.map((codec) => ({
|
|
370
|
+
minVersionForCollab: codec.minVersionForCollab,
|
|
371
|
+
formatVersion: codec.formatVersion,
|
|
372
|
+
codec: codec.codec(options),
|
|
373
|
+
}));
|
|
351
374
|
}
|
|
352
375
|
|
|
353
376
|
/**
|
|
@@ -361,7 +384,7 @@ export class ClientVersionDispatchingCodecBuilder<
|
|
|
361
384
|
const fromFormatVersion = new Map<
|
|
362
385
|
FormatVersion,
|
|
363
386
|
EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion>
|
|
364
|
-
>(applied.map((
|
|
387
|
+
>(applied.map((codec) => [codec.formatVersion, codec]));
|
|
365
388
|
return {
|
|
366
389
|
encode: (data: TDecoded, context: TContext): JsonCompatibleReadOnly => {
|
|
367
390
|
return writeVersion.codec.encode(data, context);
|
|
@@ -382,7 +405,7 @@ The client which encoded this data likely specified an "minVersionForCollab" val
|
|
|
382
405
|
|
|
383
406
|
public getCodecTree(clientVersion: MinimumVersionForCollab): CodecTree {
|
|
384
407
|
// TODO: add support for children codecs.
|
|
385
|
-
const selected =
|
|
408
|
+
const selected = getWriteVersionNoOverrides(this.registry, clientVersion);
|
|
386
409
|
return {
|
|
387
410
|
name: this.name,
|
|
388
411
|
version: selected.formatVersion,
|
|
@@ -400,7 +423,7 @@ The client which encoded this data likely specified an "minVersionForCollab" val
|
|
|
400
423
|
public static build<
|
|
401
424
|
Name extends CodecName,
|
|
402
425
|
Entry extends CodecVersion<unknown, unknown, FormatVersion, never>,
|
|
403
|
-
>(name: Name, inputRegistry:
|
|
426
|
+
>(name: Name, inputRegistry: readonly Entry[]) {
|
|
404
427
|
type TDecoded2 =
|
|
405
428
|
Entry extends CodecVersion<infer D, unknown, FormatVersion, never> ? D : never;
|
|
406
429
|
type TContext2 =
|
|
@@ -409,18 +432,24 @@ The client which encoded this data likely specified an "minVersionForCollab" val
|
|
|
409
432
|
Entry extends CodecVersion<unknown, unknown, infer F, never> ? F : never;
|
|
410
433
|
type TBuildOptions2 =
|
|
411
434
|
Entry extends CodecVersion<unknown, unknown, FormatVersion, infer B> ? B : never;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
435
|
+
|
|
436
|
+
type CodecFinal = CodecVersion<
|
|
437
|
+
TDecoded2,
|
|
438
|
+
// If it does not matter what context is provided, undefined is fine, so allow it to be omitted.
|
|
439
|
+
unknown extends TContext2 ? void : TContext2,
|
|
440
|
+
TFormatVersion2,
|
|
441
|
+
TBuildOptions2
|
|
442
|
+
>;
|
|
443
|
+
|
|
444
|
+
const input = inputRegistry as readonly unknown[] as readonly CodecFinal[];
|
|
445
|
+
|
|
446
|
+
const builder = new ClientVersionDispatchingCodecBuilder<
|
|
447
|
+
Name,
|
|
448
|
+
TDecoded2,
|
|
449
|
+
unknown extends TContext2 ? void : TContext2,
|
|
450
|
+
TFormatVersion2,
|
|
451
|
+
TBuildOptions2
|
|
452
|
+
>(name, input);
|
|
424
453
|
return builder;
|
|
425
454
|
}
|
|
426
455
|
}
|
|
@@ -433,39 +462,51 @@ The client which encoded this data likely specified an "minVersionForCollab" val
|
|
|
433
462
|
function getWriteVersion<T extends CodecVersionBase>(
|
|
434
463
|
name: CodecName,
|
|
435
464
|
options: CodecWriteOptions,
|
|
436
|
-
versions: readonly
|
|
465
|
+
versions: readonly T[],
|
|
437
466
|
): T {
|
|
438
467
|
if (options.writeVersionOverrides?.has(name) === true) {
|
|
439
468
|
const selectedFormatVersion = options.writeVersionOverrides.get(name);
|
|
440
|
-
const selected = versions.find(
|
|
441
|
-
([_v, codec]) => codec.formatVersion === selectedFormatVersion,
|
|
442
|
-
);
|
|
469
|
+
const selected = versions.find((codec) => codec.formatVersion === selectedFormatVersion);
|
|
443
470
|
if (selected === undefined) {
|
|
444
471
|
throw new UsageError(
|
|
445
|
-
`Codec "${name}" does not support requested format version ${selectedFormatVersion}. Supported versions are: ${versionList(versions)}.`,
|
|
472
|
+
`Codec "${name}" does not support requested format version ${JSON.stringify(selectedFormatVersion)}. Supported versions are: ${versionList(versions)}.`,
|
|
446
473
|
);
|
|
447
474
|
} else if (options.allowPossiblyIncompatibleWriteVersionOverrides !== true) {
|
|
448
|
-
const selectedMinVersionForCollab = selected
|
|
449
|
-
// Currently all versions must specify a minVersionForCollab, so undefined is not expected here.
|
|
450
|
-
// TODO: It should be possible to have a version which would never be automatically selected for write (and thus does not have or need a minVersionForCollab), but can be selected via override.
|
|
451
|
-
// Use-cases for this include experimental versions not yet stable, and discontinued or intermediate versions which are mainly being kept for read compatibility but still support writing (perhaps for round-trip testing).
|
|
452
|
-
// For now, this check should never pass, and there is no way to create such a version yet.
|
|
475
|
+
const selectedMinVersionForCollab = selected.minVersionForCollab;
|
|
453
476
|
if (selectedMinVersionForCollab === undefined) {
|
|
454
477
|
throw new UsageError(
|
|
455
|
-
`Codec "${name}" does not support requested format version ${selectedFormatVersion} because it
|
|
478
|
+
`Codec "${name}" does not support requested format version ${JSON.stringify(selectedFormatVersion)} because it has minVersionForCollab undefined. Use "allowPossiblyIncompatibleWriteVersionOverrides" to suppress this error if appropriate.`,
|
|
456
479
|
);
|
|
457
480
|
} else if (gt(selectedMinVersionForCollab, options.minVersionForCollab)) {
|
|
458
481
|
throw new UsageError(
|
|
459
|
-
`Codec "${name}" does not support requested format version ${selectedFormatVersion} because it is only compatible back to client version ${selectedMinVersionForCollab} and the requested oldest compatible client was ${options.minVersionForCollab}. Use "allowPossiblyIncompatibleWriteVersionOverrides" to suppress this error if appropriate.`,
|
|
482
|
+
`Codec "${name}" does not support requested format version ${JSON.stringify(selectedFormatVersion)} because it is only compatible back to client version ${selectedMinVersionForCollab} and the requested oldest compatible client was ${options.minVersionForCollab}. Use "allowPossiblyIncompatibleWriteVersionOverrides" to suppress this error if appropriate.`,
|
|
460
483
|
);
|
|
461
484
|
}
|
|
462
485
|
}
|
|
463
486
|
|
|
464
|
-
return selected
|
|
487
|
+
return selected;
|
|
465
488
|
}
|
|
489
|
+
|
|
490
|
+
return getWriteVersionNoOverrides(versions, options.minVersionForCollab);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Selects which format should be used when writing data, without consider overrides.
|
|
495
|
+
*/
|
|
496
|
+
function getWriteVersionNoOverrides<T extends CodecVersionBase>(
|
|
497
|
+
versions: readonly T[],
|
|
498
|
+
minVersionForCollab: MinimumVersionForCollab,
|
|
499
|
+
): T {
|
|
500
|
+
const stableVersions: [MinimumMinorSemanticVersion | MinimumVersionForCollab, T][] = [];
|
|
501
|
+
for (const version of versions) {
|
|
502
|
+
if (version.minVersionForCollab !== undefined) {
|
|
503
|
+
stableVersions.push([version.minVersionForCollab, version]);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
466
507
|
const result: T = getConfigForMinVersionForCollabIterable(
|
|
467
|
-
|
|
468
|
-
|
|
508
|
+
minVersionForCollab,
|
|
509
|
+
stableVersions,
|
|
469
510
|
);
|
|
470
511
|
return result;
|
|
471
512
|
}
|
|
@@ -473,11 +514,6 @@ function getWriteVersion<T extends CodecVersionBase>(
|
|
|
473
514
|
/**
|
|
474
515
|
* Formats a list of versions for use in UsageErrors.
|
|
475
516
|
*/
|
|
476
|
-
function versionList(
|
|
477
|
-
versions
|
|
478
|
-
MinimumMinorSemanticVersion | MinimumVersionForCollab,
|
|
479
|
-
CodecVersionBase,
|
|
480
|
-
][],
|
|
481
|
-
): string {
|
|
482
|
-
return `${Array.from(versions, ([_v, codec]) => codec.formatVersion).join(", ")}`;
|
|
517
|
+
function versionList(versions: readonly CodecVersionBase[]): string {
|
|
518
|
+
return JSON.stringify(Array.from(versions, (codec) => codec.formatVersion));
|
|
483
519
|
}
|
|
@@ -24,16 +24,18 @@ type BuildData = CodecWriteOptions & {
|
|
|
24
24
|
|
|
25
25
|
export const detachedFieldIndexCodecBuilder = ClientVersionDispatchingCodecBuilder.build(
|
|
26
26
|
"DetachedFieldIndex",
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
[
|
|
28
|
+
{
|
|
29
|
+
minVersionForCollab: lowestMinVersionForCollab,
|
|
29
30
|
formatVersion: DetachedFieldIndexFormatVersion.v1,
|
|
30
31
|
codec: (buildData: BuildData) =>
|
|
31
32
|
makeDetachedNodeToFieldCodecV1(buildData.revisionTagCodec, buildData.idCompressor),
|
|
32
33
|
},
|
|
33
|
-
|
|
34
|
+
{
|
|
35
|
+
minVersionForCollab: FluidClientVersion.v2_52,
|
|
34
36
|
formatVersion: DetachedFieldIndexFormatVersion.v2,
|
|
35
37
|
codec: (buildData: BuildData) =>
|
|
36
38
|
makeDetachedNodeToFieldCodecV2(buildData.revisionTagCodec, buildData.idCompressor),
|
|
37
39
|
},
|
|
38
|
-
|
|
40
|
+
],
|
|
39
41
|
);
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
7
|
+
|
|
6
8
|
import { TreeAlpha, Tree } from "./shared-tree/index.js";
|
|
7
9
|
import type {
|
|
8
10
|
TreeNodeSchema,
|
|
@@ -15,15 +17,16 @@ import type {
|
|
|
15
17
|
import {
|
|
16
18
|
createCustomizedFluidFrameworkScopedFactory,
|
|
17
19
|
eraseSchemaDetailsSubclassable,
|
|
20
|
+
getInnerNode,
|
|
18
21
|
SchemaFactory,
|
|
19
22
|
TreeBeta,
|
|
20
23
|
} from "./simple-tree/index.js";
|
|
21
24
|
import type { UnionToIntersection } from "./util/index.js";
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
|
-
* Utilities for creating extensible
|
|
27
|
+
* Utilities for creating extensible unions using a node.
|
|
25
28
|
* @remarks
|
|
26
|
-
* Use {@link
|
|
29
|
+
* Use {@link ExtensibleUnionNode.createSchema} to create the union schema.
|
|
27
30
|
*
|
|
28
31
|
* Unlike a schema union created using {@link SchemaStaticsBeta.staged | staged} allowed types, this union allows for unknown future types to exist in addition to the known types.
|
|
29
32
|
* This allows for faster roll-outs of new types without waiting for old clients to be updated to be aware of them.
|
|
@@ -35,42 +38,68 @@ import type { UnionToIntersection } from "./util/index.js";
|
|
|
35
38
|
*
|
|
36
39
|
* @example
|
|
37
40
|
* ```typescript
|
|
38
|
-
* const sf = new SchemaFactoryBeta("
|
|
41
|
+
* const sf = new SchemaFactoryBeta("extensibleUnionNodeExample.items");
|
|
39
42
|
* class ItemA extends sf.object("A", { x: sf.string }) {}
|
|
40
43
|
* class ItemB extends sf.object("B", { x: sf.number }) {}
|
|
41
44
|
*
|
|
42
|
-
* class AnyItem extends
|
|
45
|
+
* class AnyItem extends ExtensibleUnionNode.createSchema(
|
|
43
46
|
* [ItemA, ItemB], // Future versions may add more members here
|
|
44
47
|
* sf,
|
|
45
48
|
* "ExtensibleUnion",
|
|
46
49
|
* ) {}
|
|
47
50
|
* // Instances of the union are created using `create`.
|
|
48
51
|
* const anyItem = AnyItem.create(new ItemA({ x: "hello" }));
|
|
49
|
-
* // Reading the content from the union is done via `
|
|
52
|
+
* // Reading the content from the union is done via the `union` property,
|
|
50
53
|
* // which can be `undefined` to handle the case where a future version of this schema allows a type unknown to the current version.
|
|
51
|
-
* const childNode: ItemA | ItemB | undefined = anyItem.
|
|
54
|
+
* const childNode: ItemA | ItemB | undefined = anyItem.union;
|
|
52
55
|
* // To determine which member of the union was present, its schema can be inspected:
|
|
53
56
|
* const aSchema = Tree.schema(childNode ?? assert.fail("No child"));
|
|
54
57
|
* assert.equal(aSchema, ItemA);
|
|
55
58
|
* ```
|
|
56
|
-
* @
|
|
59
|
+
* @beta
|
|
57
60
|
*/
|
|
58
|
-
export namespace
|
|
61
|
+
export namespace ExtensibleUnionNode {
|
|
59
62
|
/**
|
|
60
|
-
* Members for classes created by {@link
|
|
61
|
-
* @
|
|
63
|
+
* Members for classes created by {@link ExtensibleUnionNode.createSchema}.
|
|
64
|
+
* @beta
|
|
62
65
|
*/
|
|
63
66
|
export interface Members<T> {
|
|
64
67
|
/**
|
|
65
|
-
* The child wrapped by this node
|
|
68
|
+
* The child wrapped by this node has one of the types allowed by the union,
|
|
66
69
|
* or `undefined` if the type is one which was added to the union by a future version of this schema.
|
|
70
|
+
*
|
|
71
|
+
* @throws if {@link ExtensibleUnionNode.Members.isValid} is false.
|
|
72
|
+
*/
|
|
73
|
+
readonly union: T | undefined;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns true, unless this node is in an invalid state.
|
|
77
|
+
* @remarks
|
|
78
|
+
* A well behaved application should not need this API.
|
|
79
|
+
* If an application is hitting errors when accessing {@link ExtensibleUnionNode.Members.union},
|
|
80
|
+
* this API can be used to help detect and recover from the invalid state which causes those errors (for example by replacing the invalid nodes with new ones).
|
|
81
|
+
*
|
|
82
|
+
* In this context "invalid" means that the internal implementation details of this node have had their invariants violated.
|
|
83
|
+
* This can happen when:
|
|
84
|
+
* - Using weakly typed construction APIs like {@link (TreeBeta:interface).importConcise} or {@link (TreeAlpha:interface).importVerbose} to construct an invalid state directly.
|
|
85
|
+
* Using such APIs, even when not creating invalid nodes, is not supported for this schema,
|
|
86
|
+
* since doing so requires knowing the implementation details of this node which are subject to change.
|
|
87
|
+
* - By editing a document using a different client using a different schema for this node.
|
|
88
|
+
* - Violating the TypeScript types to directly manipulate the node internals.
|
|
89
|
+
* - A bug in this node's implementation (possibly in a different client) corrupted the node.
|
|
90
|
+
* - Corruption of the document this node is contained in.
|
|
91
|
+
*
|
|
92
|
+
* @privateRemarks
|
|
93
|
+
* We could support {@link (TreeBeta:interface).exportVerbose} using {@link KeyEncodingOptions.allStoredKeys}
|
|
94
|
+
* then {@link (TreeAlpha:interface).importVerbose} with {@link KeyEncodingOptions.knownStoredKeys}.
|
|
95
|
+
* However, even this will error (but will not produce an invalid node) if there is a node of an unknown type in the union.
|
|
67
96
|
*/
|
|
68
|
-
|
|
97
|
+
isValid(): boolean;
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
/**
|
|
72
|
-
* Statics for classes created by {@link
|
|
73
|
-
* @
|
|
101
|
+
* Statics for classes created by {@link ExtensibleUnionNode.createSchema}.
|
|
102
|
+
* @beta
|
|
74
103
|
*/
|
|
75
104
|
export interface Statics<T extends readonly TreeNodeSchema[]> {
|
|
76
105
|
/**
|
|
@@ -86,11 +115,11 @@ export namespace ExtensibleSchemaUnion {
|
|
|
86
115
|
* Create an extensible schema union which currently supports the types in `types`,
|
|
87
116
|
* but tolerates collaboration with future versions that may include additional types.
|
|
88
117
|
* @remarks
|
|
89
|
-
* See {@link
|
|
90
|
-
* @
|
|
118
|
+
* See {@link ExtensibleUnionNode} for an example use.
|
|
119
|
+
* @beta
|
|
91
120
|
*/
|
|
92
121
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
93
|
-
export function
|
|
122
|
+
export function createSchema<
|
|
94
123
|
const T extends readonly TreeNodeSchema[],
|
|
95
124
|
const TScope extends string,
|
|
96
125
|
const TName extends string,
|
|
@@ -101,19 +130,32 @@ export namespace ExtensibleSchemaUnion {
|
|
|
101
130
|
}
|
|
102
131
|
const schemaFactory = createCustomizedFluidFrameworkScopedFactory(
|
|
103
132
|
inputSchemaFactory,
|
|
104
|
-
"
|
|
133
|
+
"extensibleUnionNode",
|
|
105
134
|
);
|
|
135
|
+
|
|
106
136
|
class Union
|
|
107
137
|
extends schemaFactory.object(name, record, { allowUnknownOptionalFields: true })
|
|
108
138
|
implements Members<TreeNodeFromImplicitAllowedTypes<T>>
|
|
109
139
|
{
|
|
110
|
-
public get
|
|
140
|
+
public get union(): TreeNodeFromImplicitAllowedTypes<T> | undefined {
|
|
141
|
+
if (!this.isValid()) {
|
|
142
|
+
throw new UsageError(
|
|
143
|
+
`This ExtensibleUnionNode (${Union.identifier}) is in an invalid state. It must have been edited by another client using a different schema or been directly imported or constructed in an invalid state.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
111
146
|
for (const [_key, child] of TreeAlpha.children(this)) {
|
|
112
147
|
return child as TreeNodeFromImplicitAllowedTypes<T>;
|
|
113
148
|
}
|
|
114
149
|
return undefined;
|
|
115
150
|
}
|
|
116
151
|
|
|
152
|
+
public isValid(): boolean {
|
|
153
|
+
// Use inner node, since it includes populated fields even when they are unknown.
|
|
154
|
+
const inner = getInnerNode(this);
|
|
155
|
+
// Fields only includes non-empty fields, so this is what we need to check the one child invariant.
|
|
156
|
+
return [...inner.fields].length === 1;
|
|
157
|
+
}
|
|
158
|
+
|
|
117
159
|
public static create<TThis extends TreeNodeSchema>(
|
|
118
160
|
this: TThis,
|
|
119
161
|
child: TreeNodeFromImplicitAllowedTypes<T>,
|
|
@@ -35,13 +35,7 @@ import { TreeCompressionStrategy } from "../../treeCompressionUtils.js";
|
|
|
35
35
|
|
|
36
36
|
import { decode } from "./chunkDecoding.js";
|
|
37
37
|
import type { FieldBatch } from "./fieldBatch.js";
|
|
38
|
-
import {
|
|
39
|
-
type EncodedFieldBatch,
|
|
40
|
-
validVersions,
|
|
41
|
-
FieldBatchFormatVersion,
|
|
42
|
-
EncodedFieldBatchV1,
|
|
43
|
-
EncodedFieldBatchV2,
|
|
44
|
-
} from "./format.js";
|
|
38
|
+
import { EncodedFieldBatch, validVersions, FieldBatchFormatVersion } from "./format.js";
|
|
45
39
|
import type { IncrementalEncodingPolicy } from "./incrementalEncodingPolicy.js";
|
|
46
40
|
import { schemaCompressedEncodeV1, schemaCompressedEncodeV2 } from "./schemaBasedEncode.js";
|
|
47
41
|
import { uncompressedEncodeV1, uncompressedEncodeV2 } from "./uncompressedEncode.js";
|
|
@@ -157,18 +151,15 @@ export function makeFieldBatchCodec(options: CodecWriteOptions): FieldBatchCodec
|
|
|
157
151
|
let schemaCompressedEncodeFn:
|
|
158
152
|
| typeof schemaCompressedEncodeV1
|
|
159
153
|
| typeof schemaCompressedEncodeV2;
|
|
160
|
-
let encodedFieldBatchType: typeof EncodedFieldBatchV1 | typeof EncodedFieldBatchV2;
|
|
161
154
|
switch (writeVersion) {
|
|
162
155
|
case unbrand(FieldBatchFormatVersion.v1): {
|
|
163
156
|
uncompressedEncodeFn = uncompressedEncodeV1;
|
|
164
157
|
schemaCompressedEncodeFn = schemaCompressedEncodeV1;
|
|
165
|
-
encodedFieldBatchType = EncodedFieldBatchV1;
|
|
166
158
|
break;
|
|
167
159
|
}
|
|
168
160
|
case unbrand(FieldBatchFormatVersion.v2): {
|
|
169
161
|
uncompressedEncodeFn = uncompressedEncodeV2;
|
|
170
162
|
schemaCompressedEncodeFn = schemaCompressedEncodeV2;
|
|
171
|
-
encodedFieldBatchType = EncodedFieldBatchV2;
|
|
172
163
|
break;
|
|
173
164
|
}
|
|
174
165
|
default: {
|
|
@@ -176,7 +167,10 @@ export function makeFieldBatchCodec(options: CodecWriteOptions): FieldBatchCodec
|
|
|
176
167
|
}
|
|
177
168
|
}
|
|
178
169
|
|
|
179
|
-
|
|
170
|
+
// Both the encode and decode logic here support both v1 and v2, as does `validVersions` and `EncodedFieldBatch`.
|
|
171
|
+
// This makes this use of makeVersionedValidatedCodec atypical as it is a single call being used to make a codec that supports all versions,
|
|
172
|
+
// instead of one call per version, then using another utility to select between them based on version.
|
|
173
|
+
return makeVersionedValidatedCodec(options, validVersions, EncodedFieldBatch, {
|
|
180
174
|
encode: (data: FieldBatch, context: FieldBatchEncodingContext): EncodedFieldBatch => {
|
|
181
175
|
for (const cursor of data) {
|
|
182
176
|
assert(
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getConfigForMinVersionForCollab,
|
|
10
10
|
lowestMinVersionForCollab,
|
|
11
11
|
} from "@fluidframework/runtime-utils/internal";
|
|
12
|
+
import { Type } from "@sinclair/typebox";
|
|
12
13
|
|
|
13
14
|
import {
|
|
14
15
|
type CodecTree,
|
|
@@ -21,12 +22,9 @@ import type { FieldKey, ITreeCursorSynchronous } from "../../core/index.js";
|
|
|
21
22
|
import { brand, type JsonCompatibleReadOnly } from "../../util/index.js";
|
|
22
23
|
import type { FieldBatchCodec, FieldBatchEncodingContext } from "../chunked-forest/index.js";
|
|
23
24
|
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
type Format,
|
|
28
|
-
FormatCommon,
|
|
29
|
-
} from "./formatCommon.js";
|
|
25
|
+
import { ForestFormatVersion, validVersions, type Format } from "./formatCommon.js";
|
|
26
|
+
import { FormatV1 } from "./formatV1.js";
|
|
27
|
+
import { FormatV2 } from "./formatV2.js";
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
30
|
* Uses field cursors
|
|
@@ -61,7 +59,10 @@ export function makeForestSummarizerCodec(
|
|
|
61
59
|
): ForestCodec {
|
|
62
60
|
const inner = fieldBatchCodec;
|
|
63
61
|
const writeVersion = clientVersionToForestFormatVersion(options.minVersionForCollab);
|
|
64
|
-
const formatSchema =
|
|
62
|
+
const formatSchema = Type.Union([FormatV1, FormatV2]);
|
|
63
|
+
// Both the encode and decode logic here support both v1 and v2, as does `validVersions` and `formatSchema`.
|
|
64
|
+
// This makes this use of makeVersionedValidatedCodec atypical as it is a single call being used to make a codec that supports all versions,
|
|
65
|
+
// instead of one call per version, then using another utility to select between them based on version.
|
|
65
66
|
return makeVersionedValidatedCodec(options, validVersions, formatSchema, {
|
|
66
67
|
encode: (data: FieldSet, context: FieldBatchEncodingContext): Format => {
|
|
67
68
|
const keys: FieldKey[] = [];
|
|
@@ -20,8 +20,8 @@ export type ForestFormatVersion = Values<typeof ForestFormatVersion>;
|
|
|
20
20
|
|
|
21
21
|
export const validVersions = new Set([...Object.values(ForestFormatVersion)]);
|
|
22
22
|
|
|
23
|
-
export const FormatCommon = (
|
|
24
|
-
version:
|
|
23
|
+
export const FormatCommon = <const TVersion extends ForestFormatVersion>(
|
|
24
|
+
version: TVersion,
|
|
25
25
|
// Return type is intentionally derived.
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
27
27
|
) =>
|
|
@@ -33,4 +33,6 @@ export const FormatCommon = (
|
|
|
33
33
|
},
|
|
34
34
|
{ additionalProperties: false },
|
|
35
35
|
);
|
|
36
|
-
export type Format = Static<
|
|
36
|
+
export type Format<TVersion extends ForestFormatVersion = ForestFormatVersion> = Static<
|
|
37
|
+
ReturnType<typeof FormatCommon<TVersion>>
|
|
38
|
+
>;
|
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Static } from "@sinclair/typebox";
|
|
7
7
|
|
|
8
|
-
import { brand } from "../../util/index.js";
|
|
9
|
-
|
|
10
8
|
import { FormatCommon, ForestFormatVersion } from "./formatCommon.js";
|
|
11
9
|
|
|
12
|
-
export const FormatV1 = FormatCommon(
|
|
10
|
+
export const FormatV1 = FormatCommon(ForestFormatVersion.v1);
|
|
13
11
|
export type FormatV1 = Static<typeof FormatV1>;
|
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Static } from "@sinclair/typebox";
|
|
7
7
|
|
|
8
|
-
import { brand } from "../../util/index.js";
|
|
9
|
-
|
|
10
8
|
import { FormatCommon, ForestFormatVersion } from "./formatCommon.js";
|
|
11
9
|
|
|
12
|
-
export const FormatV2 = FormatCommon(
|
|
10
|
+
export const FormatV2 = FormatCommon(ForestFormatVersion.v2);
|
|
13
11
|
export type FormatV2 = Static<typeof FormatV2>;
|