Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 51 additions & 21 deletions packages/dds/tree/src/codec/versioned/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export interface CodecVersion<
TDecoded,
TContext,
TFormatVersion extends FormatVersion,
TBuildOptions extends CodecWriteOptions,
TBuildOptions extends ICodecOptions,
> extends CodecVersionBase<
| CodecAndSchema<TDecoded, TContext>
| ((options: TBuildOptions) => CodecAndSchema<TDecoded, TContext>),
Expand All @@ -235,7 +235,7 @@ export interface NormalizedCodecVersion<
TDecoded,
TContext,
TFormatVersion extends FormatVersion,
TBuildOptions extends CodecWriteOptions,
TBuildOptions extends ICodecOptions,
> extends CodecVersionBase<
(
options: TBuildOptions,
Expand Down Expand Up @@ -263,7 +263,7 @@ function normalizeCodecVersion<
TDecoded,
TContext,
TFormatVersion extends FormatVersion,
TBuildOptions extends CodecWriteOptions,
TBuildOptions extends ICodecOptions,
>(
codecVersion: CodecVersion<TDecoded, TContext, TFormatVersion, TBuildOptions>,
): NormalizedCodecVersion<TDecoded, TContext, TFormatVersion, TBuildOptions> {
Expand Down Expand Up @@ -301,7 +301,7 @@ export class ClientVersionDispatchingCodecBuilder<
TDecoded,
TContext,
TFormatVersion extends FormatVersion,
TBuildOptions extends CodecWriteOptions,
TBuildOptions extends ICodecOptions,
> {
public readonly registry: readonly NormalizedCodecVersion<
TDecoded,
Expand Down Expand Up @@ -391,7 +391,7 @@ export class ClientVersionDispatchingCodecBuilder<
/**
* Produce a single codec which can read any supported format, and writes a version selected based on the provided options.
*/
public build(options: TBuildOptions): IJsonCodec<
public build(options: TBuildOptions & CodecWriteOptions): IJsonCodec<
TDecoded,
JsonCompatibleReadOnly,
JsonCompatibleReadOnly,
Expand All @@ -404,31 +404,61 @@ export class ClientVersionDispatchingCodecBuilder<
*/
readonly writeVersion: TFormatVersion;
} {
const applied = this.applyOptions(options);
const [applied, decoder] = this.buildDecoderInternal(options);
const writeVersion = getWriteVersion(this.name, options, applied);
const fromFormatVersion = new Map<
FormatVersion,
EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion>
>(applied.map((codec) => [codec.formatVersion, codec]));
return {
...decoder,
encode: (data: TDecoded, context: TContext): JsonCompatibleReadOnly => {
return writeVersion.codec.encode(data, context);
},
decode: (data: JsonCompatibleReadOnly, context: TContext): TDecoded => {
const versioned = data as Partial<Versioned>;
const codec = fromFormatVersion.get(versioned.version);
if (codec === undefined) {
throw new UsageError(
`Unsupported version ${versioned.version} encountered while decoding ${this.name} data. Supported versions for this data are: ${versionList(applied)}.
The client which encoded this data likely specified an "minVersionForCollab" value which corresponds to a version newer than the version of this client ("${pkgVersion}").`,
);
}
return codec.codec.decode(data, context);
},
writeVersion: writeVersion.formatVersion,
};
}

private buildDecoderInternal(
options: TBuildOptions,
): [
EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion>[],
Pick<
IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext>,
"decode"
>,
] {
const applied = this.applyOptions(options);
const fromFormatVersion = new Map<
FormatVersion,
EvaluatedCodecVersion<TDecoded, TContext, TFormatVersion>
>(applied.map((codec) => [codec.formatVersion, codec]));
return [
applied,
{
decode: (data: JsonCompatibleReadOnly, context: TContext): TDecoded => {
const versioned = data as Partial<Versioned>;
const codec = fromFormatVersion.get(versioned.version);
if (codec === undefined) {
throw new UsageError(
`Unsupported version ${versioned.version} encountered while decoding ${this.name} data. Supported versions for this data are: ${versionList(applied)}.
The client which encoded this data likely specified an "minVersionForCollab" value which corresponds to a version newer than the version of this client ("${pkgVersion}").`,
);
}
return codec.codec.decode(data, context);
},
},
];
}

/**
* Produce a single codec which can read any supported format.
*/
public buildDecoder(
options: TBuildOptions,
): Pick<
IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext>,
"decode"
> {
return this.buildDecoderInternal(options)[1];
}

public getCodecTree(clientVersion: MinimumVersionForCollab): CodecTree {
// TODO: add support for children codecs.
const selected = getWriteVersionNoOverrides(this.registry, clientVersion);
Expand Down
19 changes: 4 additions & 15 deletions packages/dds/tree/src/shared-tree/independentView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,20 @@ import {
createIdCompressor,
} from "@fluidframework/id-compressor/internal";

import {
FluidClientVersion,
type CodecWriteOptions,
type ICodecOptions,
} from "../codec/index.js";
import type { CodecWriteOptions, ICodecOptions } from "../codec/index.js";
import {
type RevisionTag,
RevisionTagCodec,
SchemaFormatVersion,
TreeStoredSchemaRepository,
} from "../core/index.js";
import {
createNodeIdentifierManager,
fieldBatchCodecBuilder,
makeSchemaCodec,
type FieldBatchEncodingContext,
defaultSchemaPolicy,
TreeCompressionStrategy,
defaultIncrementalEncodingPolicy,
schemaCodecBuilder,
} from "../feature-libraries/index.js";
import { combineChunks } from "../feature-libraries/index.js";
// eslint-disable-next-line import-x/no-internal-modules
Expand Down Expand Up @@ -236,14 +231,8 @@ export function createIndependentTreeAlpha<const TSchema extends ImplicitFieldSc
});

if (options?.content !== undefined) {
// Any version can be passed down to `makeSchemaCodec` and `fieldBatchCodecBuilder.build` here.
// We only use the decode part, which always dispatches to the correct codec based on the version in the data, not `minVersionForCollab`.
const writeOptions: CodecWriteOptions = {
...options,
minVersionForCollab: FluidClientVersion.v2_0,
};
const schemaCodec = makeSchemaCodec(writeOptions, SchemaFormatVersion.v1);
const fieldBatchCodec = fieldBatchCodecBuilder.build(writeOptions);
const schemaCodec = schemaCodecBuilder.buildDecoder(options);
const fieldBatchCodec = fieldBatchCodecBuilder.buildDecoder(options);
const newSchema = schemaCodec.decode(options.content.schema as Format);

const context: FieldBatchEncodingContext = {
Expand Down
7 changes: 1 addition & 6 deletions packages/dds/tree/src/shared-tree/sharedTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,7 @@ export function persistedToSimpleSchema(
persisted: JsonCompatible,
options: ICodecOptions,
): SimpleTreeSchema {
// Any version can be passed down to makeSchemaCodec here.
// We only use the decode part, which always dispatches to the correct codec based on the version in the data, not the version passed to `makeSchemaCodec`.
const schemaCodec = makeSchemaCodec({
...options,
minVersionForCollab: FluidClientVersion.v2_0,
});
const schemaCodec = schemaCodecBuilder.buildDecoder(options);
const stored = schemaCodec.decode(persisted as FormatV1);
return exportSimpleSchema(stored);
}
Expand Down
16 changes: 3 additions & 13 deletions packages/dds/tree/src/simple-tree/api/storedSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@

import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";

import {
FluidClientVersion,
FormatValidatorNoOp,
type ICodecOptions,
} from "../../codec/index.js";
import { SchemaFormatVersion } from "../../core/index.js";
import { makeSchemaCodec } from "../../feature-libraries/index.js";
import { FormatValidatorNoOp, type ICodecOptions } from "../../codec/index.js";
import { makeSchemaCodec, schemaCodecBuilder } from "../../feature-libraries/index.js";
import type {
FormatV1,
// eslint-disable-next-line import-x/no-internal-modules
Expand Down Expand Up @@ -102,12 +97,7 @@ export function comparePersistedSchema(
view: ImplicitFieldSchema,
options: ICodecOptions,
): Omit<SchemaCompatibilityStatus, "canInitialize"> {
// Any version can be passed down to makeSchemaCodec here.
// We only use the decode part, which always dispatches to the correct codec based on the version in the data, not the version passed to `makeSchemaCodec`.
const schemaCodec = makeSchemaCodec(
{ ...options, minVersionForCollab: FluidClientVersion.v2_0 },
SchemaFormatVersion.v1,
);
const schemaCodec = schemaCodecBuilder.buildDecoder(options);
const stored = schemaCodec.decode(persisted as FormatV1);
const config = new TreeViewConfigurationAlpha({
schema: normalizeFieldSchema(view),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ describe("schemaSummarizer", () => {
for (const schemaFormat of schemaCodecBuilder.registry) {
const encode = (schema: TreeStoredSchema): JsonCompatibleReadOnly => {
assert(schemaFormat.minVersionForCollab !== undefined);
const codec = schemaFormat.codec({
jsonValidator: FormatValidatorBasic,
minVersionForCollab: schemaFormat.minVersionForCollab,
});
const codec = schemaFormat.codec({ jsonValidator: FormatValidatorBasic });
const result: JsonCompatibleReadOnly = codec.encode(schema);
return result;
};
Expand Down
5 changes: 1 addition & 4 deletions packages/dds/tree/src/test/snapshots/schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ describe("schema snapshots", () => {
it(`${name} - schema v${schemaFormat.formatVersion}`, () => {
assert(schemaFormat.minVersionForCollab !== undefined);
const encoded = schemaFormat
.codec({
jsonValidator: FormatValidatorBasic,
minVersionForCollab: schemaFormat.minVersionForCollab,
})
.codec({ jsonValidator: FormatValidatorBasic })
.encode(schemaData);
takeJsonSnapshot(encoded);
});
Expand Down
Loading