From 9741b43dd9930780eda94330b00419bec7cc2423 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 22 Apr 2025 14:29:01 -0400 Subject: [PATCH 01/22] rename compatVersion, add optional param --- .../api-report/aqueduct.legacy.alpha.api.md | 1 + .../baseContainerRuntimeFactory.ts | 5 ++ .../src/compatibilityConfiguration.ts | 41 ++++---------- .../fluid-static/src/rootDataObject.ts | 14 ++++- .../container-runtime.legacy.alpha.api.md | 4 ++ .../container-runtime/src/compatUtils.ts | 53 ++++++++++--------- .../container-runtime/src/containerRuntime.ts | 39 ++++++++++---- .../runtime/container-runtime/src/index.ts | 1 + .../src/test/compatUtils.spec.ts | 34 ++++++------ .../src/test/containerRuntime.spec.ts | 42 +++++++-------- 10 files changed, 128 insertions(+), 106 deletions(-) diff --git a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md index 647eb2ff7194..1605d3b081ec 100644 --- a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md +++ b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md @@ -19,6 +19,7 @@ export class BaseContainerRuntimeFactory extends RuntimeFactoryHelper implements export interface BaseContainerRuntimeFactoryProps { // @deprecated (undocumented) dependencyContainer?: IFluidDependencySynthesizer; + minVersionForCollab?: SemanticVersion; provideEntryPoint: (runtime: IContainerRuntime) => Promise; registryEntries: NamedFluidDataStoreRegistryEntries; // @deprecated diff --git a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts index dd3b02138674..84570270e603 100644 --- a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts +++ b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts @@ -11,6 +11,7 @@ import { FluidDataStoreRegistry, loadContainerRuntime, type IContainerRuntimeOptions, + type SemanticVersion, } from "@fluidframework/container-runtime/internal"; import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal"; import type { FluidObject } from "@fluidframework/core-interfaces"; @@ -61,6 +62,10 @@ export interface BaseContainerRuntimeFactoryProps { * created with this factory */ provideEntryPoint: (runtime: IContainerRuntime) => Promise; + /** + * The minVersionForCollab passed to the ContainerRuntime when instantiating it + */ + minVersionForCollab?: SemanticVersion; } /** diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index 1808e66b980c..726707907684 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -3,48 +3,27 @@ * Licensed under the MIT License. */ -import { - CompressionAlgorithms, - type IContainerRuntimeOptionsInternal, -} from "@fluidframework/container-runtime/internal"; -import { FlushMode } from "@fluidframework/runtime-definitions/internal"; +import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal"; import type { CompatibilityMode } from "./types.js"; /** - * Specifies the configured runtime options for each {@link CompatibilityMode}.. + * The CompatibilityMode selected determines the set of runtime options to use. In "1" mode we support + * full interop with true 1.x clients, while in "2" mode we only support interop with 2.x clients. + * + * @remarks In general, we can use the `compatibilityMode` property of `LoadContainerRuntimeParams` to apply + * the proper configurations. However, there are some options that we need to explicity set that differ + * from the default values (i.e. `enableRuntimeIdCompressor` below). */ export const compatibilityModeRuntimeOptions: Record< CompatibilityMode, IContainerRuntimeOptionsInternal > = { - "1": { - // 1.x clients are compatible with TurnBased flushing, but here we elect to remain on Immediate flush mode - // as a work-around for inability to send batches larger than 1Mb. Immediate flushing keeps batches smaller as - // fewer messages will be included per flush. - flushMode: FlushMode.Immediate, - // Op compression is on by default but introduces a new type of op which is not compatible with 1.x clients. - compressionOptions: { - minimumBatchSizeInBytes: Number.POSITIVE_INFINITY, // disabled - compressionAlgorithm: CompressionAlgorithms.lz4, - }, - // Grouped batching is on by default but introduces a new type of op which is not compatible with 1.x clients. - enableGroupedBatching: false, - // TODO: Include explicit disables for things that are currently off-by-default? - - // Explicitly disable running Sweep in compat mode "1". Sweep is supported only in 2.x. So, when 1.x and 2.x - // clients are running in parallel, running sweep will fail 1.x clients. - gcOptions: { enableGCSweep: undefined }, - }, + "1": {}, "2": { - // Explicit schema control explicitly makes the container incompatible with 1.x clients, to force their - // ejection from collaboration and prevent container corruption. It is off by default and must be explicitly enabled. - explicitSchemaControl: true, // The runtime ID compressor is a prerequisite to use SharedTree but is off by default and must be explicitly enabled. - // It introduces a new type of op which is not compatible with 1.x clients. + // In general, we don't want to enable this by default since it increases the bundle size. However, since SharedTree + // is bundled with the fluid-framework package, we need to enable it here to support SharedTree. enableRuntimeIdCompressor: "on", - // Explicitly disable running Sweep in compat mode "2". Although sweep is supported in 2.x, it is disabled by default. - // This setting explicitly disables it to be extra safe. - gcOptions: { enableGCSweep: undefined }, }, }; diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 9dcd0614160c..a857e791c86d 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -10,7 +10,10 @@ import { DataObjectFactory, } from "@fluidframework/aqueduct/internal"; import type { IRuntimeFactory } from "@fluidframework/container-definitions/internal"; -import { FluidDataStoreRegistry } from "@fluidframework/container-runtime/internal"; +import { + FluidDataStoreRegistry, + type SemanticVersion, +} from "@fluidframework/container-runtime/internal"; import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal"; import type { FluidObject, IFluidLoadable } from "@fluidframework/core-interfaces"; import type { IChannelFactory } from "@fluidframework/datastore-definitions/internal"; @@ -36,6 +39,14 @@ import { parseDataObjectsFromSharedObjects, } from "./utils.js"; +/** + * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. + */ +const compatModeTominVersionForCollab: Record = { + "1": "1.0.0", // TODO: Should this be the LTS version? + "2": "2.0.0", +}; + /** * Input props for {@link RootDataObject.initializingFirstTime}. */ @@ -242,6 +253,7 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { registryEntries: [rootDataObjectFactory.registryEntry], runtimeOptions: compatibilityModeRuntimeOptions[compatibilityMode], provideEntryPoint, + minVersionForCollab: compatModeTominVersionForCollab[compatibilityMode], }); this.rootDataObjectFactory = rootDataObjectFactory; this.initialObjects = schema.initialObjects; diff --git a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md index d79734e3584f..7e521a6479e0 100644 --- a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md +++ b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md @@ -334,6 +334,7 @@ export interface LoadContainerRuntimeParams { containerScope?: FluidObject; context: IContainerContext; existing: boolean; + minVersionForCollab?: SemanticVersion; provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise; registryEntries: NamedFluidDataStoreRegistryEntries; // @deprecated @@ -353,6 +354,9 @@ export type OpActionEventName = MessageType.Summarize | MessageType.SummaryAck | // @alpha @deprecated export type ReadFluidDataStoreAttributes = IFluidDataStoreAttributes0 | IFluidDataStoreAttributes1 | IFluidDataStoreAttributes2; +// @alpha +export type SemanticVersion = `${bigint}.${bigint}.${bigint}` | `${bigint}.${bigint}.${bigint}-${string}`; + // @alpha export interface SubmitSummaryFailureData { // (undocumented) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index e1577e86ab59..3c7764dc316f 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -33,12 +33,12 @@ import type { IdCompressorMode } from "./summary/index.js"; * Importantly though, N/N-2 compatibility is still guaranteed with the proper * configurations set. * - * Further to distinguish unspecified `compatibilityVersion` from a specified + * Further to distinguish unspecified `minVersionForCollab` from a specified * version and allow `enableExplicitSchemaControl` to default to `true` for * any 2.0.0+ version, we will use a special value of `2.0.0-defaults`, which * is semantically less than 2.0.0. */ -export const defaultCompatibilityVersion = "2.0.0-defaults" as const; +export const defaultminVersionForCollab = "2.0.0-defaults" as const; /** * String in a valid semver format specifying bottom of a minor version @@ -50,6 +50,9 @@ export type MinimumMinorSemanticVersion = `${bigint}.${bigint}.0` | `${bigint}.0 /** * String in a valid semver format of a specific version at least specifying minor. + * + * @legacy + * @alpha */ export type SemanticVersion = | `${bigint}.${bigint}.${bigint}` @@ -98,8 +101,8 @@ export type RuntimeOptionsAffectingDocSchema = Required< * the format changes introduced by the property, then the default value for that SemanticVersion will enable the feature associated with the property. * Otherwise, the feature will be disabled. * - * For example if the compatibilityVersion is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x - * clients do not understand the document format when batching is enabled. If the compatibilityVersion is a 2.x client (i.e. "2.0.0" or later), then the + * For example if the minVersionForCollab is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x + * clients do not understand the document format when batching is enabled. If the minVersionForCollab is a 2.x client (i.e. "2.0.0" or later), then the * default value for `enableGroupedBatching` will be true because clients running 2.0 or later will be able to understand the format changes associated * with the batching feature. */ @@ -127,14 +130,14 @@ const runtimeOptionsAffectingDocSchemaConfigMap = { explicitSchemaControl: { "1.0.0": false, // This option's intention is to prevent 1.x clients from joining sessions - // when enabled. This is set to true when the compatibility version is set + // when enabled. This is set to true when the minVersionForCollab is set // to >=2.0.0 (explicitly). This is different than other 2.0 defaults // because it was not enabled by default prior to the implementation of - // `compatibilityVersion`. - // `defaultCompatibilityVersion` is set to "2.0.0-defaults" which "2.0.0" + // `minVersionForCollab`. + // `defaultminVersionForCollab` is set to "2.0.0-defaults" which "2.0.0" // does not satisfy to avoiding enabling this option by default as of - // `compatibilityVersion` introduction, which could be unexpected. - // Only enable as a default when `compatibilityVersion` is specified at + // `minVersionForCollab` introduction, which could be unexpected. + // Only enable as a default when `minVersionForCollab` is specified at // 2.0.0+. "2.0.0": true, } as const, @@ -147,19 +150,19 @@ const runtimeOptionsAffectingDocSchemaConfigMap = { } as const, gcOptions: { "1.0.0": {}, - // Although sweep is supported in 2.x, it is disabled by default until compatibilityVersion>=3.0.0 to be extra safe. + // Although sweep is supported in 2.x, it is disabled by default until minVersionForCollab>=3.0.0 to be extra safe. "3.0.0": { enableGCSweep: true }, } as const, } as const satisfies ConfigMap; /** - * Returns the default RuntimeOptionsAffectingDocSchema configuration for a given compatibility version. + * Returns the default RuntimeOptionsAffectingDocSchema configuration for a given minVersionForCollab. */ -export function getCompatibilityVersionDefaults( - compatibilityVersion: SemanticVersion, +export function getminVersionForCollabDefaults( + minVersionForCollab: SemanticVersion, ): RuntimeOptionsAffectingDocSchema { return getConfigsForCompatMode( - compatibilityVersion, + minVersionForCollab, runtimeOptionsAffectingDocSchemaConfigMap, // This is a bad cast away from Partial that getConfigsForCompatMode provides. // ConfigMap should be restructured to provide RuntimeOptionsAffectingDocSchema guarantee. @@ -167,10 +170,10 @@ export function getCompatibilityVersionDefaults( } /** - * Returns a default configuration given compatibility version and configuration version map. + * Returns a default configuration given minVersionForCollab and configuration version map. */ export function getConfigsForCompatMode>( - compatibilityVersion: SemanticVersion, + minVersionForCollab: SemanticVersion, configMap: ConfigMap, ): Partial { const defaultConfigs: Partial = {}; @@ -179,11 +182,11 @@ export function getConfigsForCompatMode (semverGte(b, a) ? -1 : 1)); - // For each config, we iterate over the keys and check if compatibilityVersion is greater than or equal to the version. + // For each config, we iterate over the keys and check if minVersionForCollab is greater than or equal to the version. // If so, we set it as the default value for the option. At the end of the loop we should have the most recent default - // value that is compatible with the version specified as the compatibilityVersion. + // value that is compatible with the version specified as the minVersionForCollab. for (const version of versions) { - if (semverGte(compatibilityVersion, version)) { + if (semverGte(minVersionForCollab, version)) { defaultConfigs[key] = config[version as MinimumMinorSemanticVersion]; } else { // If the compatibility mode is less than the version, we break out of the loop since we don't need to check @@ -196,13 +199,13 @@ export function getConfigsForCompatMode Promise; + + /** + * minVersionForCollab is used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} + * properties which affect the document schema. minVersionForCollab can be considered to be the minimum version of + * the FF runtime that we should support compatibility with. + * + * For example, let's say that feature `foo` was added in 2.0 which introduces a new op type. Additionally, option `bar` + * was added to `IContainerRuntimeOptionsInternal` in 2.0 to enable/disable `foo` since clients prior to 2.0 would not + * understand the new op type. If a customer were to set minVersionForCollab to 2.0.0, then `bar` would be set to + * enable `foo` by default. If a customer were to set minVersionForCollab to 1.0.0, then `bar` would be set to + * disable `foo` by default. + * + * minVersionForCollab accepts a string that must be in valid semver format. It must include the minor and patch indicators as well (i.e. 1.0 + * is not acceptable, but 1.0.0 is). For example, use "2.0.0" to set the default configuration for clients running at least the 2.0.0 version + * of the FF runtime. + */ + minVersionForCollab?: SemanticVersion; } /** * This is meant to be used by a {@link @fluidframework/container-definitions#IRuntimeFactory} to instantiate a container runtime. @@ -728,6 +746,7 @@ export class ContainerRuntime */ requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise; provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise; + minVersionForCollab?: SemanticVersion; }): Promise { const { context, @@ -759,19 +778,17 @@ export class ContainerRuntime const mc = loggerToMonitoringContext(logger); // Some options require a minimum version of the FF runtime to operate, so the default configs will be generated - // based on the compatibility mode. - // For example, if compatibility mode is set to "1.0.0", the default configs will ensure compatibility with FF runtime - // 1.0.0 or later. If the compatibility mode is set to "2.10.0", the default values will be generated to ensure compatibility + // based on the minVersionForCollab. + // For example, if minVersionForCollab is set to "1.0.0", the default configs will ensure compatibility with FF runtime + // 1.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility // with FF runtime 2.10.0 or later. - // TODO: We will add in a way for users to pass in compatibilityVersion in a follow up PR. - const compatibilityVersion = defaultCompatibilityVersion; - if (!isValidCompatVersion(compatibilityVersion)) { + const minVersionForCollab = params.minVersionForCollab ?? defaultminVersionForCollab; + if (!isValidCompatVersion(minVersionForCollab)) { throw new UsageError( - `Invalid compatibility version: ${compatibilityVersion}. It must be an existing FF version (i.e. 2.22.1).`, + `Invalid minVersionForCollab: ${minVersionForCollab}. It must be an existing FF version (i.e. 2.22.1).`, ); } - const defaultVersionDependentConfigs = - getCompatibilityVersionDefaults(compatibilityVersion); + const defaultVersionDependentConfigs = getminVersionForCollabDefaults(minVersionForCollab); // The following are the default values for the options that do not affect the DocumentSchema. const defaultConfigsNonVersionDependent: Required< diff --git a/packages/runtime/container-runtime/src/index.ts b/packages/runtime/container-runtime/src/index.ts index f30754b33657..26b93f0a3013 100644 --- a/packages/runtime/container-runtime/src/index.ts +++ b/packages/runtime/container-runtime/src/index.ts @@ -29,6 +29,7 @@ export { ChannelCollectionFactory, AllowTombstoneRequestHeaderKey, } from "./channelCollection.js"; +export { SemanticVersion } from "./compatUtils.js"; export { GCNodeType, IGCMetadata, diff --git a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts index 9c7fc318ae93..809e54387a91 100644 --- a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts +++ b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts @@ -62,11 +62,11 @@ describe("compatUtils", () => { }; const testCases: { - compatibilityVersion: SemanticVersion; + minVersionForCollab: SemanticVersion; expectedConfig: Partial; }[] = [ { - compatibilityVersion: "0.5.0", + minVersionForCollab: "0.5.0", expectedConfig: { featureA: "a1", featureB: "b1", @@ -77,7 +77,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "1.0.0", + minVersionForCollab: "1.0.0", expectedConfig: { featureA: "a1", featureB: "b1", @@ -88,7 +88,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "1.5.0", + minVersionForCollab: "1.5.0", expectedConfig: { featureA: "a1", featureB: "b1", @@ -99,7 +99,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "2.0.0", + minVersionForCollab: "2.0.0", expectedConfig: { featureA: "a2", featureB: "b1", @@ -110,7 +110,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "2.1.5", + minVersionForCollab: "2.1.5", expectedConfig: { featureA: "a2", featureB: "b1", @@ -121,7 +121,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "2.5.0", + minVersionForCollab: "2.5.0", expectedConfig: { featureA: "a2", featureB: "b1", @@ -132,7 +132,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "3.0.0", + minVersionForCollab: "3.0.0", expectedConfig: { featureA: "a2", featureB: "b2", @@ -143,7 +143,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "3.7.2", + minVersionForCollab: "3.7.2", expectedConfig: { featureA: "a2", featureB: "b2", @@ -154,7 +154,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "5.0.1", + minVersionForCollab: "5.0.1", expectedConfig: { featureA: "a3", featureB: "b2", @@ -165,7 +165,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "6.9.9", + minVersionForCollab: "6.9.9", expectedConfig: { featureA: "a3", featureB: "b3", @@ -176,7 +176,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "8.2.3", + minVersionForCollab: "8.2.3", expectedConfig: { featureA: "a4", featureB: "b3", @@ -187,7 +187,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "9.7.0", + minVersionForCollab: "9.7.0", expectedConfig: { featureA: "a4", featureB: "b4", @@ -198,7 +198,7 @@ describe("compatUtils", () => { }, }, { - compatibilityVersion: "10.0.0", + minVersionForCollab: "10.0.0", expectedConfig: { featureA: "a4", featureB: "b4", @@ -211,12 +211,12 @@ describe("compatUtils", () => { ]; for (const testCase of testCases) { - it(`returns correct configs for compatibilityVersion = "${testCase.compatibilityVersion}"`, () => { - const config = getConfigsForCompatMode(testCase.compatibilityVersion, testConfigMap); + it(`returns correct configs for minVersionForCollab = "${testCase.minVersionForCollab}"`, () => { + const config = getConfigsForCompatMode(testCase.minVersionForCollab, testConfigMap); assert.deepEqual( config, testCase.expectedConfig, - `Failed for compatibilityVersion: ${testCase.compatibilityVersion}`, + `Failed for minVersionForCollab: ${testCase.minVersionForCollab}`, ); }); } diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 769ecb845d79..7ca6bd3c953a 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -73,7 +73,6 @@ import { import { SinonFakeTimers, createSandbox, useFakeTimers } from "sinon"; import { ChannelCollection } from "../channelCollection.js"; -import { getCompatibilityVersionDefaults } from "../compatUtils.js"; import { CompressionAlgorithms } from "../compressionDefinitions.js"; import { ContainerRuntime, @@ -3626,9 +3625,9 @@ describe("Runtime", () => { }); }); - // TODO: Update these tests when compatibilityVersion API is implemented - ADO:36088 + // TODO: Update these tests when minVersionForCollab API is implemented - ADO:36088 describe("Default Configurations", () => { - it("compatibilityVersion not provided", async () => { + it("minVersionForCollab not provided", async () => { const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, @@ -3663,16 +3662,16 @@ describe("Runtime", () => { ]); }); - it("compatibilityVersion = 1.0.0", async () => { - const compatibilityVersion = "1.0.0"; - const defaultRuntimeOptions = getCompatibilityVersionDefaults(compatibilityVersion); + it("minVersionForCollab = 1.0.0", async () => { + const minVersionForCollab = "1.0.0"; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, registryEntries: [], existing: false, - runtimeOptions: defaultRuntimeOptions, + runtimeOptions: {}, provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab, }); const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { @@ -3700,16 +3699,16 @@ describe("Runtime", () => { ]); }); - it('compatibilityVersion = 2.0.0-defaults ("default")', async () => { - const compatibilityVersion = "2.0.0-defaults"; - const defaultRuntimeOptions = getCompatibilityVersionDefaults(compatibilityVersion); + it('minVersionForCollab = 2.0.0-defaults ("default")', async () => { + const minVersionForCollab = "2.0.0-defaults"; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, registryEntries: [], existing: false, - runtimeOptions: defaultRuntimeOptions, + runtimeOptions: {}, provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab, }); const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { @@ -3737,16 +3736,16 @@ describe("Runtime", () => { ]); }); - it("compatibilityVersion = 2.0.0 (explicit)", async () => { - const compatibilityVersion = "2.0.0"; - const defaultRuntimeOptions = getCompatibilityVersionDefaults(compatibilityVersion); + it("minVersionForCollab = 2.0.0 (explicit)", async () => { + const minVersionForCollab = "2.0.0"; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, registryEntries: [], existing: false, - runtimeOptions: defaultRuntimeOptions, + runtimeOptions: {}, provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab, }); const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { @@ -3774,16 +3773,15 @@ describe("Runtime", () => { ]); }); - it("compatibilityVersion = 2.20.0", async () => { - const compatibilityVersion = "2.20.0"; - const defaultRuntimeOptions = getCompatibilityVersionDefaults(compatibilityVersion); + it("minVersionForCollab = 2.20.0", async () => { + const minVersionForCollab = "2.20.0"; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, registryEntries: [], existing: false, - runtimeOptions: defaultRuntimeOptions, provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab, }); const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { @@ -3811,7 +3809,7 @@ describe("Runtime", () => { ]); }); - it("compatibilityVersion not provided, with manual configs for each property", async () => { + it("minVersionForCollab not provided, with manual configs for each property", async () => { const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, @@ -3914,7 +3912,8 @@ describe("Runtime", () => { }); // Skipped since 3.0.0 is not an existing FF version yet - it.skip("compatibilityVersion = 3.0.0", async () => { + it.skip("minVersionForCollab = 3.0.0", async () => { + const minVersionForCollab = "3.0.0"; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, @@ -3922,6 +3921,7 @@ describe("Runtime", () => { existing: false, runtimeOptions: {}, provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab, }); const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { From 0a00f9a8e614015bd3ca7e676ef72f00395fc139 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 09:43:26 -0400 Subject: [PATCH 02/22] Update packages/runtime/container-runtime/src/containerRuntime.ts Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> --- packages/runtime/container-runtime/src/containerRuntime.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index 51a28ad318ea..e45f779341fb 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -674,6 +674,7 @@ export interface LoadContainerRuntimeParams { * properties which affect the document schema. minVersionForCollab can be considered to be the minimum version of * the FF runtime that we should support compatibility with. * + * @example * For example, let's say that feature `foo` was added in 2.0 which introduces a new op type. Additionally, option `bar` * was added to `IContainerRuntimeOptionsInternal` in 2.0 to enable/disable `foo` since clients prior to 2.0 would not * understand the new op type. If a customer were to set minVersionForCollab to 2.0.0, then `bar` would be set to From 90cf8c5defec1374ae5b6a686c2213e1b0742654 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 09:46:59 -0400 Subject: [PATCH 03/22] PR feedback --- packages/runtime/container-runtime/src/compatUtils.ts | 6 ++++++ .../container-runtime/src/test/containerRuntime.spec.ts | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index c1830bd5682c..b992ca7011cc 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -39,6 +39,11 @@ import { pkgVersion } from "./packageVersion.js"; */ export const defaultminVersionForCollab = "2.0.0-defaults" as const; +/** + * We don't want to allow anyone to use a version less than 1.0.0 for minVersionForCollab. + */ +const lowestMinVersionForCollab = "1.0.0" as const; + /** * String in a valid semver format specifying bottom of a minor version * or special "defaults" prerelease of a major. @@ -203,6 +208,7 @@ export function isValidCompatVersion(minVersionForCollab: SemanticVersion): bool return ( minVersionForCollab !== undefined && semverValid(minVersionForCollab) !== null && + semverGte(minVersionForCollab, lowestMinVersionForCollab) && semverLte(minVersionForCollab, pkgVersion) ); } diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 7ca6bd3c953a..cecb911a86a0 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -3625,7 +3625,6 @@ describe("Runtime", () => { }); }); - // TODO: Update these tests when minVersionForCollab API is implemented - ADO:36088 describe("Default Configurations", () => { it("minVersionForCollab not provided", async () => { const logger = new MockLogger(); From b3fc9b30f592cd9ae1a74bbf177983859d62ec96 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 09:47:12 -0400 Subject: [PATCH 04/22] Update packages/runtime/container-runtime/src/containerRuntime.ts Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> --- packages/runtime/container-runtime/src/containerRuntime.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index f9581442f9f5..2212c645eccc 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -694,8 +694,8 @@ export interface LoadContainerRuntimeParams { requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise; /** - * minVersionForCollab is used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} - * properties which affect the document schema. minVersionForCollab can be considered to be the minimum version of + * Used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} + * properties, which affect the document schema. It can be considered to be the minimum version of * the FF runtime that we should support compatibility with. * * @example From 84b1168b54974e52937d812b0482e7ce3a953f5a Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 09:53:26 -0400 Subject: [PATCH 05/22] rename isValidMinVersionForCollab --- packages/runtime/container-runtime/src/compatUtils.ts | 2 +- packages/runtime/container-runtime/src/containerRuntime.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index b992ca7011cc..329ba2d4f90e 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -204,7 +204,7 @@ export function getConfigsForCompatMode Date: Thu, 24 Apr 2025 11:16:08 -0400 Subject: [PATCH 06/22] update BaseContainerRuntimeFactory --- .../baseContainerRuntimeFactory.ts | 3 +++ packages/framework/fluid-static/src/rootDataObject.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts index 84570270e603..5571ed157c28 100644 --- a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts +++ b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts @@ -93,6 +93,7 @@ export class BaseContainerRuntimeFactory // eslint-disable-next-line import/no-deprecated private readonly requestHandlers: RuntimeRequestHandler[]; private readonly provideEntryPoint: (runtime: IContainerRuntime) => Promise; + private readonly minVersionForCollab?: SemanticVersion; public constructor(props: BaseContainerRuntimeFactoryProps) { super(); @@ -103,6 +104,7 @@ export class BaseContainerRuntimeFactory this.provideEntryPoint = props.provideEntryPoint; this.requestHandlers = props.requestHandlers ?? []; this.registry = new FluidDataStoreRegistry(this.registryEntries); + this.minVersionForCollab = props.minVersionForCollab; } /** @@ -151,6 +153,7 @@ export class BaseContainerRuntimeFactory // eslint-disable-next-line import/no-deprecated requestHandler: buildRuntimeRequestHandler(...this.requestHandlers), provideEntryPoint: this.provideEntryPoint, + minVersionForCollab: this.minVersionForCollab, }); } diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index a857e791c86d..8ecad3c89944 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -42,7 +42,7 @@ import { /** * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. */ -const compatModeTominVersionForCollab: Record = { +const compatibilityModeToMinVersionForCollab: Record = { "1": "1.0.0", // TODO: Should this be the LTS version? "2": "2.0.0", }; @@ -253,7 +253,7 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { registryEntries: [rootDataObjectFactory.registryEntry], runtimeOptions: compatibilityModeRuntimeOptions[compatibilityMode], provideEntryPoint, - minVersionForCollab: compatModeTominVersionForCollab[compatibilityMode], + minVersionForCollab: compatibilityModeToMinVersionForCollab[compatibilityMode], }); this.rootDataObjectFactory = rootDataObjectFactory; this.initialObjects = schema.initialObjects; From 41b7e4fe6c3280054b93580ec6631aeb7d46d83e Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 14:43:07 -0400 Subject: [PATCH 07/22] update comments --- packages/runtime/container-runtime/src/compatUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 84fc33a93183..cb8e8f0cb871 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -22,7 +22,7 @@ import { pkgVersion } from "./packageVersion.js"; /** * Our policy is to support N/N-1 compatibility by default, where N is the most * recent public major release of the runtime. - * Therefore, if the customer does not provide a compatibility mode, we will + * Therefore, if the customer does not provide a minVersionForCollab, we will * default to use N-1. * * However, this is not consistent with today's behavior. Some options (i.e. @@ -197,7 +197,7 @@ export function getConfigsForCompatMode Date: Thu, 24 Apr 2025 14:47:15 -0400 Subject: [PATCH 08/22] fix defaultMinVersionForCollab name --- packages/runtime/container-runtime/src/compatUtils.ts | 4 ++-- packages/runtime/container-runtime/src/containerRuntime.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index cb8e8f0cb871..90aae3457f0d 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -37,7 +37,7 @@ import { pkgVersion } from "./packageVersion.js"; * any 2.0.0+ version, we will use a special value of `2.0.0-defaults`, which * is semantically less than 2.0.0. */ -export const defaultminVersionForCollab = "2.0.0-defaults" as const; +export const defaultMinVersionForCollab = "2.0.0-defaults" as const; /** * We don't want to allow anyone to use a version less than 1.0.0 for minVersionForCollab. @@ -136,7 +136,7 @@ const runtimeOptionsAffectingDocSchemaConfigMap = { // to >=2.0.0 (explicitly). This is different than other 2.0 defaults // because it was not enabled by default prior to the implementation of // `minVersionForCollab`. - // `defaultminVersionForCollab` is set to "2.0.0-defaults" which "2.0.0" + // `defaultMinVersionForCollab` is set to "2.0.0-defaults" which "2.0.0" // does not satisfy to avoiding enabling this option by default as of // `minVersionForCollab` introduction, which could be unexpected. // Only enable as a default when `minVersionForCollab` is specified at diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index c61953e70836..7490d692e1ca 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -154,7 +154,7 @@ import { wrapContext, } from "./channelCollection.js"; import { - defaultminVersionForCollab, + defaultMinVersionForCollab, getminVersionForCollabDefaults, isValidMinVersionForCollab, type RuntimeOptionsAffectingDocSchema, @@ -814,7 +814,7 @@ export class ContainerRuntime // For example, if minVersionForCollab is set to "1.0.0", the default configs will ensure compatibility with FF runtime // 1.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility // with FF runtime 2.10.0 or later. - const minVersionForCollab = params.minVersionForCollab ?? defaultminVersionForCollab; + const minVersionForCollab = params.minVersionForCollab ?? defaultMinVersionForCollab; if (!isValidMinVersionForCollab(minVersionForCollab)) { throw new UsageError( `Invalid minVersionForCollab: ${minVersionForCollab}. It must be an existing FF version (i.e. 2.22.1).`, From 8323a4ac1f8107352c21fa1d5385ceb42a88f480 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 24 Apr 2025 15:18:00 -0400 Subject: [PATCH 09/22] add comment --- packages/runtime/container-runtime/src/compatUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 90aae3457f0d..bd8053442979 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -208,12 +208,13 @@ export function getConfigsForCompatMode Date: Tue, 29 Apr 2025 10:29:58 -0400 Subject: [PATCH 10/22] pr feedback --- .../api-report/aqueduct.legacy.alpha.api.md | 2 +- .../baseContainerRuntimeFactory.ts | 7 +-- .../container-runtime/src/containerRuntime.ts | 17 ++++--- .../runtime/container-runtime/src/index.ts | 2 +- .../azure-client/src/test/AzureClient.spec.ts | 51 +++++++++++++++++++ 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md index 72895826cad5..e0fa93ef2a9a 100644 --- a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md +++ b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md @@ -19,7 +19,7 @@ export class BaseContainerRuntimeFactory extends RuntimeFactoryHelper implements export interface BaseContainerRuntimeFactoryProps { // @deprecated (undocumented) dependencyContainer?: IFluidDependencySynthesizer; - minVersionForCollab?: SemanticVersion; + minVersionForCollab?: SemanticVersion | undefined; provideEntryPoint: (runtime: IContainerRuntime) => Promise; registryEntries: NamedFluidDataStoreRegistryEntries; // @deprecated diff --git a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts index 5571ed157c28..c989e5dc6798 100644 --- a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts +++ b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts @@ -63,9 +63,10 @@ export interface BaseContainerRuntimeFactoryProps { */ provideEntryPoint: (runtime: IContainerRuntime) => Promise; /** - * The minVersionForCollab passed to the ContainerRuntime when instantiating it + * The minVersionForCollab passed to the ContainerRuntime when instantiating it. + * See {@link @fluidframework/container-runtime#LoadContainerRuntimeParams} for more details on this property. */ - minVersionForCollab?: SemanticVersion; + minVersionForCollab?: SemanticVersion | undefined; } /** @@ -93,7 +94,7 @@ export class BaseContainerRuntimeFactory // eslint-disable-next-line import/no-deprecated private readonly requestHandlers: RuntimeRequestHandler[]; private readonly provideEntryPoint: (runtime: IContainerRuntime) => Promise; - private readonly minVersionForCollab?: SemanticVersion; + private readonly minVersionForCollab: SemanticVersion | undefined; public constructor(props: BaseContainerRuntimeFactoryProps) { super(); diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index 2cd650c7bc44..16407fc043fb 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -705,20 +705,23 @@ export interface LoadContainerRuntimeParams { requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise; /** - * Used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} - * properties, which affect the document schema. It can be considered to be the minimum version of - * the FF runtime that we should support compatibility with. + * Minimum version of the FF runtime that is required to collaborate on new documents. + * The input should be a string that represents the minimum version of the FF runtime that should be + * supported for collaboration. The format of the string must be in valid semver format. + * + * The inputted version will be used to determine the default configuration for + * {@link IContainerRuntimeOptionsInternal} to ensure compatibility with the specified version. * * @example + * minVersionForCollab: "2.0.0" + * + * @privateRemarks + * Used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} that affect the document schema. * For example, let's say that feature `foo` was added in 2.0 which introduces a new op type. Additionally, option `bar` * was added to `IContainerRuntimeOptionsInternal` in 2.0 to enable/disable `foo` since clients prior to 2.0 would not * understand the new op type. If a customer were to set minVersionForCollab to 2.0.0, then `bar` would be set to * enable `foo` by default. If a customer were to set minVersionForCollab to 1.0.0, then `bar` would be set to * disable `foo` by default. - * - * minVersionForCollab accepts a string that must be in valid semver format. It must include the minor and patch indicators as well (i.e. 1.0 - * is not acceptable, but 1.0.0 is). For example, use "2.0.0" to set the default configuration for clients running at least the 2.0.0 version - * of the FF runtime. */ minVersionForCollab?: SemanticVersion; } diff --git a/packages/runtime/container-runtime/src/index.ts b/packages/runtime/container-runtime/src/index.ts index 8e4e6af472f1..80cfac050c0d 100644 --- a/packages/runtime/container-runtime/src/index.ts +++ b/packages/runtime/container-runtime/src/index.ts @@ -31,7 +31,7 @@ export { ChannelCollectionFactory, AllowTombstoneRequestHeaderKey, } from "./channelCollection.js"; -export { SemanticVersion } from "./compatUtils.js"; +export type { SemanticVersion } from "./compatUtils.js"; export { GCNodeType, IGCMetadata, diff --git a/packages/service-clients/azure-client/src/test/AzureClient.spec.ts b/packages/service-clients/azure-client/src/test/AzureClient.spec.ts index f40a2c6a7283..15d320781513 100644 --- a/packages/service-clients/azure-client/src/test/AzureClient.spec.ts +++ b/packages/service-clients/azure-client/src/test/AzureClient.spec.ts @@ -358,5 +358,56 @@ for (const compatibilityMode of ["1", "2"] as const) { assert.equal(view.root.itWorks, "yes"); }); }); + + describe("compatibilityModeRuntimeOptions", () => { + it("should set correct runtime options for compatibilityMode", async () => { + const { container: container_defaultConfig } = await client.createContainer( + schema, + compatibilityMode, + ); + + const expectedRuntimeOptions1 = { + summaryOptions: {}, + gcOptions: {}, + loadSequenceNumberVerification: "close", + flushMode: 0, + compressionOptions: { + minimumBatchSizeInBytes: Number.POSITIVE_INFINITY, + compressionAlgorithm: "lz4", + }, + maxBatchSizeInBytes: 716800, + chunkSizeInBytes: 204800, + enableRuntimeIdCompressor: undefined, + enableGroupedBatching: false, + explicitSchemaControl: false, + createBlobPayloadPending: undefined, + }; + const expectedRuntimeOptions2 = { + summaryOptions: {}, + gcOptions: {}, + loadSequenceNumberVerification: "close", + flushMode: 1, + compressionOptions: { minimumBatchSizeInBytes: 614400, compressionAlgorithm: "lz4" }, + maxBatchSizeInBytes: 716800, + chunkSizeInBytes: 204800, + enableRuntimeIdCompressor: "on", + enableGroupedBatching: true, + explicitSchemaControl: true, + createBlobPayloadPending: undefined, + }; + + const expectedRuntimeOptions = + compatibilityMode === "1" ? expectedRuntimeOptions1 : expectedRuntimeOptions2; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + const actualRuntimeOptions = (container_defaultConfig as any).container._runtime + .runtimeOptions; + + assert.deepStrictEqual( + actualRuntimeOptions, + expectedRuntimeOptions, + "Runtime options set properly based on compatibilityMode", + ); + }); + }); }); } From 2e1086d722687e5d3cdf7c9c3bb6e0f2a33d9922 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 1 May 2025 16:39:06 -0400 Subject: [PATCH 11/22] Update packages/framework/fluid-static/src/compatibilityConfiguration.ts Co-authored-by: Abram Sanderson --- .../framework/fluid-static/src/compatibilityConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index 726707907684..39b554b2399e 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -11,7 +11,7 @@ import type { CompatibilityMode } from "./types.js"; * The CompatibilityMode selected determines the set of runtime options to use. In "1" mode we support * full interop with true 1.x clients, while in "2" mode we only support interop with 2.x clients. * - * @remarks In general, we can use the `compatibilityMode` property of `LoadContainerRuntimeParams` to apply + * @privateRemarks In general, we can use the `compatibilityMode` property of `LoadContainerRuntimeParams` to apply * the proper configurations. However, there are some options that we need to explicity set that differ * from the default values (i.e. `enableRuntimeIdCompressor` below). */ From c3ee0303cd9b36f57f96a772b9dfc3a32610bc89 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 1 May 2025 16:54:57 -0400 Subject: [PATCH 12/22] update comment --- packages/runtime/container-runtime/src/compatUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index e6ee6725019d..8cbd5f6e6870 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -34,7 +34,10 @@ import { pkgVersion } from "./packageVersion.js"; export const defaultMinVersionForCollab = "2.0.0-defaults" as const; /** - * We don't want to allow anyone to use a version less than 1.0.0 for minVersionForCollab. + * We don't want allow a version before the major public release of the LTS version. + * Today we use "1.0.0", because our policy supports N/N-1 & N/N-2, which includes + * all minor versions of N. Though LTS starts at 1.4.0, we should stay consistent + * with our policy and allow all 1.x versions to be compatible with 2.x. */ const lowestMinVersionForCollab = "1.0.0" as const; @@ -202,7 +205,7 @@ export function getConfigsForCompatMode Date: Fri, 2 May 2025 13:17:56 -0400 Subject: [PATCH 13/22] fix naming --- packages/runtime/container-runtime/src/compatUtils.ts | 2 +- .../runtime/container-runtime/src/containerRuntime.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 8cbd5f6e6870..66dbb3ee0703 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -163,7 +163,7 @@ const runtimeOptionsAffectingDocSchemaConfigMap = { /** * Returns the default RuntimeOptionsAffectingDocSchema configuration for a given minVersionForCollab. */ -export function getminVersionForCollabDefaults( +export function getMinVersionForCollabDefaults( minVersionForCollab: SemanticVersion, ): RuntimeOptionsAffectingDocSchema { return getConfigsForCompatMode( diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index 16407fc043fb..d656e06c775f 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -159,7 +159,7 @@ import { } from "./channelCollection.js"; import { defaultMinVersionForCollab, - getminVersionForCollabDefaults, + getMinVersionForCollabDefaults, isValidMinVersionForCollab, type RuntimeOptionsAffectingDocSchema, type SemanticVersion, @@ -829,10 +829,10 @@ export class ContainerRuntime `Invalid minVersionForCollab: ${minVersionForCollab}. It must be an existing FF version (i.e. 2.22.1).`, ); } - const defaultVersionDependentConfigs = getminVersionForCollabDefaults(minVersionForCollab); + const defaultsAffectingDocSchema = getMinVersionForCollabDefaults(minVersionForCollab); // The following are the default values for the options that do not affect the DocumentSchema. - const defaultConfigsNonVersionDependent: Required< + const defaultsNotAffectingDocSchema: Required< Omit > = { summaryOptions: {}, @@ -842,8 +842,8 @@ export class ContainerRuntime }; const defaultConfigs = { - ...defaultVersionDependentConfigs, - ...defaultConfigsNonVersionDependent, + ...defaultsAffectingDocSchema, + ...defaultsNotAffectingDocSchema, }; // Here we set each option to its corresponding default config value if it's not provided in runtimeOptions. From 81c8e8e2b390aab09a68525855eb95b6c0e8a2b3 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 5 May 2025 15:31:18 -0400 Subject: [PATCH 14/22] pr feedback --- .../container-runtime/src/compatUtils.ts | 2 -- .../container-runtime/src/containerRuntime.ts | 5 +++-- .../src/test/containerRuntime.spec.ts | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 66dbb3ee0703..3fbfbe8e57c5 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -209,9 +209,7 @@ export function getConfigsForCompatMode + const defaultsNotAffectingDocSchema: Omit< + ContainerRuntimeOptionsInternal, + keyof RuntimeOptionsAffectingDocSchema > = { summaryOptions: {}, loadSequenceNumberVerification: "close", diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 8a63205603fa..bf1b1af6d7a0 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -73,6 +73,7 @@ import { import { SinonFakeTimers, createSandbox, useFakeTimers } from "sinon"; import { ChannelCollection } from "../channelCollection.js"; +import type { SemanticVersion } from "../compatUtils.js"; import { CompressionAlgorithms } from "../compressionDefinitions.js"; import { ContainerRuntime, @@ -3664,6 +3665,23 @@ describe("Runtime", () => { ]); }); + it("throws when minVersionForCollab is not valid", async () => { + const logger = new MockLogger(); + const invalidVersions: SemanticVersion[] = ["0.50.0", "100.0.0"]; + for (const version of invalidVersions) { + await assert.rejects(async () => { + await ContainerRuntime.loadRuntime({ + context: getMockContext({ logger }) as IContainerContext, + registryEntries: [], + existing: false, + runtimeOptions: {}, + provideEntryPoint: mockProvideEntryPoint, + minVersionForCollab: version, + }); + }); + } + }); + it("minVersionForCollab = 1.0.0", async () => { const minVersionForCollab = "1.0.0"; const logger = new MockLogger(); From 7690d0c9363efaae19efba89c81d5016c06fd2bf Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 5 May 2025 16:26:07 -0400 Subject: [PATCH 15/22] pr feedback --- .../src/test/containerRuntime.spec.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index bf1b1af6d7a0..26e136ef2806 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -73,7 +73,6 @@ import { import { SinonFakeTimers, createSandbox, useFakeTimers } from "sinon"; import { ChannelCollection } from "../channelCollection.js"; -import type { SemanticVersion } from "../compatUtils.js"; import { CompressionAlgorithms } from "../compressionDefinitions.js"; import { ContainerRuntime, @@ -3665,10 +3664,15 @@ describe("Runtime", () => { ]); }); - it("throws when minVersionForCollab is not valid", async () => { - const logger = new MockLogger(); - const invalidVersions: SemanticVersion[] = ["0.50.0", "100.0.0"]; - for (const version of invalidVersions) { + // These are examples of minVersionForCollab inputs that are not valid. + // minVersionForCollab should be at least 1.0.0 and less than or equal to + // the current pkgVersion. + const invalidVersions = ["0.50.0", "100.0.0"] as const; + for (const version of invalidVersions) { + it(`throws when minVersionForCollab = ${version}`, async () => { + const logger = new MockLogger(); + // These are examples of minVersionForCollab versions that are not valid. + // Currently we only await assert.rejects(async () => { await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, @@ -3679,8 +3683,8 @@ describe("Runtime", () => { minVersionForCollab: version, }); }); - } - }); + }); + } it("minVersionForCollab = 1.0.0", async () => { const minVersionForCollab = "1.0.0"; From 9652cd3f996eded321aca4abf37e7de3b5eff193 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 5 May 2025 16:29:55 -0400 Subject: [PATCH 16/22] remove todo --- packages/framework/fluid-static/src/rootDataObject.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 8ecad3c89944..d8968b53195e 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -43,7 +43,7 @@ import { * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. */ const compatibilityModeToMinVersionForCollab: Record = { - "1": "1.0.0", // TODO: Should this be the LTS version? + "1": "1.0.0", "2": "2.0.0", }; From a2a808067b54ce7c71a2549e36c5de6ac7faa117 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 5 May 2025 22:11:41 -0400 Subject: [PATCH 17/22] remove accidental leftover comment --- .../runtime/container-runtime/src/test/containerRuntime.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 26e136ef2806..2811d402dab0 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -3671,8 +3671,6 @@ describe("Runtime", () => { for (const version of invalidVersions) { it(`throws when minVersionForCollab = ${version}`, async () => { const logger = new MockLogger(); - // These are examples of minVersionForCollab versions that are not valid. - // Currently we only await assert.rejects(async () => { await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, From c290f1ee53ae35872e857852df3cc1a94bef9cd7 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 6 May 2025 09:44:29 -0400 Subject: [PATCH 18/22] change SemanticVersion type --- .../container-runtime.legacy.alpha.api.md | 2 +- .../container-runtime/src/compatUtils.ts | 4 +-- .../src/test/compatUtils.spec.ts | 9 ++++-- .../src/test/containerRuntime.spec.ts | 30 ++++--------------- 4 files changed, 16 insertions(+), 29 deletions(-) diff --git a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md index f083489c62b7..d4c8b5622950 100644 --- a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md +++ b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md @@ -359,7 +359,7 @@ export type OpActionEventName = MessageType.Summarize | MessageType.SummaryAck | export type ReadFluidDataStoreAttributes = IFluidDataStoreAttributes0 | IFluidDataStoreAttributes1 | IFluidDataStoreAttributes2; // @alpha @legacy -export type SemanticVersion = `${bigint}.${bigint}.${bigint}` | `${bigint}.${bigint}.${bigint}-${string}`; +export type SemanticVersion = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; // @alpha @legacy export interface SubmitSummaryFailureData { diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 3fbfbe8e57c5..700fd3c7c66d 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -56,8 +56,8 @@ export type MinimumMinorSemanticVersion = `${bigint}.${bigint}.0` | `${bigint}.0 * @alpha */ export type SemanticVersion = - | `${bigint}.${bigint}.${bigint}` - | `${bigint}.${bigint}.${bigint}-${string}`; + | `${1 | 2}.${bigint}.${bigint}` + | `${1 | 2}.${bigint}.${bigint}-${string}`; /** * Generic type for runtimeOptionsAffectingDocSchemaConfigMap diff --git a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts index 809e54387a91..4be56a498b7e 100644 --- a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts +++ b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts @@ -62,7 +62,9 @@ describe("compatUtils", () => { }; const testCases: { - minVersionForCollab: SemanticVersion; + // We use string instead of SemanticVersion for `minVersionForCollab` + // so we can test versions that don't start with 1 or 2. + minVersionForCollab: string; expectedConfig: Partial; }[] = [ { @@ -212,7 +214,10 @@ describe("compatUtils", () => { for (const testCase of testCases) { it(`returns correct configs for minVersionForCollab = "${testCase.minVersionForCollab}"`, () => { - const config = getConfigsForCompatMode(testCase.minVersionForCollab, testConfigMap); + const config = getConfigsForCompatMode( + testCase.minVersionForCollab as SemanticVersion, + testConfigMap, + ); assert.deepEqual( config, testCase.expectedConfig, diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 2811d402dab0..8606a3b3d18c 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -90,6 +90,7 @@ import { type UnknownContainerRuntimeMessage, } from "../messageTypes.js"; import type { InboundMessageResult, LocalBatchMessage } from "../opLifecycle/index.js"; +import { pkgVersion } from "../packageVersion.js"; import { IPendingLocalState, IPendingMessage, @@ -3664,26 +3665,6 @@ describe("Runtime", () => { ]); }); - // These are examples of minVersionForCollab inputs that are not valid. - // minVersionForCollab should be at least 1.0.0 and less than or equal to - // the current pkgVersion. - const invalidVersions = ["0.50.0", "100.0.0"] as const; - for (const version of invalidVersions) { - it(`throws when minVersionForCollab = ${version}`, async () => { - const logger = new MockLogger(); - await assert.rejects(async () => { - await ContainerRuntime.loadRuntime({ - context: getMockContext({ logger }) as IContainerContext, - registryEntries: [], - existing: false, - runtimeOptions: {}, - provideEntryPoint: mockProvideEntryPoint, - minVersionForCollab: version, - }); - }); - }); - } - it("minVersionForCollab = 1.0.0", async () => { const minVersionForCollab = "1.0.0"; const logger = new MockLogger(); @@ -3934,9 +3915,10 @@ describe("Runtime", () => { ]); }); - // Skipped since 3.0.0 is not an existing FF version yet - it.skip("minVersionForCollab = 3.0.0", async () => { - const minVersionForCollab = "3.0.0"; + // Note: We may need to update `expectedRuntimeOptions` for this test + // when we bump to certain versions. + it("minVersionForCollab = pkgVersion", async () => { + const minVersionForCollab = pkgVersion; const logger = new MockLogger(); await ContainerRuntime.loadRuntime({ context: getMockContext({ logger }) as IContainerContext, @@ -3949,7 +3931,7 @@ describe("Runtime", () => { const expectedRuntimeOptions: IContainerRuntimeOptionsInternal = { summaryOptions: {}, - gcOptions: { enableGCSweep: true }, + gcOptions: {}, loadSequenceNumberVerification: "close", flushMode: FlushMode.TurnBased, compressionOptions: { From 5791f81bff7a34328d4d844a2562756eeceaeef5 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 6 May 2025 13:45:12 -0400 Subject: [PATCH 19/22] PR feedback - add back in removed tests, cleanup testing types --- .../container-runtime/src/compatUtils.ts | 13 ++++++++++-- .../src/test/compatUtils.spec.ts | 9 +++----- .../src/test/containerRuntime.spec.ts | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index 700fd3c7c66d..f2da0f885e0a 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -59,6 +59,15 @@ export type SemanticVersion = | `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; +/** + * String in a valid semver format of a specific version at least specifying minor. + * Unlike {@link SemanticVersion}, this type allows any bigint for the major version. + * Currently only used in unit tests to allow versions that don't start with 1 or 2. + */ +export type MinimumVersionForCollab = + | `${bigint}.${bigint}.${bigint}` + | `${bigint}.${bigint}.${bigint}-${string}`; + /** * Generic type for runtimeOptionsAffectingDocSchemaConfigMap */ @@ -177,8 +186,8 @@ export function getMinVersionForCollabDefaults( /** * Returns a default configuration given minVersionForCollab and configuration version map. */ -export function getConfigsForCompatMode>( - minVersionForCollab: SemanticVersion, +export function getConfigsForCompatMode>( + minVersionForCollab: MinimumVersionForCollab, configMap: ConfigMap, ): Partial { const defaultConfigs: Partial = {}; diff --git a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts index 4be56a498b7e..f6fed2b16cd4 100644 --- a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts +++ b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts @@ -8,7 +8,7 @@ import { strict as assert } from "node:assert"; import { getConfigsForCompatMode, type ConfigMap, - type SemanticVersion, + type MinimumVersionForCollab, } from "../compatUtils.js"; describe("compatUtils", () => { @@ -64,7 +64,7 @@ describe("compatUtils", () => { const testCases: { // We use string instead of SemanticVersion for `minVersionForCollab` // so we can test versions that don't start with 1 or 2. - minVersionForCollab: string; + minVersionForCollab: MinimumVersionForCollab; expectedConfig: Partial; }[] = [ { @@ -214,10 +214,7 @@ describe("compatUtils", () => { for (const testCase of testCases) { it(`returns correct configs for minVersionForCollab = "${testCase.minVersionForCollab}"`, () => { - const config = getConfigsForCompatMode( - testCase.minVersionForCollab as SemanticVersion, - testConfigMap, - ); + const config = getConfigsForCompatMode(testCase.minVersionForCollab, testConfigMap); assert.deepEqual( config, testCase.expectedConfig, diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 8606a3b3d18c..021cba63b6e8 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -3665,6 +3665,27 @@ describe("Runtime", () => { ]); }); + // These are examples of minVersionForCollab inputs that are not valid. + // minVersionForCollab should be at least 1.0.0 and less than or equal to + // the current pkgVersion. + const invalidVersions = ["0.50.0", "100.0.0"] as const; + for (const version of invalidVersions) { + it(`throws when minVersionForCollab = ${version}`, async () => { + const logger = new MockLogger(); + await assert.rejects(async () => { + await ContainerRuntime.loadRuntime({ + context: getMockContext({ logger }) as IContainerContext, + registryEntries: [], + existing: false, + runtimeOptions: {}, + provideEntryPoint: mockProvideEntryPoint, + // @ts-expect-error - Invalid version strings are not castable to SemanticVersion + minVersionForCollab: version, + }); + }); + }); + } + it("minVersionForCollab = 1.0.0", async () => { const minVersionForCollab = "1.0.0"; const logger = new MockLogger(); From dd817269651aa18d8c16b79777bdda17ef6a46f8 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 6 May 2025 14:35:19 -0400 Subject: [PATCH 20/22] swap names for MinimumVersionForCollab/SemanticVersion --- .../api-report/aqueduct.legacy.alpha.api.md | 2 +- .../baseContainerRuntimeFactory.ts | 6 ++--- .../api-report/fluid-framework.alpha.api.md | 6 ++--- .../fluid-static/src/rootDataObject.ts | 4 ++-- .../container-runtime.legacy.alpha.api.md | 8 +++---- .../container-runtime/src/compatUtils.ts | 24 ++++++++++--------- .../container-runtime/src/containerRuntime.ts | 6 ++--- .../runtime/container-runtime/src/index.ts | 2 +- .../src/test/compatUtils.spec.ts | 6 ++--- .../src/test/containerRuntime.spec.ts | 2 +- 10 files changed, 34 insertions(+), 32 deletions(-) diff --git a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md index e0fa93ef2a9a..ecffc9b2b04e 100644 --- a/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md +++ b/packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md @@ -19,7 +19,7 @@ export class BaseContainerRuntimeFactory extends RuntimeFactoryHelper implements export interface BaseContainerRuntimeFactoryProps { // @deprecated (undocumented) dependencyContainer?: IFluidDependencySynthesizer; - minVersionForCollab?: SemanticVersion | undefined; + minVersionForCollab?: MinimumVersionForCollab | undefined; provideEntryPoint: (runtime: IContainerRuntime) => Promise; registryEntries: NamedFluidDataStoreRegistryEntries; // @deprecated diff --git a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts index c989e5dc6798..6578a0cb5885 100644 --- a/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts +++ b/packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts @@ -11,7 +11,7 @@ import { FluidDataStoreRegistry, loadContainerRuntime, type IContainerRuntimeOptions, - type SemanticVersion, + type MinimumVersionForCollab, } from "@fluidframework/container-runtime/internal"; import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal"; import type { FluidObject } from "@fluidframework/core-interfaces"; @@ -66,7 +66,7 @@ export interface BaseContainerRuntimeFactoryProps { * The minVersionForCollab passed to the ContainerRuntime when instantiating it. * See {@link @fluidframework/container-runtime#LoadContainerRuntimeParams} for more details on this property. */ - minVersionForCollab?: SemanticVersion | undefined; + minVersionForCollab?: MinimumVersionForCollab | undefined; } /** @@ -94,7 +94,7 @@ export class BaseContainerRuntimeFactory // eslint-disable-next-line import/no-deprecated private readonly requestHandlers: RuntimeRequestHandler[]; private readonly provideEntryPoint: (runtime: IContainerRuntime) => Promise; - private readonly minVersionForCollab: SemanticVersion | undefined; + private readonly minVersionForCollab: MinimumVersionForCollab | undefined; public constructor(props: BaseContainerRuntimeFactoryProps) { super(); diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index bf86f0c9baa0..081684939e09 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -760,9 +760,9 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Map, System_Unsafe.TreeMapNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Map, unknown>, { - [Symbol.iterator](): Iterator<[string, string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null], any, undefined>; + [Symbol.iterator](): Iterator<[string, string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null], any, undefined>; } | { - readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; + readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined>; // (undocumented) export type Primitive = TreeNodeFromImplicitAllowedTypes; @@ -770,7 +770,7 @@ export namespace JsonAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass<"com.fluidframework.json.array", NodeKind.Array, System_Unsafe.TreeArrayNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.array", NodeKind.Array, unknown>, { - [Symbol.iterator](): Iterator, LeafSchema<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator, LeafSchema<"boolean", boolean>> | JsonObject | Array | null, any, undefined>; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index d8968b53195e..7edb72119f40 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -12,7 +12,7 @@ import { import type { IRuntimeFactory } from "@fluidframework/container-definitions/internal"; import { FluidDataStoreRegistry, - type SemanticVersion, + type MinimumVersionForCollab, } from "@fluidframework/container-runtime/internal"; import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal"; import type { FluidObject, IFluidLoadable } from "@fluidframework/core-interfaces"; @@ -42,7 +42,7 @@ import { /** * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. */ -const compatibilityModeToMinVersionForCollab: Record = { +const compatibilityModeToMinVersionForCollab: Record = { "1": "1.0.0", "2": "2.0.0", }; diff --git a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md index d4c8b5622950..e0d3aab6bdaa 100644 --- a/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md +++ b/packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md @@ -338,7 +338,7 @@ export interface LoadContainerRuntimeParams { containerScope?: FluidObject; context: IContainerContext; existing: boolean; - minVersionForCollab?: SemanticVersion; + minVersionForCollab?: MinimumVersionForCollab; provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise; registryEntries: NamedFluidDataStoreRegistryEntries; // @deprecated @@ -346,6 +346,9 @@ export interface LoadContainerRuntimeParams { runtimeOptions?: IContainerRuntimeOptions; } +// @alpha @legacy +export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; + // @alpha @deprecated @legacy (undocumented) export type OmitAttributesVersions = Omit; @@ -358,9 +361,6 @@ export type OpActionEventName = MessageType.Summarize | MessageType.SummaryAck | // @alpha @deprecated @legacy export type ReadFluidDataStoreAttributes = IFluidDataStoreAttributes0 | IFluidDataStoreAttributes1 | IFluidDataStoreAttributes2; -// @alpha @legacy -export type SemanticVersion = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; - // @alpha @legacy export interface SubmitSummaryFailureData { // (undocumented) diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index f2da0f885e0a..d6fef5123501 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -55,16 +55,16 @@ export type MinimumMinorSemanticVersion = `${bigint}.${bigint}.0` | `${bigint}.0 * @legacy * @alpha */ -export type SemanticVersion = +export type MinimumVersionForCollab = | `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; /** * String in a valid semver format of a specific version at least specifying minor. - * Unlike {@link SemanticVersion}, this type allows any bigint for the major version. + * Unlike {@link MinimumVersionForCollab}, this type allows any bigint for the major version. * Currently only used in unit tests to allow versions that don't start with 1 or 2. */ -export type MinimumVersionForCollab = +export type SemanticVersion = | `${bigint}.${bigint}.${bigint}` | `${bigint}.${bigint}.${bigint}-${string}`; @@ -104,9 +104,9 @@ export type RuntimeOptionsAffectingDocSchema = Omit< /** * Mapping of RuntimeOptionsAffectingDocSchema to their compatibility related configs. * - * Each key in this map corresponds to a property in RuntimeOptionsAffectingDocSchema. The value is an object that maps SemanticVersions - * to the appropriate default value for that property to supporting that SemanticVersion. If clients running SemanticVersion X are able to understand - * the format changes introduced by the property, then the default value for that SemanticVersion will enable the feature associated with the property. + * Each key in this map corresponds to a property in RuntimeOptionsAffectingDocSchema. The value is an object that maps MinimumVersionForCollab + * to the appropriate default value for that property to supporting that MinimumVersionForCollab. If clients running MinimumVersionForCollab X are able to understand + * the format changes introduced by the property, then the default value for that MinimumVersionForCollab will enable the feature associated with the property. * Otherwise, the feature will be disabled. * * For example if the minVersionForCollab is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x @@ -173,7 +173,7 @@ const runtimeOptionsAffectingDocSchemaConfigMap = { * Returns the default RuntimeOptionsAffectingDocSchema configuration for a given minVersionForCollab. */ export function getMinVersionForCollabDefaults( - minVersionForCollab: SemanticVersion, + minVersionForCollab: MinimumVersionForCollab, ): RuntimeOptionsAffectingDocSchema { return getConfigsForCompatMode( minVersionForCollab, @@ -186,8 +186,8 @@ export function getMinVersionForCollabDefaults( /** * Returns a default configuration given minVersionForCollab and configuration version map. */ -export function getConfigsForCompatMode>( - minVersionForCollab: MinimumVersionForCollab, +export function getConfigsForCompatMode>( + minVersionForCollab: SemanticVersion, configMap: ConfigMap, ): Partial { const defaultConfigs: Partial = {}; @@ -214,9 +214,11 @@ export function getConfigsForCompatMode Promise; provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise; - minVersionForCollab?: SemanticVersion; + minVersionForCollab?: MinimumVersionForCollab; }): Promise { const { context, diff --git a/packages/runtime/container-runtime/src/index.ts b/packages/runtime/container-runtime/src/index.ts index dd8519e73673..12e532a465cf 100644 --- a/packages/runtime/container-runtime/src/index.ts +++ b/packages/runtime/container-runtime/src/index.ts @@ -31,7 +31,7 @@ export { ChannelCollectionFactory, AllowTombstoneRequestHeaderKey, } from "./channelCollection.js"; -export type { SemanticVersion } from "./compatUtils.js"; +export type { MinimumVersionForCollab } from "./compatUtils.js"; export { GCNodeType, IGCMetadata, diff --git a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts index f6fed2b16cd4..e894afc9a14c 100644 --- a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts +++ b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts @@ -8,7 +8,7 @@ import { strict as assert } from "node:assert"; import { getConfigsForCompatMode, type ConfigMap, - type MinimumVersionForCollab, + type SemanticVersion, } from "../compatUtils.js"; describe("compatUtils", () => { @@ -62,9 +62,9 @@ describe("compatUtils", () => { }; const testCases: { - // We use string instead of SemanticVersion for `minVersionForCollab` + // We use `SemanticVersion` instead of `MinimumVersionForCollab` // so we can test versions that don't start with 1 or 2. - minVersionForCollab: MinimumVersionForCollab; + minVersionForCollab: SemanticVersion; expectedConfig: Partial; }[] = [ { diff --git a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts index 021cba63b6e8..6e4c02bcc1ce 100644 --- a/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts +++ b/packages/runtime/container-runtime/src/test/containerRuntime.spec.ts @@ -3679,7 +3679,7 @@ describe("Runtime", () => { existing: false, runtimeOptions: {}, provideEntryPoint: mockProvideEntryPoint, - // @ts-expect-error - Invalid version strings are not castable to SemanticVersion + // @ts-expect-error - Invalid version strings are not castable to MinimumVersionForCollab minVersionForCollab: version, }); }); From ec3478908bbb85d780f0b510db876bedbd05b5b2 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 6 May 2025 14:41:50 -0400 Subject: [PATCH 21/22] revert api file, fix format --- .../fluid-framework/api-report/fluid-framework.alpha.api.md | 6 +++--- packages/framework/fluid-static/src/rootDataObject.ts | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 081684939e09..bf86f0c9baa0 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -760,9 +760,9 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Map, System_Unsafe.TreeMapNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Map, unknown>, { - [Symbol.iterator](): Iterator<[string, string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null], any, undefined>; + [Symbol.iterator](): Iterator<[string, string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null], any, undefined>; } | { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined>; // (undocumented) export type Primitive = TreeNodeFromImplicitAllowedTypes; @@ -770,7 +770,7 @@ export namespace JsonAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass<"com.fluidframework.json.array", NodeKind.Array, System_Unsafe.TreeArrayNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.array", NodeKind.Array, unknown>, { - [Symbol.iterator](): Iterator, LeafSchema<"boolean", boolean>> | JsonObject | Array | null, any, undefined>; + [Symbol.iterator](): Iterator, LeafSchema<"boolean", boolean>> | null, any, undefined>; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 7edb72119f40..e9bb7c51ca9f 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -42,7 +42,10 @@ import { /** * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. */ -const compatibilityModeToMinVersionForCollab: Record = { +const compatibilityModeToMinVersionForCollab: Record< + CompatibilityMode, + MinimumVersionForCollab +> = { "1": "1.0.0", "2": "2.0.0", }; From 82b8c552260aaa2b177ddc635399904e2d9744ef Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 6 May 2025 15:02:27 -0400 Subject: [PATCH 22/22] pr feedback --- packages/framework/fluid-static/src/rootDataObject.ts | 7 ++----- packages/runtime/container-runtime/src/compatUtils.ts | 7 ++++--- .../runtime/container-runtime/src/test/compatUtils.spec.ts | 2 -- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index e9bb7c51ca9f..ae93302cdfe0 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -42,13 +42,10 @@ import { /** * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. */ -const compatibilityModeToMinVersionForCollab: Record< - CompatibilityMode, - MinimumVersionForCollab -> = { +const compatibilityModeToMinVersionForCollab = { "1": "1.0.0", "2": "2.0.0", -}; +} as const satisfies Record; /** * Input props for {@link RootDataObject.initializingFirstTime}. diff --git a/packages/runtime/container-runtime/src/compatUtils.ts b/packages/runtime/container-runtime/src/compatUtils.ts index d6fef5123501..3c2af4af8ae7 100644 --- a/packages/runtime/container-runtime/src/compatUtils.ts +++ b/packages/runtime/container-runtime/src/compatUtils.ts @@ -31,7 +31,8 @@ import { pkgVersion } from "./packageVersion.js"; * any 2.0.0+ version, we will use a special value of `2.0.0-defaults`, which * is semantically less than 2.0.0. */ -export const defaultMinVersionForCollab = "2.0.0-defaults" as const; +export const defaultMinVersionForCollab = + "2.0.0-defaults" as const satisfies MinimumVersionForCollab; /** * We don't want allow a version before the major public release of the LTS version. @@ -39,7 +40,7 @@ export const defaultMinVersionForCollab = "2.0.0-defaults" as const; * all minor versions of N. Though LTS starts at 1.4.0, we should stay consistent * with our policy and allow all 1.x versions to be compatible with 2.x. */ -const lowestMinVersionForCollab = "1.0.0" as const; +const lowestMinVersionForCollab = "1.0.0" as const satisfies MinimumVersionForCollab; /** * String in a valid semver format specifying bottom of a minor version @@ -62,7 +63,7 @@ export type MinimumVersionForCollab = /** * String in a valid semver format of a specific version at least specifying minor. * Unlike {@link MinimumVersionForCollab}, this type allows any bigint for the major version. - * Currently only used in unit tests to allow versions that don't start with 1 or 2. + * Used as a more generic type that allows major versions other than 1 or 2. */ export type SemanticVersion = | `${bigint}.${bigint}.${bigint}` diff --git a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts index e894afc9a14c..809e54387a91 100644 --- a/packages/runtime/container-runtime/src/test/compatUtils.spec.ts +++ b/packages/runtime/container-runtime/src/test/compatUtils.spec.ts @@ -62,8 +62,6 @@ describe("compatUtils", () => { }; const testCases: { - // We use `SemanticVersion` instead of `MinimumVersionForCollab` - // so we can test versions that don't start with 1 or 2. minVersionForCollab: SemanticVersion; expectedConfig: Partial; }[] = [