From 3993621ddaede34b90196369892c23d16e0593df Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 30 Apr 2026 15:52:53 -0400 Subject: [PATCH 01/18] update declarative model for compat policy updates --- .../src/compatibilityConfiguration.ts | 2 + .../fluid-static/src/rootDataObject.ts | 54 +++++++++++-------- .../fluid-static/src/treeRootDataObject.ts | 48 +++++++++-------- packages/framework/fluid-static/src/types.ts | 6 +++ packages/framework/fluid-static/src/utils.ts | 35 +++++++++++- .../src/compatibilityDefinitions.ts | 2 +- .../service-clients/azure-client/package.json | 1 + .../azure-client/src/AzureClient.ts | 54 ++++++++++++++----- .../azure-client/src/interfaces.ts | 5 ++ .../src/test/AzureClientFactory.ts | 10 +++- .../tinylicious-client/package.json | 1 + .../src/TinyliciousClient.ts | 29 ++++++++-- pnpm-lock.yaml | 6 +++ 13 files changed, 189 insertions(+), 64 deletions(-) diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index 39b554b2399e..ea8be121d0b8 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -5,6 +5,7 @@ import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal"; +// eslint-disable-next-line import-x/no-deprecated import type { CompatibilityMode } from "./types.js"; /** @@ -16,6 +17,7 @@ import type { CompatibilityMode } from "./types.js"; * from the default values (i.e. `enableRuntimeIdCompressor` below). */ export const compatibilityModeRuntimeOptions: Record< + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, IContainerRuntimeOptionsInternal > = { diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 53818d6a4324..f128e528fad8 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -31,8 +31,8 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, ContainerSchema, IRootDataObject, @@ -42,13 +42,13 @@ import type { LoadableObjectRecord, } from "./types.js"; import { - compatibilityModeToMinVersionForCollab, createDataObject, createSharedObject, isDataObjectKind, isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, + resolveMinVersionAndRuntimeOptions, } from "./utils.js"; /** @@ -188,6 +188,9 @@ async function provideEntryPoint( * The root data object's registry and initial objects are configured based on the provided * schema (and optionally, data store registry). * + * `compatibilityMode` (deprecated) selects the runtime defaults. `minVersionForCollab` is an + * optional overlay; when provided, it overrides the version derived from `compatibilityMode`. + * * @internal */ export function createDOProviderContainerRuntimeFactory(props: { @@ -196,9 +199,18 @@ export function createDOProviderContainerRuntimeFactory(props: { */ schema: ContainerSchema; /** - * See {@link CompatibilityMode} and compatibilityModeRuntimeOptions for more details. + * See {@link CompatibilityMode} for more details. + * + * @deprecated Use {@link createDOProviderContainerRuntimeFactory.minVersionForCollab | minVersionForCollab} instead. */ + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode; + /** + * Minimum framework version required for collaboration on the document. + * Replaces `compatibilityMode`; aligns the declarative model with the encapsulated model's + * `minVersionForCollab` configuration. + */ + minVersionForCollab?: MinimumVersionForCollab; /** * Optional registry of data stores to pass to the DataObject factory. * If not provided, one will be created based on the schema. @@ -206,34 +218,38 @@ export function createDOProviderContainerRuntimeFactory(props: { rootDataStoreRegistry?: IFluidDataStoreRegistry; /** * Optional overrides for the container runtime options. - * If not provided, only the default options for the given compatibilityMode will be used. */ runtimeOptionOverrides?: Partial; /** - * Optional override for minimum version for collab. - * If not provided, the default for the given compatibilityMode will be used. - * @remarks - * This is useful when runtime options are overridden and change the minimum version for collab. + * @deprecated Use `minVersionForCollab` instead. When set, behaves identically to passing + * `minVersionForCollab` directly. */ minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { const { compatibilityMode, + minVersionForCollab, minVersionForCollabOverride, rootDataStoreRegistry, runtimeOptionOverrides, schema, } = props; + + const { minVersionForCollab: resolvedMinVersion, runtimeOptions: baseRuntimeOptions } = + resolveMinVersionAndRuntimeOptions({ + compatibilityMode, + minVersionForCollab: minVersionForCollab ?? minVersionForCollabOverride, + }); + const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema); const registry = rootDataStoreRegistry ?? new FluidDataStoreRegistry(registryEntries); return new DOProviderContainerRuntimeFactory( schema, - compatibilityMode, new RootDataObjectFactory(sharedObjects, registry), { - runtimeOptions: runtimeOptionOverrides, - minVersionForCollab: minVersionForCollabOverride, + runtimeOptions: { ...baseRuntimeOptions, ...runtimeOptionOverrides }, + minVersionForCollab: resolvedMinVersion, }, ); } @@ -263,31 +279,25 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { * since it can take care of constructing the root data object factory based on the schema. * * @param schema - The schema for the container - * @param compatibilityMode - Compatibility mode * @param rootDataObjectFactory - A factory that can construct the root data object. + * @param resolvedConfig - Pre-resolved runtime options and minimum version for collab. */ public constructor( schema: ContainerSchema, - compatibilityMode: CompatibilityMode, rootDataObjectFactory: DataObjectFactory< RootDataObject, { InitialState: RootDataObjectProps } >, - overrides?: Partial<{ + resolvedConfig: { runtimeOptions: Partial; minVersionForCollab: MinimumVersionForCollab; - }>, + }, ) { super({ registryEntries: [rootDataObjectFactory.registryEntry], - runtimeOptions: { - ...compatibilityModeRuntimeOptions[compatibilityMode], - ...overrides?.runtimeOptions, - }, + runtimeOptions: resolvedConfig.runtimeOptions, provideEntryPoint, - minVersionForCollab: - overrides?.minVersionForCollab ?? - compatibilityModeToMinVersionForCollab[compatibilityMode], + minVersionForCollab: resolvedConfig.minVersionForCollab, }); this.rootDataObjectFactory = rootDataObjectFactory; this.initialObjects = schema.initialObjects; diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index 0a7cb85bb01f..8fdaba48e248 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -31,8 +31,8 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, IRootDataObject, IStaticEntryPoint, @@ -41,13 +41,13 @@ import type { TreeContainerSchema, } from "./types.js"; import { - compatibilityModeToMinVersionForCollab, createDataObject, createSharedObject, isDataObjectKind, isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, + resolveMinVersionAndRuntimeOptions, } from "./utils.js"; /** @@ -135,23 +135,17 @@ class TreeContainerRuntimeFactory extends BaseContainerRuntimeFactory { readonly #treeRootDataObjectFactory: TreeDataObjectFactory; public constructor( - compatibilityMode: CompatibilityMode, treeRootDataObjectFactory: TreeDataObjectFactory, - overrides?: Partial<{ + resolvedConfig: { runtimeOptions: Partial; minVersionForCollab: MinimumVersionForCollab; - }>, + }, ) { super({ registryEntries: [treeRootDataObjectFactory.registryEntry], - runtimeOptions: { - ...compatibilityModeRuntimeOptions[compatibilityMode], - ...overrides?.runtimeOptions, - }, + runtimeOptions: resolvedConfig.runtimeOptions, provideEntryPoint, - minVersionForCollab: - overrides?.minVersionForCollab ?? - compatibilityModeToMinVersionForCollab[compatibilityMode], + minVersionForCollab: resolvedConfig.minVersionForCollab, }); this.#treeRootDataObjectFactory = treeRootDataObjectFactory; } @@ -211,9 +205,18 @@ export function createTreeContainerRuntimeFactory(props: { readonly schema: TreeContainerSchema; /** - * See {@link CompatibilityMode} and compatibilityModeRuntimeOptions for more details. + * See {@link CompatibilityMode} for more details. + * + * @deprecated Use `minVersionForCollab` instead. */ + // eslint-disable-next-line import-x/no-deprecated readonly compatibilityMode: CompatibilityMode; + /** + * Minimum framework version required for collaboration on the document. + * Replaces `compatibilityMode`; aligns the declarative model with the encapsulated model's + * `minVersionForCollab` configuration. + */ + readonly minVersionForCollab?: MinimumVersionForCollab; /** * Optional registry of data stores to pass to the DataObject factory. * If not provided, one will be created based on the schema. @@ -221,34 +224,37 @@ export function createTreeContainerRuntimeFactory(props: { readonly rootDataStoreRegistry?: IFluidDataStoreRegistry; /** * Optional overrides for the container runtime options. - * If not provided, only the default options for the given compatibilityMode will be used. */ readonly runtimeOptionOverrides?: Partial; /** - * Optional override for minimum version for collab. - * If not provided, the default for the given compatibilityMode will be used. - * @remarks - * This is useful when runtime options are overridden and change the minimum version for collab. + * @deprecated Use `minVersionForCollab` instead. When set, behaves identically to passing + * `minVersionForCollab` directly (but will override `minVersionForCollab` if both are provided). */ readonly minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { const { compatibilityMode, + minVersionForCollab, minVersionForCollabOverride, rootDataStoreRegistry, runtimeOptionOverrides, schema, } = props; + const { minVersionForCollab: resolvedMinVersion, runtimeOptions: baseRuntimeOptions } = + resolveMinVersionAndRuntimeOptions({ + compatibilityMode, + minVersionForCollab: minVersionForCollab ?? minVersionForCollabOverride, + }); + const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema); const registry = rootDataStoreRegistry ?? new FluidDataStoreRegistry(registryEntries); return new TreeContainerRuntimeFactory( - compatibilityMode, new TreeRootDataObjectFactory(sharedObjects, registry), { - runtimeOptions: runtimeOptionOverrides, - minVersionForCollab: minVersionForCollabOverride, + runtimeOptions: { ...baseRuntimeOptions, ...runtimeOptionOverrides }, + minVersionForCollab: resolvedMinVersion, }, ); } diff --git a/packages/framework/fluid-static/src/types.ts b/packages/framework/fluid-static/src/types.ts index af0b2c442845..833200344a5f 100644 --- a/packages/framework/fluid-static/src/types.ts +++ b/packages/framework/fluid-static/src/types.ts @@ -22,6 +22,12 @@ import type { ITree } from "@fluidframework/tree"; * In "1" mode we support full interop between 2.x clients and 1.x clients, * while in "2" mode we only support interop between 2.x clients. * + * @deprecated Specify the minimum framework version directly via the + * `minVersionForCollab` parameter, which accepts a + * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string. The + * legacy mode "1" is equivalent to `minVersionForCollab: "1.0.0"`; mode "2" is + * equivalent to `"2.0.0"`. + * * @public */ export type CompatibilityMode = "1" | "2"; diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index 41fcb4b8bf33..c7ffed78c81e 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -4,6 +4,7 @@ */ import type { DataObjectKind } from "@fluidframework/aqueduct/internal"; +import type { IContainerRuntimeOptions } from "@fluidframework/container-runtime/internal"; import type { FluidObjectKeys, IFluidLoadable } from "@fluidframework/core-interfaces"; import { oob } from "@fluidframework/core-utils/internal"; import type { @@ -19,7 +20,9 @@ import type { ISharedObjectKind } from "@fluidframework/shared-object-base/inter import { UsageError } from "@fluidframework/telemetry-utils/internal"; import { SharedTreeFactoryType } from "@fluidframework/tree/internal"; +import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, ContainerSchema, LoadableObjectKind, @@ -147,13 +150,43 @@ export function makeFluidObject< } /** - * Maps CompatibilityMode to a semver valid string that can be passed to the container runtime. + * Maps the legacy {@link CompatibilityMode} string identifiers ("1", "2") to their + * equivalent {@link MinimumVersionForCollab} semver values. */ export const compatibilityModeToMinVersionForCollab = { "1": "1.0.0", "2": "2.0.0", + // eslint-disable-next-line import-x/no-deprecated } as const satisfies Record; +/** + * Resolves the {@link MinimumVersionForCollab} and base runtime options to use for a + * declarative-model container, given a required `compatibilityMode` (which selects the + * runtime defaults) and an optional `minVersionForCollab` overlay. When provided, + * `minVersionForCollab` overrides the version derived from `compatibilityMode`; runtime + * options are still selected by `compatibilityMode`. + * + * TODO: when `CompatibilityMode` is removed, the runtime options (currently derived from + * {@link compatibilityModeRuntimeOptions} keyed by `compatibilityMode`) must replaced. + * + * @internal + */ +export function resolveMinVersionAndRuntimeOptions(input: { + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode; + minVersionForCollab?: MinimumVersionForCollab; +}): { + minVersionForCollab: MinimumVersionForCollab; + runtimeOptions: IContainerRuntimeOptions; +} { + const { compatibilityMode, minVersionForCollab } = input; + return { + minVersionForCollab: + minVersionForCollab ?? compatibilityModeToMinVersionForCollab[compatibilityMode], + runtimeOptions: compatibilityModeRuntimeOptions[compatibilityMode], + }; +} + /** * Determines if the provided schema is a valid tree-based container schema. * @internal diff --git a/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts b/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts index a8176b2ee323..d43c0314a354 100644 --- a/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts +++ b/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts @@ -26,7 +26,7 @@ * For example it might make sense to constrain this to something like `"1.4.0" | typeof defaultMinVersionForCollab | 2.${bigint}.0"`. * * @input - * @beta + * @public */ export type MinimumVersionForCollab = | `${1 | 2}.${bigint}.${bigint}` diff --git a/packages/service-clients/azure-client/package.json b/packages/service-clients/azure-client/package.json index b6e66beec923..662a47a1f1dc 100644 --- a/packages/service-clients/azure-client/package.json +++ b/packages/service-clients/azure-client/package.json @@ -98,6 +98,7 @@ "@fluidframework/driver-utils": "workspace:~", "@fluidframework/fluid-static": "workspace:~", "@fluidframework/routerlicious-driver": "workspace:~", + "@fluidframework/runtime-definitions": "workspace:~", "@fluidframework/telemetry-utils": "workspace:~" }, "devDependencies": { diff --git a/packages/service-clients/azure-client/src/AzureClient.ts b/packages/service-clients/azure-client/src/AzureClient.ts index 7a392714ae7c..9b40ad26cdeb 100644 --- a/packages/service-clients/azure-client/src/AzureClient.ts +++ b/packages/service-clients/azure-client/src/AzureClient.ts @@ -29,6 +29,7 @@ import { applyStorageCompression } from "@fluidframework/driver-utils/internal"; import type { ContainerSchema, IFluidContainer, + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, } from "@fluidframework/fluid-static"; import { @@ -37,6 +38,7 @@ import { createServiceAudience, } from "@fluidframework/fluid-static/internal"; import { RouterliciousDocumentServiceFactory } from "@fluidframework/routerlicious-driver/internal"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; import { wrapConfigProviderWithDefaults } from "@fluidframework/telemetry-utils/internal"; import { createAzureAudienceMember } from "./AzureAudience.js"; @@ -98,9 +100,12 @@ export class AzureClient { private readonly createContainerRuntimeFactory?: ({ schema, compatibilityMode, + minVersionForCollab, }: { schema: ContainerSchema; + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode; + minVersionForCollab?: MinimumVersionForCollab; }) => IRuntimeFactory; /** @@ -138,17 +143,25 @@ export class AzureClient { * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. * (normally not explicitly specified.) * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Compatibility mode the container should run in. + * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. + * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on + * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. * @returns New detached container instance along with associated services. */ public async createContainer( containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab?: MinimumVersionForCollab, ): Promise<{ container: IFluidContainer; services: AzureContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + compatibilityMode, + minVersionForCollab, + ); const container = await createDetachedContainer({ ...loaderProps, @@ -172,18 +185,26 @@ export class AzureClient { * (normally not explicitly specified.) * @param id - Unique ID of the container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Compatibility mode the container should run in. + * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. + * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on + * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. * @returns Existing container instance along with associated services. */ public async getContainer( id: string, containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab?: MinimumVersionForCollab, ): Promise<{ container: IFluidContainer; services: AzureContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + compatibilityMode, + minVersionForCollab, + ); const url = new URL(this.connectionConfig.endpoint); url.searchParams.append("storage", encodeURIComponent(this.connectionConfig.endpoint)); url.searchParams.append( @@ -210,18 +231,26 @@ export class AzureClient { * @param id - Unique ID of the source container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. * @param version - Unique version of the source container in Azure Fluid Relay. - * @param compatibilityMode - Compatibility mode the container should run in. + * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. + * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on + * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. * @returns Loaded container instance at the specified version. */ public async viewContainerVersion( id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab?: MinimumVersionForCollab, ): Promise<{ container: IFluidContainer; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + compatibilityMode, + minVersionForCollab, + ); const url = new URL(this.connectionConfig.endpoint); url.searchParams.append("storage", encodeURIComponent(this.connectionConfig.endpoint)); url.searchParams.append( @@ -286,17 +315,14 @@ export class AzureClient { private getLoaderProps( schema: ContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab: MinimumVersionForCollab | undefined, ): ILoaderProps { + const factoryArguments = { schema, compatibilityMode, minVersionForCollab }; const runtimeFactory = this.createContainerRuntimeFactory - ? this.createContainerRuntimeFactory({ - schema, - compatibilityMode, - }) - : createDOProviderContainerRuntimeFactory({ - schema, - compatibilityMode, - }); + ? this.createContainerRuntimeFactory(factoryArguments) + : createDOProviderContainerRuntimeFactory(factoryArguments); const load = async (): Promise => { return { diff --git a/packages/service-clients/azure-client/src/interfaces.ts b/packages/service-clients/azure-client/src/interfaces.ts index cbc26d467fa2..f159a1b6232a 100644 --- a/packages/service-clients/azure-client/src/interfaces.ts +++ b/packages/service-clients/azure-client/src/interfaces.ts @@ -11,12 +11,14 @@ import type { import type { IUser } from "@fluidframework/driver-definitions"; import type { ICompressionStorageConfig } from "@fluidframework/driver-utils"; import type { + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, ContainerSchema, IMember, IServiceAudience, } from "@fluidframework/fluid-static"; import type { ITokenProvider } from "@fluidframework/routerlicious-driver"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; /** * Props for initializing a new AzureClient instance @@ -51,9 +53,12 @@ export interface AzureClientPropsInternal extends AzureClientProps { readonly createContainerRuntimeFactory?: ({ schema, compatibilityMode, + minVersionForCollab, }: { schema: ContainerSchema; + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode; + minVersionForCollab?: MinimumVersionForCollab; }) => IRuntimeFactory; } diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts index a29919a66985..fa7a9bf39326 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts @@ -20,7 +20,12 @@ import { import type { IRuntimeFactory } from "@fluidframework/container-definitions/legacy"; import type { IConfigProviderBase } from "@fluidframework/core-interfaces"; import { ScopeType } from "@fluidframework/driver-definitions/legacy"; -import type { CompatibilityMode, ContainerSchema } from "@fluidframework/fluid-static"; +import type { + // eslint-disable-next-line import-x/no-deprecated + CompatibilityMode, + ContainerSchema, +} from "@fluidframework/fluid-static"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; import { type MockLogger, createChildLogger, @@ -57,9 +62,12 @@ export function createAzureClient( createContainerRuntimeFactory?: ({ schema, compatibilityMode, + minVersionForCollab, }: { schema: ContainerSchema; + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode; + minVersionForCollab?: MinimumVersionForCollab; }) => IRuntimeFactory, ): AzureClient { const args = process.argv.slice(2); diff --git a/packages/service-clients/tinylicious-client/package.json b/packages/service-clients/tinylicious-client/package.json index 494a3ae9b602..5f581c825889 100644 --- a/packages/service-clients/tinylicious-client/package.json +++ b/packages/service-clients/tinylicious-client/package.json @@ -93,6 +93,7 @@ "@fluidframework/fluid-static": "workspace:~", "@fluidframework/map": "workspace:~", "@fluidframework/routerlicious-driver": "workspace:~", + "@fluidframework/runtime-definitions": "workspace:~", "@fluidframework/runtime-utils": "workspace:~", "@fluidframework/telemetry-utils": "workspace:~", "@fluidframework/tinylicious-driver": "workspace:~" diff --git a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts index 8e30c30194fc..1b7aa727cbb2 100644 --- a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts +++ b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts @@ -22,6 +22,7 @@ import type { import type { ContainerSchema, IFluidContainer, + // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, } from "@fluidframework/fluid-static"; import { @@ -30,6 +31,7 @@ import { createServiceAudience, } from "@fluidframework/fluid-static/internal"; import { RouterliciousDocumentServiceFactory } from "@fluidframework/routerlicious-driver/internal"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; import { wrapConfigProviderWithDefaults } from "@fluidframework/telemetry-utils/internal"; import { InsecureTinyliciousTokenProvider, @@ -72,17 +74,25 @@ export class TinyliciousClient { /** * Creates a new detached container instance in Tinylicious server. * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Compatibility mode the container should run in. + * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. + * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on + * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. * @returns New detached container instance along with associated services. */ public async createContainer( containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab?: MinimumVersionForCollab, ): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + compatibilityMode, + minVersionForCollab, + ); // We're not actually using the code proposal (our code loader always loads the same module // regardless of the proposal), but the Container will only give us a NullRuntime if there's @@ -123,18 +133,26 @@ export class TinyliciousClient { * Accesses the existing container given its unique ID in the tinylicious server. * @param id - Unique ID of the container. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Compatibility mode the container should run in. + * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. + * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on + * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. * @returns Existing container instance along with associated services. */ public async getContainer( id: string, containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab?: MinimumVersionForCollab, ): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + compatibilityMode, + minVersionForCollab, + ); const container = await loadExistingContainer({ ...loaderProps, request: { url: id } }); const fluidContainer = await createFluidContainer({ container, @@ -155,11 +173,14 @@ export class TinyliciousClient { private getLoaderProps( schema: ContainerSchema, + // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode, + minVersionForCollab: MinimumVersionForCollab | undefined, ): ILoaderProps { const containerRuntimeFactory = createDOProviderContainerRuntimeFactory({ schema, compatibilityMode, + minVersionForCollab, }); const load = async (): Promise => { return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd8bcd7dfbd1..657a65fad4c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13736,6 +13736,9 @@ importers: '@fluidframework/routerlicious-driver': specifier: workspace:~ version: link:../../drivers/routerlicious-driver + '@fluidframework/runtime-definitions': + specifier: workspace:~ + version: link:../../runtime/runtime-definitions '@fluidframework/telemetry-utils': specifier: workspace:~ version: link:../../utils/telemetry-utils @@ -14199,6 +14202,9 @@ importers: '@fluidframework/routerlicious-driver': specifier: workspace:~ version: link:../../drivers/routerlicious-driver + '@fluidframework/runtime-definitions': + specifier: workspace:~ + version: link:../../runtime/runtime-definitions '@fluidframework/runtime-utils': specifier: workspace:~ version: link:../../runtime/runtime-utils From 86fa01721d0574bb700ad2da4d2abbab8817afca Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 30 Apr 2026 16:05:56 -0400 Subject: [PATCH 02/18] comments/api reports --- .../fluid-static/api-report/fluid-static.alpha.api.md | 2 +- .../fluid-static/api-report/fluid-static.beta.api.md | 2 +- .../api-report/fluid-static.legacy.beta.api.md | 3 ++- .../api-report/fluid-static.legacy.public.api.md | 2 +- .../fluid-static/api-report/fluid-static.public.api.md | 2 +- packages/framework/fluid-static/src/rootDataObject.ts | 5 +++-- packages/framework/fluid-static/src/treeRootDataObject.ts | 5 +++-- .../azure-client/api-report/azure-client.beta.api.md | 8 ++++---- .../api-report/azure-client.legacy.beta.api.md | 8 ++++---- .../api-report/azure-client.legacy.public.api.md | 8 ++++---- .../azure-client/api-report/azure-client.public.api.md | 8 ++++---- .../api-report/tinylicious-client.alpha.api.md | 4 ++-- .../api-report/tinylicious-client.beta.api.md | 4 ++-- .../api-report/tinylicious-client.public.api.md | 4 ++-- 14 files changed, 34 insertions(+), 31 deletions(-) diff --git a/packages/framework/fluid-static/api-report/fluid-static.alpha.api.md b/packages/framework/fluid-static/api-report/fluid-static.alpha.api.md index 56b3c969c8cb..b43e5f026aa4 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.alpha.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.alpha.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/framework/fluid-static/api-report/fluid-static.beta.api.md b/packages/framework/fluid-static/api-report/fluid-static.beta.api.md index 3b329602039e..a3076610f0d6 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.beta.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.beta.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md index ad89f685e26a..00f487d2b0d5 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public @@ -20,6 +20,7 @@ export interface ContainerSchema { export function createTreeContainerRuntimeFactory(props: { readonly schema: TreeContainerSchema; readonly compatibilityMode: CompatibilityMode; + readonly minVersionForCollab?: MinimumVersionForCollab; readonly rootDataStoreRegistry?: IFluidDataStoreRegistry; readonly runtimeOptionOverrides?: Partial; readonly minVersionForCollabOverride?: MinimumVersionForCollab; diff --git a/packages/framework/fluid-static/api-report/fluid-static.legacy.public.api.md b/packages/framework/fluid-static/api-report/fluid-static.legacy.public.api.md index e6bcd3fc736f..0467bd8f6778 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.legacy.public.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.legacy.public.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/framework/fluid-static/api-report/fluid-static.public.api.md b/packages/framework/fluid-static/api-report/fluid-static.public.api.md index e6bcd3fc736f..0467bd8f6778 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.public.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.public.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index f128e528fad8..67c06d7a4ef4 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -221,8 +221,9 @@ export function createDOProviderContainerRuntimeFactory(props: { */ runtimeOptionOverrides?: Partial; /** - * @deprecated Use `minVersionForCollab` instead. When set, behaves identically to passing - * `minVersionForCollab` directly. + * @deprecated Use `minVersionForCollab` instead. When `minVersionForCollab` is not + * provided, this is used as a fallback; when both are provided, `minVersionForCollab` + * takes precedence. */ minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index 8fdaba48e248..9fea70aa21d2 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -227,8 +227,9 @@ export function createTreeContainerRuntimeFactory(props: { */ readonly runtimeOptionOverrides?: Partial; /** - * @deprecated Use `minVersionForCollab` instead. When set, behaves identically to passing - * `minVersionForCollab` directly (but will override `minVersionForCollab` if both are provided). + * @deprecated Use `minVersionForCollab` instead. When `minVersionForCollab` is not + * provided, this is used as a fallback; when both are provided, `minVersionForCollab` + * takes precedence. */ readonly minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { diff --git a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md index 4ab351c4ddee..25fa2dd4c9cb 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; }>; } @@ -73,7 +73,7 @@ export interface AzureRemoteConnectionConfig extends AzureConnectionConfig { type: "remote"; } -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md index 4ab351c4ddee..25fa2dd4c9cb 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; }>; } @@ -73,7 +73,7 @@ export interface AzureRemoteConnectionConfig extends AzureConnectionConfig { type: "remote"; } -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md index 57c9ead9db3b..25b296466163 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; }>; } @@ -73,7 +73,7 @@ export interface AzureRemoteConnectionConfig extends AzureConnectionConfig { type: "remote"; } -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/service-clients/azure-client/api-report/azure-client.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.public.api.md index 57c9ead9db3b..25b296466163 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.public.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; }>; } @@ -73,7 +73,7 @@ export interface AzureRemoteConnectionConfig extends AzureConnectionConfig { type: "remote"; } -// @public +// @public @deprecated export type CompatibilityMode = "1" | "2"; // @public diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md index 52ea8da37ab4..c4fe83b0c953 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md index d6facf41cd8a..8c0b68f9c585 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md index a9336956c0da..72443e460711 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; From afb3e9e362592fb65fbb64684d7309506e6636b8 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Thu, 30 Apr 2026 16:27:33 -0400 Subject: [PATCH 03/18] api report --- .../api-report/runtime-definitions.beta.api.md | 2 +- .../api-report/runtime-definitions.legacy.alpha.api.md | 2 +- .../api-report/runtime-definitions.legacy.beta.api.md | 2 +- .../api-report/runtime-definitions.legacy.public.api.md | 3 +++ .../api-report/runtime-definitions.public.api.md | 3 +++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/runtime/runtime-definitions/api-report/runtime-definitions.beta.api.md b/packages/runtime/runtime-definitions/api-report/runtime-definitions.beta.api.md index d20a3e70d70d..56d71f69b5b5 100644 --- a/packages/runtime/runtime-definitions/api-report/runtime-definitions.beta.api.md +++ b/packages/runtime/runtime-definitions/api-report/runtime-definitions.beta.api.md @@ -4,7 +4,7 @@ ```ts -// @beta @input +// @public @input export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; // (No @packageDocumentation comment for this package) diff --git a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.alpha.api.md b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.alpha.api.md index 9f18e360b28c..756b235f8efc 100644 --- a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.alpha.api.md +++ b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.alpha.api.md @@ -398,7 +398,7 @@ export interface LocalAttributionKey { type: "local"; } -// @beta @input +// @public @input export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; // @beta @legacy diff --git a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.beta.api.md b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.beta.api.md index 75f077ccfa54..aeee28a6ab04 100644 --- a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.beta.api.md +++ b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.beta.api.md @@ -391,7 +391,7 @@ export interface LocalAttributionKey { type: "local"; } -// @beta @input +// @public @input export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; // @beta @legacy diff --git a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.public.api.md b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.public.api.md index 26348215bc12..8850043fa4ea 100644 --- a/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.public.api.md +++ b/packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.public.api.md @@ -4,6 +4,9 @@ ```ts +// @public @input +export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/runtime/runtime-definitions/api-report/runtime-definitions.public.api.md b/packages/runtime/runtime-definitions/api-report/runtime-definitions.public.api.md index 26348215bc12..8850043fa4ea 100644 --- a/packages/runtime/runtime-definitions/api-report/runtime-definitions.public.api.md +++ b/packages/runtime/runtime-definitions/api-report/runtime-definitions.public.api.md @@ -4,6 +4,9 @@ ```ts +// @public @input +export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`; + // (No @packageDocumentation comment for this package) ``` From 7179ef548876b453d28b736805eb87db507ee8a7 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Fri, 1 May 2026 10:56:55 -0400 Subject: [PATCH 04/18] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/framework/fluid-static/src/rootDataObject.ts | 2 +- packages/framework/fluid-static/src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 67c06d7a4ef4..e964a7f68463 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -201,7 +201,7 @@ export function createDOProviderContainerRuntimeFactory(props: { /** * See {@link CompatibilityMode} for more details. * - * @deprecated Use {@link createDOProviderContainerRuntimeFactory.minVersionForCollab | minVersionForCollab} instead. + * @deprecated Use `minVersionForCollab` instead. */ // eslint-disable-next-line import-x/no-deprecated compatibilityMode: CompatibilityMode; diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index c7ffed78c81e..4980a6ba8df1 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -167,7 +167,7 @@ export const compatibilityModeToMinVersionForCollab = { * options are still selected by `compatibilityMode`. * * TODO: when `CompatibilityMode` is removed, the runtime options (currently derived from - * {@link compatibilityModeRuntimeOptions} keyed by `compatibilityMode`) must replaced. + * {@link compatibilityModeRuntimeOptions} keyed by `compatibilityMode`) must be replaced. * * @internal */ From 5f1483eb73650f8ca6dbb807dc6d427569330234 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 4 May 2026 13:48:18 -0400 Subject: [PATCH 05/18] keep API surface one param --- .../fluid-static.legacy.beta.api.md | 3 +- .../fluid-static/src/rootDataObject.ts | 65 +++++++++---------- .../fluid-static/src/treeRootDataObject.ts | 58 +++++++++-------- packages/framework/fluid-static/src/utils.ts | 45 ++++--------- .../api-report/azure-client.beta.api.md | 6 +- .../azure-client.legacy.beta.api.md | 6 +- .../azure-client.legacy.public.api.md | 6 +- .../api-report/azure-client.public.api.md | 6 +- .../azure-client/src/AzureClient.ts | 57 ++++++---------- .../azure-client/src/interfaces.ts | 4 +- .../azure-client/src/test/AzureClient.spec.ts | 6 +- .../src/test/AzureClientFactory.ts | 4 +- .../azure-client/src/test/audience.spec.ts | 38 ++++++----- .../src/test/containerCreate.spec.ts | 46 ++++++------- .../azure-client/src/test/ddsTests.spec.ts | 40 +++++++----- .../src/test/multiprocess/childClient.tool.ts | 8 ++- .../azure-client/src/test/signals.spec.ts | 6 +- .../azure-client/src/test/tree.spec.ts | 14 ++-- .../src/test/viewContainerVersion.spec.ts | 16 ++--- .../odsp-client/src/odspClient.ts | 2 +- .../tinylicious-client.alpha.api.md | 4 +- .../api-report/tinylicious-client.beta.api.md | 4 +- .../tinylicious-client.public.api.md | 4 +- .../src/TinyliciousClient.ts | 36 ++++------ .../src/test/TinyliciousClient.spec.ts | 2 +- 25 files changed, 230 insertions(+), 256 deletions(-) diff --git a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md index 00f487d2b0d5..daaf74ade5eb 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md @@ -19,8 +19,7 @@ export interface ContainerSchema { // @beta @legacy export function createTreeContainerRuntimeFactory(props: { readonly schema: TreeContainerSchema; - readonly compatibilityMode: CompatibilityMode; - readonly minVersionForCollab?: MinimumVersionForCollab; + readonly compatibilityMode: MinimumVersionForCollab | CompatibilityMode; readonly rootDataStoreRegistry?: IFluidDataStoreRegistry; readonly runtimeOptionOverrides?: Partial; readonly minVersionForCollabOverride?: MinimumVersionForCollab; diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index e964a7f68463..970686a0af0c 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -31,6 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; +import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -48,7 +49,7 @@ import { isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, - resolveMinVersionAndRuntimeOptions, + resolveCompatibilityInput, } from "./utils.js"; /** @@ -188,9 +189,6 @@ async function provideEntryPoint( * The root data object's registry and initial objects are configured based on the provided * schema (and optionally, data store registry). * - * `compatibilityMode` (deprecated) selects the runtime defaults. `minVersionForCollab` is an - * optional overlay; when provided, it overrides the version derived from `compatibilityMode`. - * * @internal */ export function createDOProviderContainerRuntimeFactory(props: { @@ -199,18 +197,13 @@ export function createDOProviderContainerRuntimeFactory(props: { */ schema: ContainerSchema; /** - * See {@link CompatibilityMode} for more details. - * - * @deprecated Use `minVersionForCollab` instead. + * Minimum framework version required for collaboration. Accepts a + * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string; + * the legacy {@link CompatibilityMode} values `"1"` and `"2"` are **deprecated** + * equivalents of `"1.0.0"` and `"2.0.0"`. */ // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode; - /** - * Minimum framework version required for collaboration on the document. - * Replaces `compatibilityMode`; aligns the declarative model with the encapsulated model's - * `minVersionForCollab` configuration. - */ - minVersionForCollab?: MinimumVersionForCollab; + compatibilityMode: MinimumVersionForCollab | CompatibilityMode; /** * Optional registry of data stores to pass to the DataObject factory. * If not provided, one will be created based on the schema. @@ -218,39 +211,39 @@ export function createDOProviderContainerRuntimeFactory(props: { rootDataStoreRegistry?: IFluidDataStoreRegistry; /** * Optional overrides for the container runtime options. + * If not provided, only the default options for the given compatibilityMode will be used. */ runtimeOptionOverrides?: Partial; /** - * @deprecated Use `minVersionForCollab` instead. When `minVersionForCollab` is not - * provided, this is used as a fallback; when both are provided, `minVersionForCollab` - * takes precedence. + * Optional override for minimum version for collab. + * If not provided, the default for the given compatibilityMode will be used. + * @remarks + * This is useful when runtime options are overridden and change the minimum version for collab. + * + * @deprecated Pass a {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} + * semver string directly via `compatibilityMode` instead. */ minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { const { - compatibilityMode, - minVersionForCollab, minVersionForCollabOverride, rootDataStoreRegistry, runtimeOptionOverrides, schema, } = props; - - const { minVersionForCollab: resolvedMinVersion, runtimeOptions: baseRuntimeOptions } = - resolveMinVersionAndRuntimeOptions({ - compatibilityMode, - minVersionForCollab: minVersionForCollab ?? minVersionForCollabOverride, - }); - + const { compatibilityMode, minVersionForCollab } = resolveCompatibilityInput( + props.compatibilityMode, + ); const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema); const registry = rootDataStoreRegistry ?? new FluidDataStoreRegistry(registryEntries); return new DOProviderContainerRuntimeFactory( schema, + compatibilityMode, new RootDataObjectFactory(sharedObjects, registry), { - runtimeOptions: { ...baseRuntimeOptions, ...runtimeOptionOverrides }, - minVersionForCollab: resolvedMinVersion, + runtimeOptions: runtimeOptionOverrides, + minVersionForCollab: minVersionForCollabOverride ?? minVersionForCollab, }, ); } @@ -280,25 +273,31 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { * since it can take care of constructing the root data object factory based on the schema. * * @param schema - The schema for the container + * @param compatibilityMode - Compatibility mode * @param rootDataObjectFactory - A factory that can construct the root data object. - * @param resolvedConfig - Pre-resolved runtime options and minimum version for collab. + * @param config - Resolved minimum version for collab (required) and optional runtime option overrides. */ public constructor( schema: ContainerSchema, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, rootDataObjectFactory: DataObjectFactory< RootDataObject, { InitialState: RootDataObjectProps } >, - resolvedConfig: { - runtimeOptions: Partial; + config: { minVersionForCollab: MinimumVersionForCollab; + runtimeOptions?: Partial; }, ) { super({ registryEntries: [rootDataObjectFactory.registryEntry], - runtimeOptions: resolvedConfig.runtimeOptions, + runtimeOptions: { + ...compatibilityModeRuntimeOptions[compatibilityMode], + ...config.runtimeOptions, + }, provideEntryPoint, - minVersionForCollab: resolvedConfig.minVersionForCollab, + minVersionForCollab: config.minVersionForCollab, }); this.rootDataObjectFactory = rootDataObjectFactory; this.initialObjects = schema.initialObjects; diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index 9fea70aa21d2..368ee2528c63 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -31,6 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; +import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -47,7 +48,7 @@ import { isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, - resolveMinVersionAndRuntimeOptions, + resolveCompatibilityInput, } from "./utils.js"; /** @@ -135,17 +136,22 @@ class TreeContainerRuntimeFactory extends BaseContainerRuntimeFactory { readonly #treeRootDataObjectFactory: TreeDataObjectFactory; public constructor( + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, treeRootDataObjectFactory: TreeDataObjectFactory, - resolvedConfig: { - runtimeOptions: Partial; + config: { minVersionForCollab: MinimumVersionForCollab; + runtimeOptions?: Partial; }, ) { super({ registryEntries: [treeRootDataObjectFactory.registryEntry], - runtimeOptions: resolvedConfig.runtimeOptions, + runtimeOptions: { + ...compatibilityModeRuntimeOptions[compatibilityMode], + ...config.runtimeOptions, + }, provideEntryPoint, - minVersionForCollab: resolvedConfig.minVersionForCollab, + minVersionForCollab: config.minVersionForCollab, }); this.#treeRootDataObjectFactory = treeRootDataObjectFactory; } @@ -205,18 +211,13 @@ export function createTreeContainerRuntimeFactory(props: { readonly schema: TreeContainerSchema; /** - * See {@link CompatibilityMode} for more details. - * - * @deprecated Use `minVersionForCollab` instead. + * Minimum framework version required for collaboration. Accepts a + * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string; + * the legacy {@link CompatibilityMode} values `"1"` and `"2"` are **deprecated** + * equivalents of `"1.0.0"` and `"2.0.0"`. */ // eslint-disable-next-line import-x/no-deprecated - readonly compatibilityMode: CompatibilityMode; - /** - * Minimum framework version required for collaboration on the document. - * Replaces `compatibilityMode`; aligns the declarative model with the encapsulated model's - * `minVersionForCollab` configuration. - */ - readonly minVersionForCollab?: MinimumVersionForCollab; + readonly compatibilityMode: MinimumVersionForCollab | CompatibilityMode; /** * Optional registry of data stores to pass to the DataObject factory. * If not provided, one will be created based on the schema. @@ -224,38 +225,39 @@ export function createTreeContainerRuntimeFactory(props: { readonly rootDataStoreRegistry?: IFluidDataStoreRegistry; /** * Optional overrides for the container runtime options. + * If not provided, only the default options for the given compatibilityMode will be used. */ readonly runtimeOptionOverrides?: Partial; /** - * @deprecated Use `minVersionForCollab` instead. When `minVersionForCollab` is not - * provided, this is used as a fallback; when both are provided, `minVersionForCollab` - * takes precedence. + * Optional override for minimum version for collab. + * If not provided, the default for the given compatibilityMode will be used. + * @remarks + * This is useful when runtime options are overridden and change the minimum version for collab. + * + * @deprecated Pass a {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} + * semver string directly via `compatibilityMode` instead. */ readonly minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { const { - compatibilityMode, - minVersionForCollab, minVersionForCollabOverride, rootDataStoreRegistry, runtimeOptionOverrides, schema, } = props; - - const { minVersionForCollab: resolvedMinVersion, runtimeOptions: baseRuntimeOptions } = - resolveMinVersionAndRuntimeOptions({ - compatibilityMode, - minVersionForCollab: minVersionForCollab ?? minVersionForCollabOverride, - }); + const { compatibilityMode, minVersionForCollab } = resolveCompatibilityInput( + props.compatibilityMode, + ); const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema); const registry = rootDataStoreRegistry ?? new FluidDataStoreRegistry(registryEntries); return new TreeContainerRuntimeFactory( + compatibilityMode, new TreeRootDataObjectFactory(sharedObjects, registry), { - runtimeOptions: { ...baseRuntimeOptions, ...runtimeOptionOverrides }, - minVersionForCollab: resolvedMinVersion, + runtimeOptions: runtimeOptionOverrides, + minVersionForCollab: minVersionForCollabOverride ?? minVersionForCollab, }, ); } diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index 4980a6ba8df1..f992915b6656 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -4,7 +4,6 @@ */ import type { DataObjectKind } from "@fluidframework/aqueduct/internal"; -import type { IContainerRuntimeOptions } from "@fluidframework/container-runtime/internal"; import type { FluidObjectKeys, IFluidLoadable } from "@fluidframework/core-interfaces"; import { oob } from "@fluidframework/core-utils/internal"; import type { @@ -20,7 +19,6 @@ import type { ISharedObjectKind } from "@fluidframework/shared-object-base/inter import { UsageError } from "@fluidframework/telemetry-utils/internal"; import { SharedTreeFactoryType } from "@fluidframework/tree/internal"; -import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -150,40 +148,25 @@ export function makeFluidObject< } /** - * Maps the legacy {@link CompatibilityMode} string identifiers ("1", "2") to their - * equivalent {@link MinimumVersionForCollab} semver values. - */ -export const compatibilityModeToMinVersionForCollab = { - "1": "1.0.0", - "2": "2.0.0", - // eslint-disable-next-line import-x/no-deprecated -} as const satisfies Record; - -/** - * Resolves the {@link MinimumVersionForCollab} and base runtime options to use for a - * declarative-model container, given a required `compatibilityMode` (which selects the - * runtime defaults) and an optional `minVersionForCollab` overlay. When provided, - * `minVersionForCollab` overrides the version derived from `compatibilityMode`; runtime - * options are still selected by `compatibilityMode`. - * - * TODO: when `CompatibilityMode` is removed, the runtime options (currently derived from - * {@link compatibilityModeRuntimeOptions} keyed by `compatibilityMode`) must be replaced. + * Outermost-boundary filter that resolves the unified `compatibilityMode` input — accepting + * either a {@link MinimumVersionForCollab} semver string or a legacy {@link CompatibilityMode} + * value — into the components used internally: a precise `minVersionForCollab` and a + * {@link CompatibilityMode} key (derived from the major version) for indexing + * {@link compatibilityModeRuntimeOptions}. * * @internal */ -export function resolveMinVersionAndRuntimeOptions(input: { +export function resolveCompatibilityInput( + // eslint-disable-next-line import-x/no-deprecated + input: MinimumVersionForCollab | CompatibilityMode, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode; - minVersionForCollab?: MinimumVersionForCollab; -}): { - minVersionForCollab: MinimumVersionForCollab; - runtimeOptions: IContainerRuntimeOptions; -} { - const { compatibilityMode, minVersionForCollab } = input; +): { minVersionForCollab: MinimumVersionForCollab; compatibilityMode: CompatibilityMode } { + // TODO: Checking for "1" and "2" can be removed once CompatibilityMode is removed. + const minVersionForCollab: MinimumVersionForCollab = + input === "1" ? "1.0.0" : input === "2" ? "2.0.0" : input; return { - minVersionForCollab: - minVersionForCollab ?? compatibilityModeToMinVersionForCollab[compatibilityMode], - runtimeOptions: compatibilityModeRuntimeOptions[compatibilityMode], + minVersionForCollab, + compatibilityMode: minVersionForCollab.startsWith("1.") ? "1" : "2", }; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md index 25fa2dd4c9cb..b586e38af11d 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md index 25fa2dd4c9cb..b586e38af11d 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md index 25b296466163..60fcadf641fe 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.public.api.md index 25b296466163..60fcadf641fe 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.public.api.md @@ -7,16 +7,16 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/src/AzureClient.ts b/packages/service-clients/azure-client/src/AzureClient.ts index 9b40ad26cdeb..9204f8fa152a 100644 --- a/packages/service-clients/azure-client/src/AzureClient.ts +++ b/packages/service-clients/azure-client/src/AzureClient.ts @@ -100,12 +100,10 @@ export class AzureClient { private readonly createContainerRuntimeFactory?: ({ schema, compatibilityMode, - minVersionForCollab, }: { schema: ContainerSchema; // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode; - minVersionForCollab?: MinimumVersionForCollab; + compatibilityMode: MinimumVersionForCollab | CompatibilityMode; }) => IRuntimeFactory; /** @@ -143,25 +141,21 @@ export class AzureClient { * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. * (normally not explicitly specified.) * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. - * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on - * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. + * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy + * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and + * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. * @returns New detached container instance along with associated services. */ public async createContainer( containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab?: MinimumVersionForCollab, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): Promise<{ container: IFluidContainer; services: AzureContainerServices; }> { - const loaderProps = this.getLoaderProps( - containerSchema, - compatibilityMode, - minVersionForCollab, - ); + const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); const container = await createDetachedContainer({ ...loaderProps, @@ -185,26 +179,22 @@ export class AzureClient { * (normally not explicitly specified.) * @param id - Unique ID of the container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. - * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on - * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. + * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy + * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and + * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. * @returns Existing container instance along with associated services. */ public async getContainer( id: string, containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab?: MinimumVersionForCollab, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): Promise<{ container: IFluidContainer; services: AzureContainerServices; }> { - const loaderProps = this.getLoaderProps( - containerSchema, - compatibilityMode, - minVersionForCollab, - ); + const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); const url = new URL(this.connectionConfig.endpoint); url.searchParams.append("storage", encodeURIComponent(this.connectionConfig.endpoint)); url.searchParams.append( @@ -231,9 +221,10 @@ export class AzureClient { * @param id - Unique ID of the source container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. * @param version - Unique version of the source container in Azure Fluid Relay. - * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. - * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on - * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. + * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy + * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and + * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. * @returns Loaded container instance at the specified version. */ public async viewContainerVersion( @@ -241,16 +232,11 @@ export class AzureClient { containerSchema: TContainerSchema, version: AzureContainerVersion, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab?: MinimumVersionForCollab, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): Promise<{ container: IFluidContainer; }> { - const loaderProps = this.getLoaderProps( - containerSchema, - compatibilityMode, - minVersionForCollab, - ); + const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); const url = new URL(this.connectionConfig.endpoint); url.searchParams.append("storage", encodeURIComponent(this.connectionConfig.endpoint)); url.searchParams.append( @@ -316,10 +302,9 @@ export class AzureClient { private getLoaderProps( schema: ContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab: MinimumVersionForCollab | undefined, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): ILoaderProps { - const factoryArguments = { schema, compatibilityMode, minVersionForCollab }; + const factoryArguments = { schema, compatibilityMode }; const runtimeFactory = this.createContainerRuntimeFactory ? this.createContainerRuntimeFactory(factoryArguments) : createDOProviderContainerRuntimeFactory(factoryArguments); diff --git a/packages/service-clients/azure-client/src/interfaces.ts b/packages/service-clients/azure-client/src/interfaces.ts index f159a1b6232a..36381311fbfe 100644 --- a/packages/service-clients/azure-client/src/interfaces.ts +++ b/packages/service-clients/azure-client/src/interfaces.ts @@ -53,12 +53,10 @@ export interface AzureClientPropsInternal extends AzureClientProps { readonly createContainerRuntimeFactory?: ({ schema, compatibilityMode, - minVersionForCollab, }: { schema: ContainerSchema; // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode; - minVersionForCollab?: MinimumVersionForCollab; + compatibilityMode: MinimumVersionForCollab | CompatibilityMode; }) => IRuntimeFactory; } 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 20875f56d72d..c3845fdfad4e 100644 --- a/packages/service-clients/azure-client/src/test/AzureClient.spec.ts +++ b/packages/service-clients/azure-client/src/test/AzureClient.spec.ts @@ -76,7 +76,7 @@ const connectionModeOf = (container: IFluidContainer): ConnectionMode => { return getContainerConnectionMode(container.container); }; -for (const compatibilityMode of ["1", "2"] as const) { +for (const compatibilityMode of ["1.0.0", "2.0.0"] as const) { describe(`AzureClient (compatibilityMode: ${compatibilityMode})`, function () { const connectTimeoutMs = 1000; let client: AzureClient; @@ -363,7 +363,7 @@ for (const compatibilityMode of ["1", "2"] as const) { it("preserves 'SharedTree' type", async function () { // SharedTree is not supported in compatibilityMode "1", because it requires idCompressor to be enabled. - if (compatibilityMode === "1") { + if (compatibilityMode === "1.0.0") { this.skip(); } const { container } = await client.createContainer( @@ -438,7 +438,7 @@ for (const compatibilityMode of ["1", "2"] as const) { } as const satisfies ContainerRuntimeOptionsInternal; const expectedRuntimeOptions = - compatibilityMode === "1" ? expectedRuntimeOptions1 : expectedRuntimeOptions2; + compatibilityMode === "1.0.0" ? expectedRuntimeOptions1 : expectedRuntimeOptions2; assert(isInternalFluidContainer(container_defaultConfig)); const actualRuntimeOptions = getRuntimeOptions( getContainerRuntime(container_defaultConfig.container), diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts index fa7a9bf39326..6b6c4846ea69 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/AzureClientFactory.ts @@ -62,12 +62,10 @@ export function createAzureClient( createContainerRuntimeFactory?: ({ schema, compatibilityMode, - minVersionForCollab, }: { schema: ContainerSchema; // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode; - minVersionForCollab?: MinimumVersionForCollab; + compatibilityMode: MinimumVersionForCollab | CompatibilityMode; }) => IRuntimeFactory, ): AzureClient { const args = process.argv.slice(2); diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts index 821832d4a872..755172d344c5 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts @@ -54,9 +54,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2")); + ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container, services } = await client.createContainer(schema, "2")); + ({ container, services } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -99,9 +99,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2")); + ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container, services } = await client.createContainer(schema, "2")); + ({ container, services } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -131,7 +131,11 @@ for (const testOpts of testMatrix) { "Fluid.Container.ForceWriteConnection": true, }), ); - const { services: servicesGet } = await client2.getContainer(containerId, schema, "2"); + const { services: servicesGet } = await client2.getContainer( + containerId, + schema, + "2.0.0", + ); /* This is a workaround for a known bug, we should have one member (self) upon container connection */ const partner = await waitForMember(servicesGet.audience, "test-user-id-2"); @@ -163,9 +167,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -184,7 +188,11 @@ for (const testOpts of testMatrix) { "Fluid.Container.ForceWriteConnection": true, }), ); - const { services: servicesGet } = await client2.getContainer(containerId, schema, "2"); + const { services: servicesGet } = await client2.getContainer( + containerId, + schema, + "2.0.0", + ); /* This is a workaround for a known bug, we should have one member (self) upon container connection */ const partner = await waitForMember(servicesGet.audience, "test-user-id-2"); @@ -222,9 +230,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2")); + ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container, services } = await client.createContainer(schema, "2")); + ({ container, services } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -254,7 +262,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer, services: partnerServices } = - await partnerClient.getContainer(containerId, schema, "2"); + await partnerClient.getContainer(containerId, schema, "2.0.0"); if (partnerContainer.connectionState !== ConnectionState.Connected) { await timeoutPromise( @@ -331,9 +339,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -352,7 +360,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer, services: partnerServices } = - await partnerClient.getContainer(containerId, schema, "2"); + await partnerClient.getContainer(containerId, schema, "2.0.0"); if (partnerContainer.connectionState !== ConnectionState.Connected) { await timeoutPromise( @@ -398,7 +406,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer2, services: partnerServices2 } = - await partnerClient2.getContainer(containerId, schema, "2"); + await partnerClient2.getContainer(containerId, schema, "2.0.0"); if (partnerContainer2.connectionState !== ConnectionState.Connected) { await timeoutPromise( diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts index 9207a3c9accf..a763c9ec0dc8 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts @@ -73,7 +73,7 @@ for (const testOpts of testMatrix) { if (isEphemeral) { this.skip(); } - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); assert.strictEqual( container.attachState, AttachState.Detached, @@ -101,9 +101,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -138,9 +138,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -181,7 +181,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container: newContainer } = await client.createContainer(schema, "2")); + ({ container: newContainer } = await client.createContainer(schema, "2.0.0")); containerId = await newContainer.attach(); if (newContainer.connectionState !== ConnectionState.Connected) { @@ -192,7 +192,7 @@ for (const testOpts of testMatrix) { } } - const resources = client.getContainer(containerId, schema, "2"); + const resources = client.getContainer(containerId, schema, "2.0.0"); await assert.doesNotReject( resources, () => true, @@ -210,7 +210,7 @@ for (const testOpts of testMatrix) { it("cannot load improperly created container (cannot load a non-existent container)", async () => { const consoleErrorFn = console.error; console.error = (): void => {}; - const containerAndServicesP = client.getContainer("containerConfig", schema, "2"); + const containerAndServicesP = client.getContainer("containerConfig", schema, "2.0.0"); const errorFn = (error: Error): boolean => { assert.notStrictEqual(error.message, undefined, "Azure Client error is undefined"); @@ -266,7 +266,7 @@ for (const testOpts of testMatrix) { if (isEphemeral) { this.skip(); } - await client.createContainer(schema, "2"); + await client.createContainer(schema, "2.0.0"); const event = mockLogger.events.find((e) => e.eventName.endsWith("ContainerLoadStats")); assert(event !== undefined, "ContainerLoadStats event should exist"); const featureGates = event.featureGates as string; @@ -310,7 +310,7 @@ for (const testOpts of testMatrix) { * Expected behavior: an error should not be thrown nor should a rejected promise * be returned. */ - for (const compatibilityMode of ["1", "2"] as const) { + for (const compatibilityMode of ["1.0.0", "2.0.0"] as const) { it(`Current AzureClient (mode: "${compatibilityMode}") can get container made by legacy AzureClient`, async () => { const { container: containerLegacy } = await clientLegacy.createContainer(schemaLegacy); @@ -348,7 +348,7 @@ for (const testOpts of testMatrix) { // Await the value being saved, especially important if we dispose the legacy container. await valueSetP; - if (compatibilityMode === "2") { + if (compatibilityMode === "2.0.0") { // We don't support interop between legacy containers and "2" mode, dispose the legacy // container to avoid this case. containerLegacy.dispose(); @@ -429,7 +429,7 @@ for (const testOpts of testMatrix) { const { container: containerCurrent } = await clientCurrent1.createContainer( schemaCurrent, // Note: Only containers created in compatibility mode "1" may be loaded by legacy client. - "1", + "1.0.0", ); const containerId = await containerCurrent.attach(); @@ -470,7 +470,7 @@ for (const testOpts of testMatrix) { it(`Current AzureClient (mode: "2") can get container made by current AzureClient (mode: "1")`, async () => { const { container: containerCurrent1 } = await clientCurrent1.createContainer( schemaCurrent, - "1", + "1.0.0", ); const containerId = await containerCurrent1.attach(); @@ -486,7 +486,7 @@ for (const testOpts of testMatrix) { containerCurrent1.initialObjects.map1.set("key", "value"); - const resources = clientCurrent2.getContainer(containerId, schemaCurrent, "2"); + const resources = clientCurrent2.getContainer(containerId, schemaCurrent, "2.0.0"); await assert.doesNotReject(resources, () => true, "container could not be loaded"); const { container: containerCurrent2 } = await resources; @@ -512,7 +512,7 @@ for (const testOpts of testMatrix) { it(`Current AzureClient (mode: "1") can get container made by current AzureClient (mode: "2")`, async () => { const { container: containerCurrent2 } = await clientCurrent2.createContainer( schemaCurrent, - "2", + "2.0.0", ); const containerId = await containerCurrent2.attach(); @@ -528,7 +528,7 @@ for (const testOpts of testMatrix) { containerCurrent2.initialObjects.map1.set("key", "value"); - const resources = clientCurrent1.getContainer(containerId, schemaCurrent, "1"); + const resources = clientCurrent1.getContainer(containerId, schemaCurrent, "1.0.0"); await assert.doesNotReject(resources, () => true, "container could not be loaded"); const { container: containerCurrent1 } = await resources; @@ -549,7 +549,7 @@ for (const testOpts of testMatrix) { it("op grouping disabled as expected for 1.x clients", async () => { const { container: container1 } = await clientCurrent1.createContainer( schemaCurrent, - "1", + "1.0.0", ); const containerId = await container1.attach(); @@ -605,7 +605,7 @@ for (const testOpts of testMatrix) { } }); - for (const compatibilityMode of ["1", "2"] as const) { + for (const compatibilityMode of ["1.0.0", "2.0.0"] as const) { it(`op grouping works as expected (compatibilityMode: ${compatibilityMode})`, async () => { const { container: container1 } = await clientCurrent1.createContainer( schemaCurrent, @@ -643,8 +643,8 @@ for (const testOpts of testMatrix) { const map2 = container2.initialObjects.map1; // Process ops coming from service - assert.strictEqual(await mapWait(map2, "1"), 1); - assert.strictEqual(await mapWait(map2, "2"), 2); + assert.strictEqual(await mapWait(map2, "1.0.0"), 1); + assert.strictEqual(await mapWait(map2, "2.0.0"), 2); assert.strictEqual(await mapWait(map2, "3"), 3); // Inspect the incoming ops @@ -660,7 +660,7 @@ for (const testOpts of testMatrix) { } } - if (compatibilityMode === "1") { + if (compatibilityMode === "1.0.0") { assert.strictEqual( groupedBatchCount, 0, @@ -706,7 +706,7 @@ describe("Container create in tree-only mode", () => { tree: SharedTree, }, }; - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); assert(SharedTree.is(container.initialObjects.tree)); }); @@ -721,7 +721,7 @@ describe("Container create in tree-only mode", () => { }; await assert.rejects( - client.createContainer(schema, "2"), + client.createContainer(schema, "2.0.0"), (error: unknown) => { assert(error instanceof UsageError); assert.strictEqual(error.message, invalidSchemaErrorMessage); diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts index c8dfdc2650d9..5e60c0959b97 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts @@ -54,9 +54,13 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container: newContainer } = await client.getContainer(containerId, schema, "2")); + ({ container: newContainer } = await client.getContainer( + containerId, + schema, + "2.0.0", + )); } else { - ({ container: newContainer } = await client.createContainer(schema, "2")); + ({ container: newContainer } = await client.createContainer(schema, "2.0.0")); containerId = await newContainer.attach(); } @@ -67,7 +71,7 @@ for (const testOpts of testMatrix) { }); } - const resources = client.getContainer(containerId, schema, "2"); + const resources = client.getContainer(containerId, schema, "2.0.0"); await assert.doesNotReject( resources, () => true, @@ -97,9 +101,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -115,7 +119,11 @@ for (const testOpts of testMatrix) { map1Create.set("new-key", "new-value"); const valueCreate: string | undefined = map1Create.get("new-key"); - const { container: containerGet } = await client.getContainer(containerId, schema, "2"); + const { container: containerGet } = await client.getContainer( + containerId, + schema, + "2.0.0", + ); const map1Get = containerGet.initialObjects.map1; const valueGet: string | undefined = await mapWait(map1Get, "new-key"); assert.strictEqual(valueGet, valueCreate, "container can't change initial objects"); @@ -142,9 +150,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, doSchema, "2")); + ({ container } = await client.getContainer(containerId, doSchema, "2.0.0")); } else { - ({ container } = await client.createContainer(doSchema, "2")); + ({ container } = await client.createContainer(doSchema, "2.0.0")); containerId = await container.attach(); } @@ -168,7 +176,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2", + "2.0.0", ); const initialObjectsGet = containerGet.initialObjects; assert( @@ -205,9 +213,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, doSchema, "2")); + ({ container } = await client.getContainer(containerId, doSchema, "2.0.0")); } else { - ({ container } = await client.createContainer(doSchema, "2")); + ({ container } = await client.createContainer(doSchema, "2.0.0")); containerId = await container.attach(); } @@ -235,7 +243,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2", + "2.0.0", ); const initialObjectsGet = containerGet.initialObjects; assert( @@ -274,7 +282,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container } = await client.createContainer(doSchema, "2")); + ({ container } = await client.createContainer(doSchema, "2.0.0")); const initialObjectsCreate = container.initialObjects; const mdo2 = initialObjectsCreate.mdo2 as CounterTestDataObject; @@ -297,7 +305,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2", + "2.0.0", ); const initialObjectsGet = containerGet.initialObjects; const mdo2get = initialObjectsGet.mdo2 as CounterTestDataObject; @@ -333,9 +341,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, dynamicSchema, "2")); + ({ container } = await client.getContainer(containerId, dynamicSchema, "2.0.0")); } else { - ({ container } = await client.createContainer(dynamicSchema, "2")); + ({ container } = await client.createContainer(dynamicSchema, "2.0.0")); containerId = await container.attach(); } diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts index d8574fa2329a..1d94eef0c3b7 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts @@ -145,10 +145,14 @@ const getOrCreateContainer = async (params: { }); let services: AzureContainerServices; if (containerId === undefined) { - ({ container, services } = await client.createContainer(containerSchema, "2")); + ({ container, services } = await client.createContainer(containerSchema, "2.0.0")); containerId = await container.attach(); } else { - ({ container, services } = await client.getContainer(containerId, containerSchema, "2")); + ({ container, services } = await client.getContainer( + containerId, + containerSchema, + "2.0.0", + )); } container.on("disconnected", onDisconnected); diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts index 098699380195..d770c1ee77a0 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts @@ -108,14 +108,14 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2")); + ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container, services } = await client.createContainer(schema, "2")); + ({ container, services } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } } else { containerId = id; - ({ container, services } = await client.getContainer(containerId, schema, "2")); + ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); } if (container.connectionState !== ConnectionState.Connected) { diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts index 10a3a2042f9f..a5d798e9227d 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts @@ -87,7 +87,7 @@ for (const testOpts of testMatrix) { let treeData: TreeView; if (summaryTree === undefined) { - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); treeData = container.initialObjects.tree1.viewWith(treeConfiguration); treeData.initialize(new StringArray([])); containerId = await container.attach(); @@ -100,7 +100,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); - const { container } = await client.getContainer(containerId, schema, "2"); + const { container } = await client.getContainer(containerId, schema, "2.0.0"); treeData = container.initialObjects.tree1.viewWith(treeConfiguration); await waitForConnection(container); } @@ -137,7 +137,7 @@ for (const testOpts of testMatrix) { treeData.root.insertNew("test string 1"); - const resources = client.getContainer(containerId, schema, "2"); + const resources = client.getContainer(containerId, schema, "2.0.0"); await assert.doesNotReject( resources, () => true, @@ -172,7 +172,7 @@ for (const testOpts of testMatrix) { }) {} it("can read and edit data", async () => { - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }), @@ -213,7 +213,7 @@ for (const testOpts of testMatrix) { }); it("can handle undo/redo and transactions", async () => { - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); await container.attach(); const view = asAlpha( container.initialObjects.tree1.viewWith( @@ -262,7 +262,7 @@ for (const testOpts of testMatrix) { it("can use identifiers and the static Tree APIs", async () => { class Widget extends sf.object("Widget", { id: sf.identifier }) {} - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ @@ -297,7 +297,7 @@ for (const testOpts of testMatrix) { }) {} allowUnused>(); - const { container } = await client.createContainer(schema, "2"); + const { container } = await client.createContainer(schema, "2.0.0"); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ schema: Doll, enableSchemaValidation: true }), diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts index 6c02cdc15440..4e0e499a5b70 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts @@ -63,9 +63,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); } @@ -126,7 +126,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); containerId = await container.attach(); if (container.connectionState !== ConnectionState.Connected) { @@ -143,7 +143,7 @@ for (const testOpts of testMatrix) { containerId, schema, versions[0], - "2", + "2.0.0", ); await assert.doesNotReject(viewContainerVersionAttempt); const { container: containerView } = await viewContainerVersionAttempt; @@ -169,10 +169,10 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2")); + ({ container } = await client.getContainer(containerId, schema, "2.0.0")); map1 = container.initialObjects.map1 as SharedMap; } else { - ({ container } = await client.createContainer(schema, "2")); + ({ container } = await client.createContainer(schema, "2.0.0")); map1 = container.initialObjects.map1 as SharedMap; map1.set(testKey, expectedValue); @@ -197,7 +197,7 @@ for (const testOpts of testMatrix) { containerId, schema, versions[versions.length - 1], - "2", + "2.0.0", ); await assert.doesNotReject(viewContainerVersionAttempt); const { container: containerView } = await viewContainerVersionAttempt; @@ -216,7 +216,7 @@ for (const testOpts of testMatrix) { { id: "whatever", }, - "2", + "2.0.0", ); const errorFn = (error: Error): boolean => { assert.notStrictEqual(error.message, undefined, "Azure Client error is undefined"); diff --git a/packages/service-clients/odsp-client/src/odspClient.ts b/packages/service-clients/odsp-client/src/odspClient.ts index db8690c54095..1f5868169abd 100644 --- a/packages/service-clients/odsp-client/src/odspClient.ts +++ b/packages/service-clients/odsp-client/src/odspClient.ts @@ -166,7 +166,7 @@ export class OdspClient { private getLoaderProps(schema: ContainerSchema): ILoaderProps { const runtimeFactory = createDOProviderContainerRuntimeFactory({ schema, - compatibilityMode: "2", + compatibilityMode: "2.0.0", }); const load = async (): Promise => { return { diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md index c4fe83b0c953..a0ad08f545ac 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md index 8c0b68f9c585..1c4f1237ef04 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md index 72443e460711..cd30b58e584e 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md @@ -12,11 +12,11 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode, minVersionForCollab?: MinimumVersionForCollab): Promise<{ + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts index 1b7aa727cbb2..e832cce1a501 100644 --- a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts +++ b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts @@ -74,25 +74,21 @@ export class TinyliciousClient { /** * Creates a new detached container instance in Tinylicious server. * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. - * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on - * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. + * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy + * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and + * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. * @returns New detached container instance along with associated services. */ public async createContainer( containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab?: MinimumVersionForCollab, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps( - containerSchema, - compatibilityMode, - minVersionForCollab, - ); + const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); // We're not actually using the code proposal (our code loader always loads the same module // regardless of the proposal), but the Container will only give us a NullRuntime if there's @@ -133,26 +129,22 @@ export class TinyliciousClient { * Accesses the existing container given its unique ID in the tinylicious server. * @param id - Unique ID of the container. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Compatibility mode the container should run in. Deprecated in favor of `minVersionForCollab`. - * @param minVersionForCollab - Optional minimum Fluid Framework version required for collaboration on - * the document. When provided, takes precedence over `compatibilityMode`. Will be made required in the future when `compatibilityMode` is removed. + * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy + * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and + * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. * @returns Existing container instance along with associated services. */ public async getContainer( id: string, containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab?: MinimumVersionForCollab, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps( - containerSchema, - compatibilityMode, - minVersionForCollab, - ); + const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); const container = await loadExistingContainer({ ...loaderProps, request: { url: id } }); const fluidContainer = await createFluidContainer({ container, @@ -174,13 +166,11 @@ export class TinyliciousClient { private getLoaderProps( schema: ContainerSchema, // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, - minVersionForCollab: MinimumVersionForCollab | undefined, + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): ILoaderProps { const containerRuntimeFactory = createDOProviderContainerRuntimeFactory({ schema, compatibilityMode, - minVersionForCollab, }); const load = async (): Promise => { return { diff --git a/packages/service-clients/tinylicious-client/src/test/TinyliciousClient.spec.ts b/packages/service-clients/tinylicious-client/src/test/TinyliciousClient.spec.ts index e90f44a20564..26ed4173d9c0 100644 --- a/packages/service-clients/tinylicious-client/src/test/TinyliciousClient.spec.ts +++ b/packages/service-clients/tinylicious-client/src/test/TinyliciousClient.spec.ts @@ -50,7 +50,7 @@ const waitForDataCorruption = async (container: IFluidContainer): Promise }), ); -for (const compatibilityMode of ["1", "2"] as const) { +for (const compatibilityMode of ["1.0.0", "2.0.0"] as const) { describe(`TinyliciousClient (compatibilityMode: ${compatibilityMode})`, function () { let tinyliciousClient: TinyliciousClient; const schema = { From 3e1d70ed9cc86fb1affc64781830e710e78c130d Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 4 May 2026 14:08:44 -0400 Subject: [PATCH 06/18] cleanup --- .../src/compatibilityConfiguration.ts | 18 +++++------- .../fluid-static/src/rootDataObject.ts | 14 ++++----- .../fluid-static/src/treeRootDataObject.ts | 13 ++++----- packages/framework/fluid-static/src/utils.ts | 29 +++++++++---------- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index ea8be121d0b8..9de38071608c 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -5,20 +5,16 @@ import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal"; -// eslint-disable-next-line import-x/no-deprecated -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. + * The CompatibilityMode (minVersionForCollab) determines the set of runtime options to use. + * For a 1.x minVersionForCollab we support full interop with true 1.x clients. + * For a 2.x minVersionForCollab we only support interop with 2.x clients. * - * @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). + * @privateRemarks This is for when we want to use a different set of defaults than the defaults for a given + * minVersionForCollab (i.e. `enableRuntimeIdCompressor` below). */ -export const compatibilityModeRuntimeOptions: Record< - // eslint-disable-next-line import-x/no-deprecated - CompatibilityMode, +export const minVersionForCollabToDefaultRuntimeOptions: Record< + "1" | "2", IContainerRuntimeOptionsInternal > = { "1": {}, diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 970686a0af0c..d6b4f2eb779c 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -31,7 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; +import { minVersionForCollabToDefaultRuntimeOptions } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -49,7 +49,7 @@ import { isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, - resolveCompatibilityInput, + resolveCompatibilityModeToMinVersionForCollab, } from "./utils.js"; /** @@ -231,7 +231,7 @@ export function createDOProviderContainerRuntimeFactory(props: { runtimeOptionOverrides, schema, } = props; - const { compatibilityMode, minVersionForCollab } = resolveCompatibilityInput( + const minVersionForCollab = resolveCompatibilityModeToMinVersionForCollab( props.compatibilityMode, ); const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema); @@ -239,7 +239,6 @@ export function createDOProviderContainerRuntimeFactory(props: { return new DOProviderContainerRuntimeFactory( schema, - compatibilityMode, new RootDataObjectFactory(sharedObjects, registry), { runtimeOptions: runtimeOptionOverrides, @@ -273,14 +272,11 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { * since it can take care of constructing the root data object factory based on the schema. * * @param schema - The schema for the container - * @param compatibilityMode - Compatibility mode * @param rootDataObjectFactory - A factory that can construct the root data object. * @param config - Resolved minimum version for collab (required) and optional runtime option overrides. */ public constructor( schema: ContainerSchema, - // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, rootDataObjectFactory: DataObjectFactory< RootDataObject, { InitialState: RootDataObjectProps } @@ -293,7 +289,9 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { super({ registryEntries: [rootDataObjectFactory.registryEntry], runtimeOptions: { - ...compatibilityModeRuntimeOptions[compatibilityMode], + ...minVersionForCollabToDefaultRuntimeOptions[ + config.minVersionForCollab.startsWith("1.") ? "1" : "2" + ], ...config.runtimeOptions, }, provideEntryPoint, diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index 368ee2528c63..e5977c8dc94b 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -31,7 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js"; +import { minVersionForCollabToDefaultRuntimeOptions } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -48,7 +48,7 @@ import { isSharedObjectKind, makeFluidObject, parseDataObjectsFromSharedObjects, - resolveCompatibilityInput, + resolveCompatibilityModeToMinVersionForCollab, } from "./utils.js"; /** @@ -136,8 +136,6 @@ class TreeContainerRuntimeFactory extends BaseContainerRuntimeFactory { readonly #treeRootDataObjectFactory: TreeDataObjectFactory; public constructor( - // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: CompatibilityMode, treeRootDataObjectFactory: TreeDataObjectFactory, config: { minVersionForCollab: MinimumVersionForCollab; @@ -147,7 +145,9 @@ class TreeContainerRuntimeFactory extends BaseContainerRuntimeFactory { super({ registryEntries: [treeRootDataObjectFactory.registryEntry], runtimeOptions: { - ...compatibilityModeRuntimeOptions[compatibilityMode], + ...minVersionForCollabToDefaultRuntimeOptions[ + config.minVersionForCollab.startsWith("1.") ? "1" : "2" + ], ...config.runtimeOptions, }, provideEntryPoint, @@ -245,7 +245,7 @@ export function createTreeContainerRuntimeFactory(props: { runtimeOptionOverrides, schema, } = props; - const { compatibilityMode, minVersionForCollab } = resolveCompatibilityInput( + const minVersionForCollab = resolveCompatibilityModeToMinVersionForCollab( props.compatibilityMode, ); @@ -253,7 +253,6 @@ export function createTreeContainerRuntimeFactory(props: { const registry = rootDataStoreRegistry ?? new FluidDataStoreRegistry(registryEntries); return new TreeContainerRuntimeFactory( - compatibilityMode, new TreeRootDataObjectFactory(sharedObjects, registry), { runtimeOptions: runtimeOptionOverrides, diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index f992915b6656..fedfa9faeb04 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -148,26 +148,23 @@ export function makeFluidObject< } /** - * Outermost-boundary filter that resolves the unified `compatibilityMode` input — accepting - * either a {@link MinimumVersionForCollab} semver string or a legacy {@link CompatibilityMode} - * value — into the components used internally: a precise `minVersionForCollab` and a - * {@link CompatibilityMode} key (derived from the major version) for indexing - * {@link compatibilityModeRuntimeOptions}. + * Resolves the `compatibilityMode` input — either a {@link MinimumVersionForCollab} + * semver string or a legacy {@link CompatibilityMode} value — into a precise + * {@link MinimumVersionForCollab}. + * + * TODO: This can be removed when the deprecated CompatibilityMode is removed. * * @internal */ -export function resolveCompatibilityInput( - // eslint-disable-next-line import-x/no-deprecated - input: MinimumVersionForCollab | CompatibilityMode, +export function resolveCompatibilityModeToMinVersionForCollab( // eslint-disable-next-line import-x/no-deprecated -): { minVersionForCollab: MinimumVersionForCollab; compatibilityMode: CompatibilityMode } { - // TODO: Checking for "1" and "2" can be removed once CompatibilityMode is removed. - const minVersionForCollab: MinimumVersionForCollab = - input === "1" ? "1.0.0" : input === "2" ? "2.0.0" : input; - return { - minVersionForCollab, - compatibilityMode: minVersionForCollab.startsWith("1.") ? "1" : "2", - }; + compatibilityMode: MinimumVersionForCollab | CompatibilityMode, +): MinimumVersionForCollab { + return compatibilityMode === "1" + ? "1.0.0" + : compatibilityMode === "2" + ? "2.0.0" + : compatibilityMode; } /** From 1ca24cb34832420150ee67066d265e5d3f666f41 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 4 May 2026 14:20:09 -0400 Subject: [PATCH 07/18] use semver-ts to determine runtime options --- packages/framework/fluid-static/package.json | 3 ++- .../src/compatibilityConfiguration.ts | 15 +++++++++++++++ .../framework/fluid-static/src/rootDataObject.ts | 6 ++---- .../fluid-static/src/treeRootDataObject.ts | 6 ++---- pnpm-lock.yaml | 3 +++ 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/framework/fluid-static/package.json b/packages/framework/fluid-static/package.json index 744b1547c521..905abd5a1da2 100644 --- a/packages/framework/fluid-static/package.json +++ b/packages/framework/fluid-static/package.json @@ -142,7 +142,8 @@ "@fluidframework/runtime-utils": "workspace:~", "@fluidframework/shared-object-base": "workspace:~", "@fluidframework/telemetry-utils": "workspace:~", - "@fluidframework/tree": "workspace:~" + "@fluidframework/tree": "workspace:~", + "semver-ts": "^1.0.3" }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.2", diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index 9de38071608c..bff2d2974283 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -4,6 +4,8 @@ */ import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal"; +import { gte } from "semver-ts"; /** * The CompatibilityMode (minVersionForCollab) determines the set of runtime options to use. @@ -25,3 +27,16 @@ export const minVersionForCollabToDefaultRuntimeOptions: Record< enableRuntimeIdCompressor: "on", }, }; + +/** + * Determines default runtime options set for the given minVersionForCollab. + * + * @internal + */ +export function defaultRuntimeOptionsForMinVersion( + minVersionForCollab: MinimumVersionForCollab, +): IContainerRuntimeOptionsInternal { + return minVersionForCollabToDefaultRuntimeOptions[ + gte(minVersionForCollab, "2.0.0") ? "2" : "1" + ]; +} diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index d6b4f2eb779c..2c0d0480e4c5 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -31,7 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { minVersionForCollabToDefaultRuntimeOptions } from "./compatibilityConfiguration.js"; +import { defaultRuntimeOptionsForMinVersion } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -289,9 +289,7 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory { super({ registryEntries: [rootDataObjectFactory.registryEntry], runtimeOptions: { - ...minVersionForCollabToDefaultRuntimeOptions[ - config.minVersionForCollab.startsWith("1.") ? "1" : "2" - ], + ...defaultRuntimeOptionsForMinVersion(config.minVersionForCollab), ...config.runtimeOptions, }, provideEntryPoint, diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index e5977c8dc94b..b3fe215b79be 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -31,7 +31,7 @@ import type { } from "@fluidframework/runtime-definitions/internal"; import type { SharedObjectKind } from "@fluidframework/shared-object-base/internal"; -import { minVersionForCollabToDefaultRuntimeOptions } from "./compatibilityConfiguration.js"; +import { defaultRuntimeOptionsForMinVersion } from "./compatibilityConfiguration.js"; import type { // eslint-disable-next-line import-x/no-deprecated CompatibilityMode, @@ -145,9 +145,7 @@ class TreeContainerRuntimeFactory extends BaseContainerRuntimeFactory { super({ registryEntries: [treeRootDataObjectFactory.registryEntry], runtimeOptions: { - ...minVersionForCollabToDefaultRuntimeOptions[ - config.minVersionForCollab.startsWith("1.") ? "1" : "2" - ], + ...defaultRuntimeOptionsForMinVersion(config.minVersionForCollab), ...config.runtimeOptions, }, provideEntryPoint, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e106235f25d..46291d7e573e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11485,6 +11485,9 @@ importers: '@fluidframework/tree': specifier: workspace:~ version: link:../../dds/tree + semver-ts: + specifier: ^1.0.3 + version: 1.0.3 devDependencies: '@arethetypeswrong/cli': specifier: ^0.18.2 From 0eac478c3e37eb027236f1cdda45b4b8adb2c142 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Mon, 4 May 2026 15:01:56 -0400 Subject: [PATCH 08/18] fix search and replace error --- .../azure-client/src/test/containerCreate.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts index a763c9ec0dc8..37dd7702c6e6 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts @@ -643,8 +643,8 @@ for (const testOpts of testMatrix) { const map2 = container2.initialObjects.map1; // Process ops coming from service - assert.strictEqual(await mapWait(map2, "1.0.0"), 1); - assert.strictEqual(await mapWait(map2, "2.0.0"), 2); + assert.strictEqual(await mapWait(map2, "1"), 1); + assert.strictEqual(await mapWait(map2, "2"), 2); assert.strictEqual(await mapWait(map2, "3"), 3); // Inspect the incoming ops From 30b0de87a56e19e9f7a5d0d74adb8da9668d1805 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 5 May 2026 12:47:04 -0400 Subject: [PATCH 09/18] cleanup/update doc --- CrossClientCompatibility.md | 26 ++++++++----------- .../src/compatibilityConfiguration.ts | 14 +++++----- .../fluid-static/src/rootDataObject.ts | 2 +- .../fluid-static/src/treeRootDataObject.ts | 2 +- packages/framework/fluid-static/src/types.ts | 2 +- .../azure-client/src/AzureClient.ts | 2 +- .../src/TinyliciousClient.ts | 2 +- 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/CrossClientCompatibility.md b/CrossClientCompatibility.md index 98cd157decf5..c4c068e0e8e8 100644 --- a/CrossClientCompatibility.md +++ b/CrossClientCompatibility.md @@ -108,25 +108,21 @@ compat internally (see #### Configuring Cross-Client Compatibility (Declarative Model) -If you are using a service client (i.e. `AzureClient` or `OdspClient`), cross-client compatibility is configured via the `CompatibilityMode` parameter. This is a required argument when creating or loading a container: +If you are using a service client (i.e. `AzureClient` or `OdspClient`), cross-client compatibility is +configured by passing a `minVersionForCollab` semver string when creating or loading a container: ```typescript // Creating a new container -const { container } = await azureClient.createContainer(schema, compatibilityMode); +const { container } = await azureClient.createContainer(schema, "2.0.0"); // Loading an existing container -const { container } = await azureClient.getContainer(id, schema, compatibilityMode); +const { container } = await azureClient.getContainer(id, schema, "2.0.0"); ``` -The client will map `CompatibilityMode` to a `minVersionForCollab` value (see [utils.ts](./packages/framework/fluid-static/src/utils.ts) for details) and automatically configure runtime options via [compatibilityConfiguration.ts](./packages/framework/fluid-static/src/compatibilityConfiguration.ts). This means you do not need to manage individual runtime options or version strings directly. - -Below is the mapping of `CompatibilityMode` values to `minVersionForCollab` at the time of writing. For the most up-to-date mapping, please refer to `compatibilityModeToMinVersionForCollab` in [utils.ts](./packages/framework/fluid-static/src/utils.ts). - - -| Mode | Meaning | Mapped `minVersionForCollab` | -| --- | --- | --- | -| `"1"` | Supports collaboration with 1.x clients. Uses a conservative set of runtime options. | `"1.0.0"` | -| `"2"` | Supports collaboration with 2.x clients only. Enables newer features (e.g., runtime ID compressor for SharedTree support). | `"2.0.0"` | +This sets the minimum Fluid version allowed to collaborate on the document and automatically configures +runtime options to be compatible with that version (see +[compatibilityConfiguration.ts](./packages/framework/fluid-static/src/compatibilityConfiguration.ts)). +You do not need to manage individual runtime options directly. #### Configuring Cross-Client Compatibility (Encapsulated Model) @@ -165,9 +161,9 @@ version your users are [saturated](#terminology) on. This will ensure: We recommend following the below pattern to ensure cross-client compatibility. Keeping your compatibility configuration up-to-date on an ongoing basis ensures you are always within a safe compatibility window. 1. Observe the distribution of Fluid versions across your application's clients. See [Observing Client Version Distribution](./FluidCompatibilityConsiderations.md#observing-client-version-distribution) for how to do this using telemetry. -2. Update your compatibility configuration to match the oldest deployed version that your clients are [saturated](#terminology) on: - - **Declarative model**: Set `CompatibilityMode` to the value corresponding to that saturated version. - - **Encapsulated model**: Set `minVersionForCollab` to the specific saturated version (e.g., `"2.10.0"`). +2. Update your compatibility configuration to match the oldest deployed version that your clients are + [saturated](#terminology) on. In both the declarative and encapsulated models, set `minVersionForCollab` + to that saturated version (e.g., `"2.10.0"`). 3. Verify that the configured compatibility checkpoint is within the supported compatibility window of the Fluid Framework version you want to upgrade to. If it is, bump your Fluid Framework dependencies and update your lock file (so a newer version isn't picked up implicitly); no further action is required. If not, wait for further saturation and return to step 1. 4. Monitor telemetry for warnings/errors to ensure safe rollout (see [Errors and Warnings to Monitor](#errors-and-warnings-to-monitor) below). At this point any clients running a version older than the configured `minVersionForCollab` may be blocked from accessing the document. diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index bff2d2974283..fc24abb3024b 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -8,14 +8,14 @@ import type { MinimumVersionForCollab } from "@fluidframework/runtime-definition import { gte } from "semver-ts"; /** - * The CompatibilityMode (minVersionForCollab) determines the set of runtime options to use. - * For a 1.x minVersionForCollab we support full interop with true 1.x clients. - * For a 2.x minVersionForCollab we only support interop with 2.x clients. + * The `minVersionForCollab` determines the set of runtime options to use. + * For a 1.x `minVersionForCollab` we support full interop with true 1.x clients. + * For a 2.x `minVersionForCollab` we only support interop with 2.x clients. * - * @privateRemarks This is for when we want to use a different set of defaults than the defaults for a given - * minVersionForCollab (i.e. `enableRuntimeIdCompressor` below). + * @privateRemarks The purpose of this map is to use a different set of defaults + * than what the runtime normally uses based on a given `minVersionForCollab` (e.g. `enableRuntimeIdCompressor` below).) */ -export const minVersionForCollabToDefaultRuntimeOptions: Record< +const minVersionForCollabToDefaultRuntimeOptions: Record< "1" | "2", IContainerRuntimeOptionsInternal > = { @@ -29,7 +29,7 @@ export const minVersionForCollabToDefaultRuntimeOptions: Record< }; /** - * Determines default runtime options set for the given minVersionForCollab. + * Determines default runtime options for the given minVersionForCollab. * * @internal */ diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index 2c0d0480e4c5..f16ebfce021d 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -197,7 +197,7 @@ export function createDOProviderContainerRuntimeFactory(props: { */ schema: ContainerSchema; /** - * Minimum framework version required for collaboration. Accepts a + * Minimum Fluid Framework version required for collaboration. Accepts a * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string; * the legacy {@link CompatibilityMode} values `"1"` and `"2"` are **deprecated** * equivalents of `"1.0.0"` and `"2.0.0"`. diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index b3fe215b79be..a8c1ad589272 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -209,7 +209,7 @@ export function createTreeContainerRuntimeFactory(props: { readonly schema: TreeContainerSchema; /** - * Minimum framework version required for collaboration. Accepts a + * Minimum Fluid Framework version required for collaboration. Accepts a * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string; * the legacy {@link CompatibilityMode} values `"1"` and `"2"` are **deprecated** * equivalents of `"1.0.0"` and `"2.0.0"`. diff --git a/packages/framework/fluid-static/src/types.ts b/packages/framework/fluid-static/src/types.ts index 833200344a5f..91de09c155bc 100644 --- a/packages/framework/fluid-static/src/types.ts +++ b/packages/framework/fluid-static/src/types.ts @@ -22,7 +22,7 @@ import type { ITree } from "@fluidframework/tree"; * In "1" mode we support full interop between 2.x clients and 1.x clients, * while in "2" mode we only support interop between 2.x clients. * - * @deprecated Specify the minimum framework version directly via the + * @deprecated Specify the minimum Fluid Framework version directly via the * `minVersionForCollab` parameter, which accepts a * {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} semver string. The * legacy mode "1" is equivalent to `minVersionForCollab: "1.0.0"`; mode "2" is diff --git a/packages/service-clients/azure-client/src/AzureClient.ts b/packages/service-clients/azure-client/src/AzureClient.ts index 9204f8fa152a..2db7ea7f88a5 100644 --- a/packages/service-clients/azure-client/src/AzureClient.ts +++ b/packages/service-clients/azure-client/src/AzureClient.ts @@ -141,7 +141,7 @@ export class AzureClient { * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. * (normally not explicitly specified.) * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * @param compatibilityMode - Minimum Fluid Framework version required for collaboration. Accepts * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. diff --git a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts index e832cce1a501..1e06bc2fe75b 100644 --- a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts +++ b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts @@ -129,7 +129,7 @@ export class TinyliciousClient { * Accesses the existing container given its unique ID in the tinylicious server. * @param id - Unique ID of the container. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Minimum framework version required for collaboration. Accepts + * @param compatibilityMode - Minimum Fluid Framework version required for collaboration. Accepts * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. From 6f983ce08971368e44902e8980e3ec8e23c7992c Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 10:37:24 -0400 Subject: [PATCH 10/18] remove minVersionForCollabOverride --- .changeset/zesty-otters-collab.md | 29 +++++++++++++++++++ .../fluid-static.legacy.beta.api.md | 1 - .../fluid-static/src/rootDataObject.ts | 19 ++---------- .../fluid-static/src/treeRootDataObject.ts | 19 ++---------- 4 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 .changeset/zesty-otters-collab.md diff --git a/.changeset/zesty-otters-collab.md b/.changeset/zesty-otters-collab.md new file mode 100644 index 000000000000..cbdf66428df3 --- /dev/null +++ b/.changeset/zesty-otters-collab.md @@ -0,0 +1,29 @@ +--- +"@fluidframework/fluid-static": minor +"fluid-framework": minor +"__section": legacy +--- +`createTreeContainerRuntimeFactory` no longer accepts `minVersionForCollabOverride` + +The `minVersionForCollabOverride` property on the `props` argument of `createTreeContainerRuntimeFactory` has been removed. + +Pass a [`MinimumVersionForCollab`](https://fluidframework.com/docs/api/runtime-definitions/minimumversionforcollab-typealias) semver string (for example `"2.10.0"`) directly via the `compatibilityMode` property instead. `compatibilityMode` now accepts either a `MinimumVersionForCollab` semver string or the existing (deprecated) `CompatibilityMode` values `"1"` / `"2"`. + +Before: + +```ts +createTreeContainerRuntimeFactory({ + schema, + compatibilityMode: "2", + minVersionForCollabOverride: "2.10.0", +}); +``` + +After: + +```ts +createTreeContainerRuntimeFactory({ + schema, + compatibilityMode: "2.10.0", +}); +``` diff --git a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md index daaf74ade5eb..fb115e985111 100644 --- a/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md +++ b/packages/framework/fluid-static/api-report/fluid-static.legacy.beta.api.md @@ -22,7 +22,6 @@ export function createTreeContainerRuntimeFactory(props: { readonly compatibilityMode: MinimumVersionForCollab | CompatibilityMode; readonly rootDataStoreRegistry?: IFluidDataStoreRegistry; readonly runtimeOptionOverrides?: Partial; - readonly minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory; // @public diff --git a/packages/framework/fluid-static/src/rootDataObject.ts b/packages/framework/fluid-static/src/rootDataObject.ts index f16ebfce021d..c7792d748d40 100644 --- a/packages/framework/fluid-static/src/rootDataObject.ts +++ b/packages/framework/fluid-static/src/rootDataObject.ts @@ -214,23 +214,8 @@ export function createDOProviderContainerRuntimeFactory(props: { * If not provided, only the default options for the given compatibilityMode will be used. */ runtimeOptionOverrides?: Partial; - /** - * Optional override for minimum version for collab. - * If not provided, the default for the given compatibilityMode will be used. - * @remarks - * This is useful when runtime options are overridden and change the minimum version for collab. - * - * @deprecated Pass a {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} - * semver string directly via `compatibilityMode` instead. - */ - minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { - const { - minVersionForCollabOverride, - rootDataStoreRegistry, - runtimeOptionOverrides, - schema, - } = props; + const { rootDataStoreRegistry, runtimeOptionOverrides, schema } = props; const minVersionForCollab = resolveCompatibilityModeToMinVersionForCollab( props.compatibilityMode, ); @@ -242,7 +227,7 @@ export function createDOProviderContainerRuntimeFactory(props: { new RootDataObjectFactory(sharedObjects, registry), { runtimeOptions: runtimeOptionOverrides, - minVersionForCollab: minVersionForCollabOverride ?? minVersionForCollab, + minVersionForCollab, }, ); } diff --git a/packages/framework/fluid-static/src/treeRootDataObject.ts b/packages/framework/fluid-static/src/treeRootDataObject.ts index a8c1ad589272..de33eb09b3ad 100644 --- a/packages/framework/fluid-static/src/treeRootDataObject.ts +++ b/packages/framework/fluid-static/src/treeRootDataObject.ts @@ -226,23 +226,8 @@ export function createTreeContainerRuntimeFactory(props: { * If not provided, only the default options for the given compatibilityMode will be used. */ readonly runtimeOptionOverrides?: Partial; - /** - * Optional override for minimum version for collab. - * If not provided, the default for the given compatibilityMode will be used. - * @remarks - * This is useful when runtime options are overridden and change the minimum version for collab. - * - * @deprecated Pass a {@link @fluidframework/runtime-definitions#MinimumVersionForCollab} - * semver string directly via `compatibilityMode` instead. - */ - readonly minVersionForCollabOverride?: MinimumVersionForCollab; }): IRuntimeFactory { - const { - minVersionForCollabOverride, - rootDataStoreRegistry, - runtimeOptionOverrides, - schema, - } = props; + const { rootDataStoreRegistry, runtimeOptionOverrides, schema } = props; const minVersionForCollab = resolveCompatibilityModeToMinVersionForCollab( props.compatibilityMode, ); @@ -254,7 +239,7 @@ export function createTreeContainerRuntimeFactory(props: { new TreeRootDataObjectFactory(sharedObjects, registry), { runtimeOptions: runtimeOptionOverrides, - minVersionForCollab: minVersionForCollabOverride ?? minVersionForCollab, + minVersionForCollab, }, ); } From 8a9d114cccfeee638b47bfebf33b6d60b6a0c1eb Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 15:18:57 -0400 Subject: [PATCH 11/18] update comments for defaultRuntimeOptionsForMinVersion --- .../fluid-static/src/compatibilityConfiguration.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/framework/fluid-static/src/compatibilityConfiguration.ts b/packages/framework/fluid-static/src/compatibilityConfiguration.ts index fc24abb3024b..fff5a3c6d909 100644 --- a/packages/framework/fluid-static/src/compatibilityConfiguration.ts +++ b/packages/framework/fluid-static/src/compatibilityConfiguration.ts @@ -29,8 +29,12 @@ const minVersionForCollabToDefaultRuntimeOptions: Record< }; /** - * Determines default runtime options for the given minVersionForCollab. + * Returns the fluid-static-specific runtime option overrides for the given `minVersionForCollab`. * + * @remarks + * The bulk of runtime defaults for a given `minVersionForCollab` are selected by container-runtime + * (via `getMinVersionForCollabDefaults`). This function only contributes the additional overrides + * that fluid-static needs to layer on top of those defaults. * @internal */ export function defaultRuntimeOptionsForMinVersion( From ce1688fa26d491f21e980aca3fd7dc9de54cff67 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 15:24:05 -0400 Subject: [PATCH 12/18] add work item for removal of CompatibilityMode --- packages/framework/fluid-static/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index fedfa9faeb04..cbdb4f8d088d 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -152,7 +152,7 @@ export function makeFluidObject< * semver string or a legacy {@link CompatibilityMode} value — into a precise * {@link MinimumVersionForCollab}. * - * TODO: This can be removed when the deprecated CompatibilityMode is removed. + * TODO: This can be removed when the deprecated CompatibilityMode is removed - AB#73679 * * @internal */ From a38e5cfaade7544c1d2588556636e4784b457636 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 15:47:16 -0400 Subject: [PATCH 13/18] Proper deprecation --- .../api-report/azure-client.beta.api.md | 20 +++- .../azure-client.legacy.beta.api.md | 20 +++- .../azure-client.legacy.public.api.md | 20 +++- .../api-report/azure-client.public.api.md | 20 +++- .../azure-client/src/AzureClient.ts | 100 +++++++++++++++--- .../tinylicious-client.alpha.api.md | 14 ++- .../api-report/tinylicious-client.beta.api.md | 14 ++- .../tinylicious-client.public.api.md | 14 ++- .../src/TinyliciousClient.ts | 61 +++++++++-- .../tinylicious-client/src/interfaces.ts | 2 +- 10 files changed, 246 insertions(+), 39 deletions(-) diff --git a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md index b586e38af11d..85b633ce69b8 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.beta.api.md @@ -7,16 +7,30 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + }>; + // @deprecated + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md index b586e38af11d..85b633ce69b8 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.beta.api.md @@ -7,16 +7,30 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + }>; + // @deprecated + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md index 60fcadf641fe..086c53aadfe6 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.legacy.public.api.md @@ -7,16 +7,30 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + }>; + // @deprecated + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/api-report/azure-client.public.api.md b/packages/service-clients/azure-client/api-report/azure-client.public.api.md index 60fcadf641fe..086c53aadfe6 100644 --- a/packages/service-clients/azure-client/api-report/azure-client.public.api.md +++ b/packages/service-clients/azure-client/api-report/azure-client.public.api.md @@ -7,16 +7,30 @@ // @public export class AzureClient { constructor(properties: AzureClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: AzureContainerServices; }>; getContainerVersions(id: string, options?: AzureGetVersionsOptions): Promise; - viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + }>; + // @deprecated + viewContainerVersion(id: string, containerSchema: TContainerSchema, version: AzureContainerVersion, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; }>; } diff --git a/packages/service-clients/azure-client/src/AzureClient.ts b/packages/service-clients/azure-client/src/AzureClient.ts index 2db7ea7f88a5..2d1b63015b4d 100644 --- a/packages/service-clients/azure-client/src/AzureClient.ts +++ b/packages/service-clients/azure-client/src/AzureClient.ts @@ -141,12 +141,35 @@ export class AzureClient { * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. * (normally not explicitly specified.) * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Minimum Fluid Framework version required for collaboration. Accepts - * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy - * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and - * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. + * @param minVersionForCollab - Minimum Fluid Framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`). * @returns New detached container instance along with associated services. */ + public async createContainer( + containerSchema: TContainerSchema, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + /** + * Creates a new detached container instance in the Azure Fluid Relay. + * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. + * (normally not explicitly specified.) + * @param containerSchema - Container schema for the new container. + * @param compatibilityMode - Legacy {@link @fluidframework/fluid-static#CompatibilityMode} value. + * @returns New detached container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) instead. The legacy + * values `"1"` and `"2"` correspond to `"1.0.0"` and `"2.0.0"` respectively. + */ + public async createContainer( + containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, + ): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; public async createContainer( containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated @@ -179,12 +202,38 @@ export class AzureClient { * (normally not explicitly specified.) * @param id - Unique ID of the container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Minimum framework version required for collaboration. Accepts - * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy - * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and - * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. + * @param minVersionForCollab - Minimum framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`). * @returns Existing container instance along with associated services. */ + public async getContainer( + id: string, + containerSchema: TContainerSchema, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; + /** + * Accesses the existing container given its unique ID in the Azure Fluid Relay. + * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. + * (normally not explicitly specified.) + * @param id - Unique ID of the container in Azure Fluid Relay. + * @param containerSchema - Container schema used to access data objects in the container. + * @param compatibilityMode - Legacy {@link @fluidframework/fluid-static#CompatibilityMode} value. + * @returns Existing container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) instead. The legacy + * values `"1"` and `"2"` correspond to `"1.0.0"` and `"2.0.0"` respectively. + */ + public async getContainer( + id: string, + containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, + ): Promise<{ + container: IFluidContainer; + services: AzureContainerServices; + }>; public async getContainer( id: string, containerSchema: TContainerSchema, @@ -221,12 +270,39 @@ export class AzureClient { * @param id - Unique ID of the source container in Azure Fluid Relay. * @param containerSchema - Container schema used to access data objects in the container. * @param version - Unique version of the source container in Azure Fluid Relay. - * @param compatibilityMode - Minimum framework version required for collaboration. Accepts - * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy - * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and - * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. + * @param minVersionForCollab - Minimum framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`). + * @returns Loaded container instance at the specified version. + */ + public async viewContainerVersion( + id: string, + containerSchema: TContainerSchema, + version: AzureContainerVersion, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IFluidContainer; + }>; + /** + * Load a specific version of a container for viewing only. + * @typeparam TContainerSchema - Used to infer the the type of 'initialObjects' in the returned container. + * (normally not explicitly specified.) + * @param id - Unique ID of the source container in Azure Fluid Relay. + * @param containerSchema - Container schema used to access data objects in the container. + * @param version - Unique version of the source container in Azure Fluid Relay. + * @param compatibilityMode - Legacy {@link @fluidframework/fluid-static#CompatibilityMode} value. * @returns Loaded container instance at the specified version. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) instead. The legacy + * values `"1"` and `"2"` correspond to `"1.0.0"` and `"2.0.0"` respectively. */ + public async viewContainerVersion( + id: string, + containerSchema: TContainerSchema, + version: AzureContainerVersion, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, + ): Promise<{ + container: IFluidContainer; + }>; public async viewContainerVersion( id: string, containerSchema: TContainerSchema, diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md index a0ad08f545ac..05fbf2875875 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.alpha.api.md @@ -12,11 +12,21 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md index 1c4f1237ef04..0c44007d5449 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.beta.api.md @@ -12,11 +12,21 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md index cd30b58e584e..359643136b38 100644 --- a/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md +++ b/packages/service-clients/tinylicious-client/api-report/tinylicious-client.public.api.md @@ -12,11 +12,21 @@ export type ITinyliciousAudience = IServiceAudience; // @public @sealed export class TinyliciousClient { constructor(properties?: TinyliciousClientProps); - createContainer(containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + createContainer(containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; - getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: MinimumVersionForCollab | CompatibilityMode): Promise<{ + // @deprecated + createContainer(containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + getContainer(id: string, containerSchema: TContainerSchema, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + // @deprecated + getContainer(id: string, containerSchema: TContainerSchema, compatibilityMode: CompatibilityMode): Promise<{ container: IFluidContainer; services: TinyliciousContainerServices; }>; diff --git a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts index 1e06bc2fe75b..f950a509b027 100644 --- a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts +++ b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts @@ -74,12 +74,33 @@ export class TinyliciousClient { /** * Creates a new detached container instance in Tinylicious server. * @param containerSchema - Container schema for the new container. - * @param compatibilityMode - Minimum framework version required for collaboration. Accepts - * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy - * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and - * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. + * @param minVersionForCollab - Minimum framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`). * @returns New detached container instance along with associated services. */ + public async createContainer( + containerSchema: TContainerSchema, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + /** + * Creates a new detached container instance in Tinylicious server. + * @param containerSchema - Container schema for the new container. + * @param compatibilityMode - Legacy {@link @fluidframework/fluid-static#CompatibilityMode} value. + * @returns New detached container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) instead. The legacy + * values `"1"` and `"2"` correspond to `"1.0.0"` and `"2.0.0"` respectively. + */ + public async createContainer( + containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, + ): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; public async createContainer( containerSchema: TContainerSchema, // eslint-disable-next-line import-x/no-deprecated @@ -129,12 +150,36 @@ export class TinyliciousClient { * Accesses the existing container given its unique ID in the tinylicious server. * @param id - Unique ID of the container. * @param containerSchema - Container schema used to access data objects in the container. - * @param compatibilityMode - Minimum Fluid Framework version required for collaboration. Accepts - * either a `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`) or a legacy - * {@link @fluidframework/fluid-static#CompatibilityMode} value. The legacy values `"1"` and - * `"2"` are **deprecated** equivalents of `"1.0.0"` and `"2.0.0"`. + * @param minVersionForCollab - Minimum Fluid Framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"1.0.0"`, `"2.0.0"`). * @returns Existing container instance along with associated services. */ + public async getContainer( + id: string, + containerSchema: TContainerSchema, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; + /** + * Accesses the existing container given its unique ID in the tinylicious server. + * @param id - Unique ID of the container. + * @param containerSchema - Container schema used to access data objects in the container. + * @param compatibilityMode - Legacy {@link @fluidframework/fluid-static#CompatibilityMode} value. + * @returns Existing container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) instead. The legacy + * values `"1"` and `"2"` correspond to `"1.0.0"` and `"2.0.0"` respectively. + */ + public async getContainer( + id: string, + containerSchema: TContainerSchema, + // eslint-disable-next-line import-x/no-deprecated + compatibilityMode: CompatibilityMode, + ): Promise<{ + container: IFluidContainer; + services: TinyliciousContainerServices; + }>; public async getContainer( id: string, containerSchema: TContainerSchema, diff --git a/packages/service-clients/tinylicious-client/src/interfaces.ts b/packages/service-clients/tinylicious-client/src/interfaces.ts index ed82d94deee9..718371f170a7 100644 --- a/packages/service-clients/tinylicious-client/src/interfaces.ts +++ b/packages/service-clients/tinylicious-client/src/interfaces.ts @@ -64,7 +64,7 @@ export interface TinyliciousConnectionConfig { * Any functionality regarding how the data is handled within the FluidContainer itself (e.g., which data objects or * DDSes to use) will not be included here but rather on the FluidContainer class itself. * - * Returned by {@link TinyliciousClient.createContainer} and {@link TinyliciousClient.getContainer} alongside the FluidContainer. + * Returned by `TinyliciousClient.createContainer` and `TinyliciousClient.getContainer` alongside the FluidContainer. * * @sealed * @public From d3a83b3b0a5094baa1409e7dcc073f7b5533d3fc Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 15:47:26 -0400 Subject: [PATCH 14/18] update examples --- examples/apps/presence-tracker/src/app.ts | 4 ++-- examples/benchmarks/tablebench/src/azure.ts | 2 +- .../app-insights-logger/src/components/ClientUtilities.ts | 4 ++-- examples/data-objects/text-editor/src/app.tsx | 4 ++-- .../azure-client/external-controller/src/app.ts | 4 ++-- .../azure-client/external-controller/tests/index.ts | 2 +- examples/service-clients/azure-client/todo-list/src/app.tsx | 4 ++-- .../service-clients/azure-client/todo-list/test/index.tsx | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/apps/presence-tracker/src/app.ts b/examples/apps/presence-tracker/src/app.ts index c6fa5e0f049e..ded034335a92 100644 --- a/examples/apps/presence-tracker/src/app.ts +++ b/examples/apps/presence-tracker/src/app.ts @@ -44,7 +44,7 @@ async function start(): Promise { if (createNew) { // The client will create a new detached container using the schema // A detached container will enable the app to modify the container before attaching it to the client - ({ container } = await client.createContainer(containerSchema, "2")); + ({ container } = await client.createContainer(containerSchema, "2.0.0")); // If the app is in a `createNew` state, and the container is detached, we attach the container. // This uploads the container to the service and connects to the collaboration session. @@ -56,7 +56,7 @@ async function start(): Promise { id = location.hash.slice(1); // Use the unique container ID to fetch the container created earlier. It will already be connected to the // collaboration session. - ({ container } = await client.getContainer(id, containerSchema, "2")); + ({ container } = await client.getContainer(id, containerSchema, "2.0.0")); } const presence = getPresence(container); diff --git a/examples/benchmarks/tablebench/src/azure.ts b/examples/benchmarks/tablebench/src/azure.ts index adc1d830cca5..1900e8083eea 100644 --- a/examples/benchmarks/tablebench/src/azure.ts +++ b/examples/benchmarks/tablebench/src/azure.ts @@ -42,7 +42,7 @@ export async function initFluid(): Promise<{ view: TreeView }> { const { tree } = container.initialObjects; view = tree.viewWith(config); } else { - ({ container } = await client.createContainer(containerSchema, "2")); + ({ container } = await client.createContainer(containerSchema, "2.0.0")); const { tree } = container.initialObjects; view = tree.viewWith(config); view.initialize(generateTable(10000)); diff --git a/examples/client-logger/app-insights-logger/src/components/ClientUtilities.ts b/examples/client-logger/app-insights-logger/src/components/ClientUtilities.ts index 096c1655af78..28d74a0eae75 100644 --- a/examples/client-logger/app-insights-logger/src/components/ClientUtilities.ts +++ b/examples/client-logger/app-insights-logger/src/components/ClientUtilities.ts @@ -75,7 +75,7 @@ export async function createFluidContainer( console.log("Creating new container..."); let createContainerResult: ContainerLoadResult; try { - createContainerResult = await client.createContainer(containerSchema, "2"); + createContainerResult = await client.createContainer(containerSchema, "2.0.0"); } catch (error) { console.error(`Encountered error creating Fluid container: "${error}".`); throw error; @@ -126,7 +126,7 @@ export async function loadExistingFluidContainer( console.log("Loading existing container..."); let loadContainerResult: ContainerLoadResult; try { - loadContainerResult = await client.getContainer(containerId, containerSchema, "2"); + loadContainerResult = await client.getContainer(containerId, containerSchema, "2.0.0"); } catch (error) { console.error(`Encountered error loading Fluid container: "${error}".`); throw error; diff --git a/examples/data-objects/text-editor/src/app.tsx b/examples/data-objects/text-editor/src/app.tsx index 9db0d38bdb05..bef4ffea3bbb 100644 --- a/examples/data-objects/text-editor/src/app.tsx +++ b/examples/data-objects/text-editor/src/app.tsx @@ -96,7 +96,7 @@ async function createAndAttachNewContainer(client: AzureClient): Promise<{ containerId: string; treeView: TreeView; }> { - const { container } = await client.createContainer(containerSchema, "2"); + const { container } = await client.createContainer(containerSchema, "2.0.0"); const treeView = container.initialObjects.tree.viewWith(treeConfig); @@ -124,7 +124,7 @@ async function loadExistingContainer( container: IFluidContainer; treeView: TreeView; }> { - const { container } = await client.getContainer(containerId, containerSchema, "2"); + const { container } = await client.getContainer(containerId, containerSchema, "2.0.0"); const treeView = container.initialObjects.tree.viewWith(treeConfig); return { container, diff --git a/examples/service-clients/azure-client/external-controller/src/app.ts b/examples/service-clients/azure-client/external-controller/src/app.ts index 66c9077935f9..a3e505e074e6 100644 --- a/examples/service-clients/azure-client/external-controller/src/app.ts +++ b/examples/service-clients/azure-client/external-controller/src/app.ts @@ -44,7 +44,7 @@ async function start(): Promise { if (createNew) { // The client will create a new detached container using the schema // A detached container will enable the app to modify the container before attaching it to the client - ({ container, services } = await client.createContainer(diceRollerContainerSchema, "2")); + ({ container, services } = await client.createContainer(diceRollerContainerSchema, "2.0.0")); // const map1 = container.initialObjects.map1 as ISharedMap; // map1.set("diceValue", 1); // const map2 = container.initialObjects.map1 as ISharedMap; @@ -63,7 +63,7 @@ async function start(): Promise { id = location.hash.slice(1); // Use the unique container ID to fetch the container created earlier. It will already be connected to the // collaboration session. - ({ container, services } = await client.getContainer(id, diceRollerContainerSchema, "2")); + ({ container, services } = await client.getContainer(id, diceRollerContainerSchema, "2.0.0")); appModel = loadAppFromExistingContainer(container); } diff --git a/examples/service-clients/azure-client/external-controller/tests/index.ts b/examples/service-clients/azure-client/external-controller/tests/index.ts index c24a7d0ab54f..3ecef0ee0cf4 100644 --- a/examples/service-clients/azure-client/external-controller/tests/index.ts +++ b/examples/service-clients/azure-client/external-controller/tests/index.ts @@ -104,7 +104,7 @@ async function createContainerAndRenderInElement( containerId, createDOProviderContainerRuntimeFactory({ schema: diceRollerContainerSchema, - compatibilityMode: "2", + compatibilityMode: "2.0.0", }), createNewFlag, ); diff --git a/examples/service-clients/azure-client/todo-list/src/app.tsx b/examples/service-clients/azure-client/todo-list/src/app.tsx index ef77714e7f70..62a5a628175f 100644 --- a/examples/service-clients/azure-client/todo-list/src/app.tsx +++ b/examples/service-clients/azure-client/todo-list/src/app.tsx @@ -57,7 +57,7 @@ async function start(): Promise { if (createNew) { // The client will create a new detached container using the schema // A detached container will enable the app to modify the container before attaching it to the client - ({ container } = await client.createContainer(todoListContainerSchema, "2")); + ({ container } = await client.createContainer(todoListContainerSchema, "2.0.0")); // Initialize our models so they are ready for use with our controllers appModel = await initializeAppForNewContainer(container); @@ -71,7 +71,7 @@ async function start(): Promise { containerId = location.hash.slice(1); // Use the unique container ID to fetch the container created earlier. It will already be connected to the // collaboration session. - ({ container } = await client.getContainer(containerId, todoListContainerSchema, "2")); + ({ container } = await client.getContainer(containerId, todoListContainerSchema, "2.0.0")); appModel = loadAppFromExistingContainer(container); } diff --git a/examples/service-clients/azure-client/todo-list/test/index.tsx b/examples/service-clients/azure-client/todo-list/test/index.tsx index c0a9622d8c8b..118247462b8f 100644 --- a/examples/service-clients/azure-client/todo-list/test/index.tsx +++ b/examples/service-clients/azure-client/todo-list/test/index.tsx @@ -105,7 +105,7 @@ async function createContainerAndRenderInElement( containerId, createTreeContainerRuntimeFactory({ schema: todoListContainerSchema, - compatibilityMode: "2", + compatibilityMode: "2.0.0", }), createNewFlag, ); From dc8c8acbba0567766a9ad50fbc0734abc0839e93 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 15:54:49 -0400 Subject: [PATCH 15/18] narrow internal types --- packages/framework/fluid-static/src/index.ts | 5 ++++- packages/framework/fluid-static/src/utils.ts | 6 +++--- .../azure-client/src/AzureClient.ts | 9 ++++++--- .../azure-client/src/interfaces.ts | 11 ++--------- .../tinylicious-client/src/TinyliciousClient.ts | 16 +++++++++++----- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/packages/framework/fluid-static/src/index.ts b/packages/framework/fluid-static/src/index.ts index bef5b736a6ff..725829a0cedd 100644 --- a/packages/framework/fluid-static/src/index.ts +++ b/packages/framework/fluid-static/src/index.ts @@ -33,4 +33,7 @@ export type { Myself, TreeContainerSchema, } from "./types.js"; -export { isTreeContainerSchema } from "./utils.js"; +export { + isTreeContainerSchema, + resolveCompatibilityModeToMinVersionForCollab, +} from "./utils.js"; diff --git a/packages/framework/fluid-static/src/utils.ts b/packages/framework/fluid-static/src/utils.ts index cbdb4f8d088d..b57b3a915558 100644 --- a/packages/framework/fluid-static/src/utils.ts +++ b/packages/framework/fluid-static/src/utils.ts @@ -148,9 +148,9 @@ export function makeFluidObject< } /** - * Resolves the `compatibilityMode` input — either a {@link MinimumVersionForCollab} - * semver string or a legacy {@link CompatibilityMode} value — into a precise - * {@link MinimumVersionForCollab}. + * Resolves the `compatibilityMode` input — either a `MinimumVersionForCollab` + * semver string or a legacy `CompatibilityMode` value — into a precise + * `MinimumVersionForCollab`. * * TODO: This can be removed when the deprecated CompatibilityMode is removed - AB#73679 * diff --git a/packages/service-clients/azure-client/src/AzureClient.ts b/packages/service-clients/azure-client/src/AzureClient.ts index 2d1b63015b4d..20599bb03b47 100644 --- a/packages/service-clients/azure-client/src/AzureClient.ts +++ b/packages/service-clients/azure-client/src/AzureClient.ts @@ -36,6 +36,7 @@ import { createDOProviderContainerRuntimeFactory, createFluidContainer, createServiceAudience, + resolveCompatibilityModeToMinVersionForCollab, } from "@fluidframework/fluid-static/internal"; import { RouterliciousDocumentServiceFactory } from "@fluidframework/routerlicious-driver/internal"; import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; @@ -102,8 +103,7 @@ export class AzureClient { compatibilityMode, }: { schema: ContainerSchema; - // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: MinimumVersionForCollab | CompatibilityMode; + compatibilityMode: MinimumVersionForCollab; }) => IRuntimeFactory; /** @@ -380,7 +380,10 @@ export class AzureClient { // eslint-disable-next-line import-x/no-deprecated compatibilityMode: MinimumVersionForCollab | CompatibilityMode, ): ILoaderProps { - const factoryArguments = { schema, compatibilityMode }; + const factoryArguments = { + schema, + compatibilityMode: resolveCompatibilityModeToMinVersionForCollab(compatibilityMode), + }; const runtimeFactory = this.createContainerRuntimeFactory ? this.createContainerRuntimeFactory(factoryArguments) : createDOProviderContainerRuntimeFactory(factoryArguments); diff --git a/packages/service-clients/azure-client/src/interfaces.ts b/packages/service-clients/azure-client/src/interfaces.ts index 36381311fbfe..ce80d79f6476 100644 --- a/packages/service-clients/azure-client/src/interfaces.ts +++ b/packages/service-clients/azure-client/src/interfaces.ts @@ -10,13 +10,7 @@ import type { } from "@fluidframework/core-interfaces"; import type { IUser } from "@fluidframework/driver-definitions"; import type { ICompressionStorageConfig } from "@fluidframework/driver-utils"; -import type { - // eslint-disable-next-line import-x/no-deprecated - CompatibilityMode, - ContainerSchema, - IMember, - IServiceAudience, -} from "@fluidframework/fluid-static"; +import type { ContainerSchema, IMember, IServiceAudience } from "@fluidframework/fluid-static"; import type { ITokenProvider } from "@fluidframework/routerlicious-driver"; import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; @@ -55,8 +49,7 @@ export interface AzureClientPropsInternal extends AzureClientProps { compatibilityMode, }: { schema: ContainerSchema; - // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: MinimumVersionForCollab | CompatibilityMode; + compatibilityMode: MinimumVersionForCollab; }) => IRuntimeFactory; } diff --git a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts index f950a509b027..c070d172b252 100644 --- a/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts +++ b/packages/service-clients/tinylicious-client/src/TinyliciousClient.ts @@ -29,6 +29,7 @@ import { createDOProviderContainerRuntimeFactory, createFluidContainer, createServiceAudience, + resolveCompatibilityModeToMinVersionForCollab, } from "@fluidframework/fluid-static/internal"; import { RouterliciousDocumentServiceFactory } from "@fluidframework/routerlicious-driver/internal"; import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; @@ -109,7 +110,10 @@ export class TinyliciousClient { container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + resolveCompatibilityModeToMinVersionForCollab(compatibilityMode), + ); // We're not actually using the code proposal (our code loader always loads the same module // regardless of the proposal), but the Container will only give us a NullRuntime if there's @@ -189,7 +193,10 @@ export class TinyliciousClient { container: IFluidContainer; services: TinyliciousContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema, compatibilityMode); + const loaderProps = this.getLoaderProps( + containerSchema, + resolveCompatibilityModeToMinVersionForCollab(compatibilityMode), + ); const container = await loadExistingContainer({ ...loaderProps, request: { url: id } }); const fluidContainer = await createFluidContainer({ container, @@ -210,12 +217,11 @@ export class TinyliciousClient { private getLoaderProps( schema: ContainerSchema, - // eslint-disable-next-line import-x/no-deprecated - compatibilityMode: MinimumVersionForCollab | CompatibilityMode, + minVersionForCollab: MinimumVersionForCollab, ): ILoaderProps { const containerRuntimeFactory = createDOProviderContainerRuntimeFactory({ schema, - compatibilityMode, + compatibilityMode: minVersionForCollab, }); const load = async (): Promise => { return { From dd1678ad4f705d40b8e0894c655d581865027cbf Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 16:02:39 -0400 Subject: [PATCH 16/18] use pkgVersion for test version --- .../azure-client/package.json | 1 + .../azure-client/src/packageVersion.ts | 9 +++++ .../azure-client/src/test/audience.spec.ts | 32 ++++++++--------- .../src/test/containerCreate.spec.ts | 24 ++++++------- .../azure-client/src/test/ddsTests.spec.ts | 34 +++++++++---------- .../src/test/multiprocess/childClient.tool.ts | 5 +-- .../azure-client/src/test/signals.spec.ts | 12 ++++--- .../azure-client/src/test/tree.spec.ts | 16 ++++----- .../azure-client/src/test/tsconfig.json | 6 ++-- .../azure-client/src/test/utils.ts | 2 ++ .../src/test/viewContainerVersion.spec.ts | 18 +++++----- 11 files changed, 88 insertions(+), 71 deletions(-) create mode 100644 packages/service-clients/end-to-end-tests/azure-client/src/packageVersion.ts diff --git a/packages/service-clients/end-to-end-tests/azure-client/package.json b/packages/service-clients/end-to-end-tests/azure-client/package.json index 8fc152cac12f..428da24130e4 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/package.json +++ b/packages/service-clients/end-to-end-tests/azure-client/package.json @@ -15,6 +15,7 @@ "scripts": { "build": "fluid-build . --task build", "build:compile": "fluid-build . --task compile", + "build:genver": "gen-version", "build:test": "tsc --project ./src/test/tsconfig.json", "check:biome": "biome check .", "check:format": "npm run check:biome", diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/packageVersion.ts b/packages/service-clients/end-to-end-tests/azure-client/src/packageVersion.ts new file mode 100644 index 000000000000..9e2ead378643 --- /dev/null +++ b/packages/service-clients/end-to-end-tests/azure-client/src/packageVersion.ts @@ -0,0 +1,9 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + * + * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY + */ + +export const pkgName = "@fluidframework/azure-end-to-end-tests"; +export const pkgVersion = "2.101.0"; diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts index 755172d344c5..957c31abed59 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/audience.spec.ts @@ -19,7 +19,7 @@ import { ScopeType, } from "./AzureClientFactory.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { configProvider, waitForMember, getTestMatrix } from "./utils.js"; +import { configProvider, waitForMember, getTestMatrix, pkgVersion } from "./utils.js"; const testMatrix = getTestMatrix(); for (const testOpts of testMatrix) { @@ -54,9 +54,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container, services } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container, services } = await client.createContainer(schema, "2.0.0")); + ({ container, services } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -99,9 +99,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container, services } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container, services } = await client.createContainer(schema, "2.0.0")); + ({ container, services } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -134,7 +134,7 @@ for (const testOpts of testMatrix) { const { services: servicesGet } = await client2.getContainer( containerId, schema, - "2.0.0", + pkgVersion, ); /* This is a workaround for a known bug, we should have one member (self) upon container connection */ @@ -167,9 +167,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -191,7 +191,7 @@ for (const testOpts of testMatrix) { const { services: servicesGet } = await client2.getContainer( containerId, schema, - "2.0.0", + pkgVersion, ); /* This is a workaround for a known bug, we should have one member (self) upon container connection */ @@ -230,9 +230,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container, services } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container, services } = await client.createContainer(schema, "2.0.0")); + ({ container, services } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -262,7 +262,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer, services: partnerServices } = - await partnerClient.getContainer(containerId, schema, "2.0.0"); + await partnerClient.getContainer(containerId, schema, pkgVersion); if (partnerContainer.connectionState !== ConnectionState.Connected) { await timeoutPromise( @@ -339,9 +339,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -360,7 +360,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer, services: partnerServices } = - await partnerClient.getContainer(containerId, schema, "2.0.0"); + await partnerClient.getContainer(containerId, schema, pkgVersion); if (partnerContainer.connectionState !== ConnectionState.Connected) { await timeoutPromise( @@ -406,7 +406,7 @@ for (const testOpts of testMatrix) { [ScopeType.DocRead], ); const { container: partnerContainer2, services: partnerServices2 } = - await partnerClient2.getContainer(containerId, schema, "2.0.0"); + await partnerClient2.getContainer(containerId, schema, pkgVersion); if (partnerContainer2.connectionState !== ConnectionState.Connected) { await timeoutPromise( diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts index 37dd7702c6e6..14bdb3d6a9bb 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/containerCreate.spec.ts @@ -36,7 +36,7 @@ import { getContainerIdFromPayloadResponse, } from "./AzureClientFactory.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { getTestMatrix, mapWait } from "./utils.js"; +import { getTestMatrix, mapWait, pkgVersion } from "./utils.js"; const configProvider = (settings: Record): IConfigProviderBase => ({ getRawConfig: (name: string): ConfigTypes => settings[name], @@ -73,7 +73,7 @@ for (const testOpts of testMatrix) { if (isEphemeral) { this.skip(); } - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); assert.strictEqual( container.attachState, AttachState.Detached, @@ -101,9 +101,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -138,9 +138,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -181,7 +181,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container: newContainer } = await client.createContainer(schema, "2.0.0")); + ({ container: newContainer } = await client.createContainer(schema, pkgVersion)); containerId = await newContainer.attach(); if (newContainer.connectionState !== ConnectionState.Connected) { @@ -192,7 +192,7 @@ for (const testOpts of testMatrix) { } } - const resources = client.getContainer(containerId, schema, "2.0.0"); + const resources = client.getContainer(containerId, schema, pkgVersion); await assert.doesNotReject( resources, () => true, @@ -210,7 +210,7 @@ for (const testOpts of testMatrix) { it("cannot load improperly created container (cannot load a non-existent container)", async () => { const consoleErrorFn = console.error; console.error = (): void => {}; - const containerAndServicesP = client.getContainer("containerConfig", schema, "2.0.0"); + const containerAndServicesP = client.getContainer("containerConfig", schema, pkgVersion); const errorFn = (error: Error): boolean => { assert.notStrictEqual(error.message, undefined, "Azure Client error is undefined"); @@ -266,7 +266,7 @@ for (const testOpts of testMatrix) { if (isEphemeral) { this.skip(); } - await client.createContainer(schema, "2.0.0"); + await client.createContainer(schema, pkgVersion); const event = mockLogger.events.find((e) => e.eventName.endsWith("ContainerLoadStats")); assert(event !== undefined, "ContainerLoadStats event should exist"); const featureGates = event.featureGates as string; @@ -706,7 +706,7 @@ describe("Container create in tree-only mode", () => { tree: SharedTree, }, }; - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); assert(SharedTree.is(container.initialObjects.tree)); }); @@ -721,7 +721,7 @@ describe("Container create in tree-only mode", () => { }; await assert.rejects( - client.createContainer(schema, "2.0.0"), + client.createContainer(schema, pkgVersion), (error: unknown) => { assert(error instanceof UsageError); assert.strictEqual(error.message, invalidSchemaErrorMessage); diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts index 5e60c0959b97..135fda0acb7b 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/ddsTests.spec.ts @@ -19,7 +19,7 @@ import { } from "./AzureClientFactory.js"; import { CounterTestDataObject, TestDataObject } from "./TestDataObject.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { getTestMatrix, mapWait } from "./utils.js"; +import { getTestMatrix, mapWait, pkgVersion } from "./utils.js"; const testMatrix = getTestMatrix(); for (const testOpts of testMatrix) { @@ -57,10 +57,10 @@ for (const testOpts of testMatrix) { ({ container: newContainer } = await client.getContainer( containerId, schema, - "2.0.0", + pkgVersion, )); } else { - ({ container: newContainer } = await client.createContainer(schema, "2.0.0")); + ({ container: newContainer } = await client.createContainer(schema, pkgVersion)); containerId = await newContainer.attach(); } @@ -71,7 +71,7 @@ for (const testOpts of testMatrix) { }); } - const resources = client.getContainer(containerId, schema, "2.0.0"); + const resources = client.getContainer(containerId, schema, pkgVersion); await assert.doesNotReject( resources, () => true, @@ -101,9 +101,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -122,7 +122,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, schema, - "2.0.0", + pkgVersion, ); const map1Get = containerGet.initialObjects.map1; const valueGet: string | undefined = await mapWait(map1Get, "new-key"); @@ -150,9 +150,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, doSchema, "2.0.0")); + ({ container } = await client.getContainer(containerId, doSchema, pkgVersion)); } else { - ({ container } = await client.createContainer(doSchema, "2.0.0")); + ({ container } = await client.createContainer(doSchema, pkgVersion)); containerId = await container.attach(); } @@ -176,7 +176,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2.0.0", + pkgVersion, ); const initialObjectsGet = containerGet.initialObjects; assert( @@ -213,9 +213,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, doSchema, "2.0.0")); + ({ container } = await client.getContainer(containerId, doSchema, pkgVersion)); } else { - ({ container } = await client.createContainer(doSchema, "2.0.0")); + ({ container } = await client.createContainer(doSchema, pkgVersion)); containerId = await container.attach(); } @@ -243,7 +243,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2.0.0", + pkgVersion, ); const initialObjectsGet = containerGet.initialObjects; assert( @@ -282,7 +282,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container } = await client.createContainer(doSchema, "2.0.0")); + ({ container } = await client.createContainer(doSchema, pkgVersion)); const initialObjectsCreate = container.initialObjects; const mdo2 = initialObjectsCreate.mdo2 as CounterTestDataObject; @@ -305,7 +305,7 @@ for (const testOpts of testMatrix) { const { container: containerGet } = await client.getContainer( containerId, doSchema, - "2.0.0", + pkgVersion, ); const initialObjectsGet = containerGet.initialObjects; const mdo2get = initialObjectsGet.mdo2 as CounterTestDataObject; @@ -341,9 +341,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, dynamicSchema, "2.0.0")); + ({ container } = await client.getContainer(containerId, dynamicSchema, pkgVersion)); } else { - ({ container } = await client.createContainer(dynamicSchema, "2.0.0")); + ({ container } = await client.createContainer(dynamicSchema, pkgVersion)); containerId = await container.attach(); } diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts index 1d94eef0c3b7..a18fbb2a49e8 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/childClient.tool.ts @@ -31,6 +31,7 @@ import { timeoutPromise } from "@fluidframework/test-utils/internal"; import { createAzureTokenProvider } from "../AzureTokenFactory.js"; import { TestDataObject } from "../TestDataObject.js"; +import { pkgVersion } from "../utils.js"; import type { MessageFromChild as MessageToParent, @@ -145,13 +146,13 @@ const getOrCreateContainer = async (params: { }); let services: AzureContainerServices; if (containerId === undefined) { - ({ container, services } = await client.createContainer(containerSchema, "2.0.0")); + ({ container, services } = await client.createContainer(containerSchema, pkgVersion)); containerId = await container.attach(); } else { ({ container, services } = await client.getContainer( containerId, containerSchema, - "2.0.0", + pkgVersion, )); } container.on("disconnected", onDisconnected); diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts index d770c1ee77a0..13082c50887c 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/signals.spec.ts @@ -19,7 +19,7 @@ import { } from "./AzureClientFactory.js"; import { SignalerTestDataObject } from "./TestDataObject.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { configProvider, getTestMatrix } from "./utils.js"; +import { configProvider, getTestMatrix, pkgVersion } from "./utils.js"; interface UserIdAndName { readonly id: string; @@ -108,14 +108,18 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container, services } = await client.getContainer( + containerId, + schema, + pkgVersion, + )); } else { - ({ container, services } = await client.createContainer(schema, "2.0.0")); + ({ container, services } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } } else { containerId = id; - ({ container, services } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container, services } = await client.getContainer(containerId, schema, pkgVersion)); } if (container.connectionState !== ConnectionState.Connected) { diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts index a5d798e9227d..a689d8c5a779 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/tree.spec.ts @@ -20,7 +20,7 @@ import { getContainerIdFromPayloadResponse, } from "./AzureClientFactory.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { getTestMatrix } from "./utils.js"; +import { getTestMatrix, pkgVersion } from "./utils.js"; const sf = new SchemaFactory("d302b84c-75f6-4ecd-9663-524f467013e3"); @@ -87,7 +87,7 @@ for (const testOpts of testMatrix) { let treeData: TreeView; if (summaryTree === undefined) { - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); treeData = container.initialObjects.tree1.viewWith(treeConfiguration); treeData.initialize(new StringArray([])); containerId = await container.attach(); @@ -100,7 +100,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); - const { container } = await client.getContainer(containerId, schema, "2.0.0"); + const { container } = await client.getContainer(containerId, schema, pkgVersion); treeData = container.initialObjects.tree1.viewWith(treeConfiguration); await waitForConnection(container); } @@ -137,7 +137,7 @@ for (const testOpts of testMatrix) { treeData.root.insertNew("test string 1"); - const resources = client.getContainer(containerId, schema, "2.0.0"); + const resources = client.getContainer(containerId, schema, pkgVersion); await assert.doesNotReject( resources, () => true, @@ -172,7 +172,7 @@ for (const testOpts of testMatrix) { }) {} it("can read and edit data", async () => { - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ schema: User, enableSchemaValidation: true }), @@ -213,7 +213,7 @@ for (const testOpts of testMatrix) { }); it("can handle undo/redo and transactions", async () => { - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); await container.attach(); const view = asAlpha( container.initialObjects.tree1.viewWith( @@ -262,7 +262,7 @@ for (const testOpts of testMatrix) { it("can use identifiers and the static Tree APIs", async () => { class Widget extends sf.object("Widget", { id: sf.identifier }) {} - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ @@ -297,7 +297,7 @@ for (const testOpts of testMatrix) { }) {} allowUnused>(); - const { container } = await client.createContainer(schema, "2.0.0"); + const { container } = await client.createContainer(schema, pkgVersion); await container.attach(); const view = container.initialObjects.tree1.viewWith( new TreeViewConfiguration({ schema: Doll, enableSchemaValidation: true }), diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/tsconfig.json b/packages/service-clients/end-to-end-tests/azure-client/src/test/tsconfig.json index 3f0b3018a758..51e495c6da03 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/tsconfig.json +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/tsconfig.json @@ -1,11 +1,11 @@ { "extends": "../../../../../../common/build/build-common/tsconfig.test.node16.json", "compilerOptions": { - "rootDir": "./", - "outDir": "../../lib/test", + "rootDir": "../", + "outDir": "../../lib", "types": ["mocha", "node"], "noUncheckedIndexedAccess": false, "exactOptionalPropertyTypes": false, }, - "include": ["./**/*"], + "include": ["./**/*", "../packageVersion.ts"], } diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/utils.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/utils.ts index 4b43de4ec4a0..9461e20e0cbe 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/utils.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/utils.ts @@ -8,6 +8,8 @@ import type { ConfigTypes, IConfigProviderBase } from "@fluidframework/core-inte import type { IMember } from "@fluidframework/fluid-static"; import type { ISharedMap, IValueChanged } from "@fluidframework/map/legacy"; +export { pkgVersion } from "../packageVersion.js"; + export const waitForMember = async ( audience: IAzureAudience, id: string, diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts index 4e0e499a5b70..2929a5b8d2c9 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/viewContainerVersion.spec.ts @@ -17,7 +17,7 @@ import { getContainerIdFromPayloadResponse, } from "./AzureClientFactory.js"; import * as ephemeralSummaryTrees from "./ephemeralSummaryTrees.js"; -import { getTestMatrix } from "./utils.js"; +import { getTestMatrix, pkgVersion } from "./utils.js"; const testMatrix = getTestMatrix(); for (const testOpts of testMatrix) { @@ -63,9 +63,9 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); } @@ -126,7 +126,7 @@ for (const testOpts of testMatrix) { ); containerId = getContainerIdFromPayloadResponse(containerResponse); } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); containerId = await container.attach(); if (container.connectionState !== ConnectionState.Connected) { @@ -143,7 +143,7 @@ for (const testOpts of testMatrix) { containerId, schema, versions[0], - "2.0.0", + pkgVersion, ); await assert.doesNotReject(viewContainerVersionAttempt); const { container: containerView } = await viewContainerVersionAttempt; @@ -169,10 +169,10 @@ for (const testOpts of testMatrix) { "test-user-name-1", ); containerId = getContainerIdFromPayloadResponse(containerResponse); - ({ container } = await client.getContainer(containerId, schema, "2.0.0")); + ({ container } = await client.getContainer(containerId, schema, pkgVersion)); map1 = container.initialObjects.map1 as SharedMap; } else { - ({ container } = await client.createContainer(schema, "2.0.0")); + ({ container } = await client.createContainer(schema, pkgVersion)); map1 = container.initialObjects.map1 as SharedMap; map1.set(testKey, expectedValue); @@ -197,7 +197,7 @@ for (const testOpts of testMatrix) { containerId, schema, versions[versions.length - 1], - "2.0.0", + pkgVersion, ); await assert.doesNotReject(viewContainerVersionAttempt); const { container: containerView } = await viewContainerVersionAttempt; @@ -216,7 +216,7 @@ for (const testOpts of testMatrix) { { id: "whatever", }, - "2.0.0", + pkgVersion, ); const errorFn = (error: Error): boolean => { assert.notStrictEqual(error.message, undefined, "Azure Client error is undefined"); From d65eeeb1628674719eb30cad29f4f43bf9cbfff7 Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 16:07:03 -0400 Subject: [PATCH 17/18] lint --- .../azure-client/external-controller/src/app.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/service-clients/azure-client/external-controller/src/app.ts b/examples/service-clients/azure-client/external-controller/src/app.ts index a3e505e074e6..e1c0863af1d4 100644 --- a/examples/service-clients/azure-client/external-controller/src/app.ts +++ b/examples/service-clients/azure-client/external-controller/src/app.ts @@ -44,7 +44,10 @@ async function start(): Promise { if (createNew) { // The client will create a new detached container using the schema // A detached container will enable the app to modify the container before attaching it to the client - ({ container, services } = await client.createContainer(diceRollerContainerSchema, "2.0.0")); + ({ container, services } = await client.createContainer( + diceRollerContainerSchema, + "2.0.0", + )); // const map1 = container.initialObjects.map1 as ISharedMap; // map1.set("diceValue", 1); // const map2 = container.initialObjects.map1 as ISharedMap; @@ -63,7 +66,11 @@ async function start(): Promise { id = location.hash.slice(1); // Use the unique container ID to fetch the container created earlier. It will already be connected to the // collaboration session. - ({ container, services } = await client.getContainer(id, diceRollerContainerSchema, "2.0.0")); + ({ container, services } = await client.getContainer( + id, + diceRollerContainerSchema, + "2.0.0", + )); appModel = loadAppFromExistingContainer(container); } From a872826698682391d1df2d936b24561084a2ec7f Mon Sep 17 00:00:00 2001 From: Scott Norton Date: Tue, 19 May 2026 16:29:36 -0400 Subject: [PATCH 18/18] update odsp client --- .../odsp-client/shared-tree-demo/src/fluid.ts | 4 +- .../end-to-end-tests/odsp-client/package.json | 1 + .../odsp-client/src/packageVersion.ts | 9 +++ .../src/test/containerCreate.spec.ts | 14 ++-- .../odsp-client/src/test/tsconfig.json | 6 +- .../service-clients/odsp-client/README.md | 6 +- .../api-report/odsp-client.alpha.api.md | 12 +++- .../api-report/odsp-client.beta.api.md | 12 +++- .../service-clients/odsp-client/package.json | 1 + .../odsp-client/src/odspClient.ts | 72 +++++++++++++++++-- .../odsp-client/src/test/odspClient.spec.ts | 9 ++- pnpm-lock.yaml | 3 + 12 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 packages/service-clients/end-to-end-tests/odsp-client/src/packageVersion.ts diff --git a/examples/service-clients/odsp-client/shared-tree-demo/src/fluid.ts b/examples/service-clients/odsp-client/shared-tree-demo/src/fluid.ts index 0c02f0924993..ffe2cef657c1 100644 --- a/examples/service-clients/odsp-client/shared-tree-demo/src/fluid.ts +++ b/examples/service-clients/odsp-client/shared-tree-demo/src/fluid.ts @@ -23,7 +23,7 @@ export async function loadFluidData( services: OdspContainerServices; container: IFluidContainer; }> { - const { container, services } = await client.getContainer(itemId, schema); + const { container, services } = await client.getContainer(itemId, schema, "2.0.0"); return { services, container }; } @@ -36,7 +36,7 @@ export async function createFluidData( }> { // The client will create a new detached container using the schema // A detached container will enable the app to modify the container before attaching it to the client - const { container, services } = await client.createContainer(schema); + const { container, services } = await client.createContainer(schema, "2.0.0"); return { services, container }; } diff --git a/packages/service-clients/end-to-end-tests/odsp-client/package.json b/packages/service-clients/end-to-end-tests/odsp-client/package.json index bf0542a4e9de..de666ecea342 100644 --- a/packages/service-clients/end-to-end-tests/odsp-client/package.json +++ b/packages/service-clients/end-to-end-tests/odsp-client/package.json @@ -15,6 +15,7 @@ "scripts": { "build": "fluid-build . --task build", "build:compile": "fluid-build . --task compile", + "build:genver": "gen-version", "build:test": "tsc --project ./src/test/tsconfig.json", "check:biome": "biome check .", "check:format": "npm run check:biome", diff --git a/packages/service-clients/end-to-end-tests/odsp-client/src/packageVersion.ts b/packages/service-clients/end-to-end-tests/odsp-client/src/packageVersion.ts new file mode 100644 index 000000000000..64181dcb5702 --- /dev/null +++ b/packages/service-clients/end-to-end-tests/odsp-client/src/packageVersion.ts @@ -0,0 +1,9 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + * + * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY + */ + +export const pkgName = "@fluid-experimental/odsp-end-to-end-tests"; +export const pkgVersion = "2.102.0"; diff --git a/packages/service-clients/end-to-end-tests/odsp-client/src/test/containerCreate.spec.ts b/packages/service-clients/end-to-end-tests/odsp-client/src/test/containerCreate.spec.ts index 24cf43a4f46a..dad5e2eff2d0 100644 --- a/packages/service-clients/end-to-end-tests/odsp-client/src/test/containerCreate.spec.ts +++ b/packages/service-clients/end-to-end-tests/odsp-client/src/test/containerCreate.spec.ts @@ -12,6 +12,8 @@ import { SharedMap } from "@fluidframework/map/internal"; import type { OdspClient } from "@fluidframework/odsp-client/internal"; import { timeoutPromise } from "@fluidframework/test-utils/internal"; +import { pkgVersion } from "../packageVersion.js"; + import { createOdspClient, getCredentials } from "./OdspClientFactory.js"; describe("Container create scenarios", () => { @@ -42,7 +44,7 @@ describe("Container create scenarios", () => { * be returned. */ it("Created container is detached", async () => { - const { container } = await client.createContainer(schema); + const { container } = await client.createContainer(schema, pkgVersion); assert.strictEqual( container.attachState, AttachState.Detached, @@ -61,7 +63,7 @@ describe("Container create scenarios", () => { * be returned. */ it("can attach a container", async () => { - const { container } = await client.createContainer(schema); + const { container } = await client.createContainer(schema, pkgVersion); const itemId = await container.attach(); if (container.connectionState !== ConnectionState.Connected) { @@ -86,7 +88,7 @@ describe("Container create scenarios", () => { * be returned. */ it("cannot attach a container twice", async () => { - const { container } = await client.createContainer(schema); + const { container } = await client.createContainer(schema, pkgVersion); const itemId = await container.attach(); if (container.connectionState !== ConnectionState.Connected) { @@ -112,7 +114,7 @@ describe("Container create scenarios", () => { * be returned. */ it("can retrieve existing ODSP container successfully", async () => { - const { container: newContainer } = await client.createContainer(schema); + const { container: newContainer } = await client.createContainer(schema, pkgVersion); const itemId = await newContainer.attach(); if (newContainer.connectionState !== ConnectionState.Connected) { @@ -122,7 +124,7 @@ describe("Container create scenarios", () => { }); } - const resources = client.getContainer(itemId, schema); + const resources = client.getContainer(itemId, schema, pkgVersion); await assert.doesNotReject( resources, () => true, @@ -136,7 +138,7 @@ describe("Container create scenarios", () => { * Expected behavior: an error should be thrown when trying to get a non-existent container. */ it("cannot load improperly created container (cannot load a non-existent container)", async () => { - const containerAndServicesP = client.getContainer("containerConfig", schema); + const containerAndServicesP = client.getContainer("containerConfig", schema, pkgVersion); const errorFn = (error: Error): boolean => { assert.notStrictEqual(error.message, undefined, "Odsp Client error is undefined"); diff --git a/packages/service-clients/end-to-end-tests/odsp-client/src/test/tsconfig.json b/packages/service-clients/end-to-end-tests/odsp-client/src/test/tsconfig.json index 3c20a3790638..46f4c2894afe 100644 --- a/packages/service-clients/end-to-end-tests/odsp-client/src/test/tsconfig.json +++ b/packages/service-clients/end-to-end-tests/odsp-client/src/test/tsconfig.json @@ -1,10 +1,10 @@ { "extends": "../../../../../../common/build/build-common/tsconfig.test.node16.json", "compilerOptions": { - "rootDir": "./", - "outDir": "../../lib/test", + "rootDir": "../", + "outDir": "../../lib", "types": ["mocha", "node"], "exactOptionalPropertyTypes": false, }, - "include": ["./**/*"], + "include": ["./**/*", "../packageVersion.ts"], } diff --git a/packages/service-clients/odsp-client/README.md b/packages/service-clients/odsp-client/README.md index 42e187eb7476..8a7f3073e4fb 100644 --- a/packages/service-clients/odsp-client/README.md +++ b/packages/service-clients/odsp-client/README.md @@ -104,7 +104,7 @@ const containerSchema = { ], }; const odspClient = new OdspClient(clientProps); -const { container, services } = await odspClient.createContainer(containerSchema); +const { container, services } = await odspClient.createContainer(containerSchema, "2.0.0"); const itemId = await container.attach(); ``` @@ -117,7 +117,7 @@ Using the `OdspClient` class the developer can create and get Fluid containers. import { OdspClient } from "@fluidframework/odsp-client"; const odspClient = new OdspClient(props); -const { container, services } = await odspClient.getContainer("_unique-itemId_", schema); +const { container, services } = await odspClient.getContainer("_unique-itemId_", schema, "2.0.0"); ``` ## Using initial objects @@ -137,7 +137,7 @@ const schema = { }; // Fetch back the container that had been created earlier with the same itemId and schema -const { container, services } = await OdspClient.getContainer("_unique-itemId_", schema); +const { container, services } = await OdspClient.getContainer("_unique-itemId_", schema, "2.0.0"); // Get our list of initial objects that we had defined in the schema. initialObjects here will have the same signature const initialObjects = container.initialObjects; diff --git a/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md b/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md index 0c9ed9b7c689..5ef196e7c7b9 100644 --- a/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md +++ b/packages/service-clients/odsp-client/api-report/odsp-client.alpha.api.md @@ -28,12 +28,20 @@ export interface IOdspTokenProvider { // @beta @sealed export class OdspClient { constructor(properties: OdspClientProps); - // (undocumented) + createContainer(containerSchema: T, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IOdspFluidContainer; + services: OdspContainerServices; + }>; + // @deprecated createContainer(containerSchema: T): Promise<{ container: IOdspFluidContainer; services: OdspContainerServices; }>; - // (undocumented) + getContainer(id: string, containerSchema: T, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IOdspFluidContainer; + services: OdspContainerServices; + }>; + // @deprecated getContainer(id: string, containerSchema: T): Promise<{ container: IOdspFluidContainer; services: OdspContainerServices; diff --git a/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md b/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md index 5ff2a629caca..05f9098d6dab 100644 --- a/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md +++ b/packages/service-clients/odsp-client/api-report/odsp-client.beta.api.md @@ -28,12 +28,20 @@ export interface IOdspTokenProvider { // @beta @sealed export class OdspClient { constructor(properties: OdspClientProps); - // (undocumented) + createContainer(containerSchema: T, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IOdspFluidContainer; + services: OdspContainerServices; + }>; + // @deprecated createContainer(containerSchema: T): Promise<{ container: IOdspFluidContainer; services: OdspContainerServices; }>; - // (undocumented) + getContainer(id: string, containerSchema: T, minVersionForCollab: MinimumVersionForCollab): Promise<{ + container: IOdspFluidContainer; + services: OdspContainerServices; + }>; + // @deprecated getContainer(id: string, containerSchema: T): Promise<{ container: IOdspFluidContainer; services: OdspContainerServices; diff --git a/packages/service-clients/odsp-client/package.json b/packages/service-clients/odsp-client/package.json index 844f2be04a08..4174cc664f77 100644 --- a/packages/service-clients/odsp-client/package.json +++ b/packages/service-clients/odsp-client/package.json @@ -115,6 +115,7 @@ "@fluidframework/odsp-doclib-utils": "workspace:~", "@fluidframework/odsp-driver": "workspace:~", "@fluidframework/odsp-driver-definitions": "workspace:~", + "@fluidframework/runtime-definitions": "workspace:~", "@fluidframework/telemetry-utils": "workspace:~", "uuid": "^11.1.0" }, diff --git a/packages/service-clients/odsp-client/src/odspClient.ts b/packages/service-clients/odsp-client/src/odspClient.ts index 1f5868169abd..d661ab93309f 100644 --- a/packages/service-clients/odsp-client/src/odspClient.ts +++ b/packages/service-clients/odsp-client/src/odspClient.ts @@ -34,6 +34,7 @@ import { isOdspResolvedUrl, } from "@fluidframework/odsp-driver/internal"; import type { OdspResourceTokenFetchOptions } from "@fluidframework/odsp-driver-definitions/internal"; +import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions"; import { wrapConfigProviderWithDefaults } from "@fluidframework/telemetry-utils/internal"; import { v4 as uuid } from "uuid"; @@ -111,13 +112,41 @@ export class OdspClient { this.configProvider = wrapConfigProvider(properties.configProvider); } + /** + * Creates a new detached container instance backed by ODSP. + * @param containerSchema - Container schema for the new container. + * @param minVersionForCollab - Minimum Fluid Framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`). + * @returns New detached container instance along with associated services. + */ public async createContainer( containerSchema: T, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IOdspFluidContainer; + services: IOdspContainerServices; + }>; + /** + * Creates a new detached container instance backed by ODSP. + * @param containerSchema - Container schema for the new container. + * @returns New detached container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) as a second argument. + * The previous behavior was equivalent to passing `"2.0.0"`. + */ + public async createContainer( + containerSchema: T, + ): Promise<{ + container: IOdspFluidContainer; + services: IOdspContainerServices; + }>; + public async createContainer( + containerSchema: T, + minVersionForCollab: MinimumVersionForCollab = "2.0.0", ): Promise<{ container: IOdspFluidContainer; services: IOdspContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema); + const loaderProps = this.getLoaderProps(containerSchema, minVersionForCollab); const container = await createDetachedContainer({ ...loaderProps, @@ -137,14 +166,46 @@ export class OdspClient { return { container: fluidContainer, services }; } + /** + * Accesses an existing container by its unique ID in ODSP. + * @param id - Unique ID of the container in ODSP. + * @param containerSchema - Container schema used to access data objects in the container. + * @param minVersionForCollab - Minimum Fluid Framework version required for collaboration, as a + * `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`). + * @returns Existing container instance along with associated services. + */ + public async getContainer( + id: string, + containerSchema: T, + minVersionForCollab: MinimumVersionForCollab, + ): Promise<{ + container: IOdspFluidContainer; + services: IOdspContainerServices; + }>; + /** + * Accesses an existing container by its unique ID in ODSP. + * @param id - Unique ID of the container in ODSP. + * @param containerSchema - Container schema used to access data objects in the container. + * @returns Existing container instance along with associated services. + * @deprecated Pass a `MinimumVersionForCollab` semver string (e.g. `"2.0.0"`) as a third argument. + * The previous behavior was equivalent to passing `"2.0.0"`. + */ + public async getContainer( + id: string, + containerSchema: T, + ): Promise<{ + container: IOdspFluidContainer; + services: IOdspContainerServices; + }>; public async getContainer( id: string, containerSchema: T, + minVersionForCollab: MinimumVersionForCollab = "2.0.0", ): Promise<{ container: IOdspFluidContainer; services: IOdspContainerServices; }> { - const loaderProps = this.getLoaderProps(containerSchema); + const loaderProps = this.getLoaderProps(containerSchema, minVersionForCollab); const url = createOdspUrl({ siteUrl: this.connectionConfig.siteUrl, driveId: this.connectionConfig.driveId, @@ -163,10 +224,13 @@ export class OdspClient { return { container: fluidContainer, services }; } - private getLoaderProps(schema: ContainerSchema): ILoaderProps { + private getLoaderProps( + schema: ContainerSchema, + minVersionForCollab: MinimumVersionForCollab, + ): ILoaderProps { const runtimeFactory = createDOProviderContainerRuntimeFactory({ schema, - compatibilityMode: "2.0.0", + compatibilityMode: minVersionForCollab, }); const load = async (): Promise => { return { diff --git a/packages/service-clients/odsp-client/src/test/odspClient.spec.ts b/packages/service-clients/odsp-client/src/test/odspClient.spec.ts index 2d613b44ea1b..58454355b300 100644 --- a/packages/service-clients/odsp-client/src/test/odspClient.spec.ts +++ b/packages/service-clients/odsp-client/src/test/odspClient.spec.ts @@ -75,7 +75,7 @@ describe("OdspClient", () => { * be returned. */ it("can create new ODSP container successfully", async () => { - const resourcesP = client.createContainer(schema); + const resourcesP = client.createContainer(schema, "2.0.0"); await assert.doesNotReject(resourcesP, () => true, "container cannot be created in ODSP"); }); @@ -88,7 +88,7 @@ describe("OdspClient", () => { * be returned. */ it("created container is detached", async () => { - const { container } = await client.createContainer(schema); + const { container } = await client.createContainer(schema, "2.0.0"); assert.strictEqual( container.attachState, AttachState.Detached, @@ -97,7 +97,10 @@ describe("OdspClient", () => { }); it("GC is disabled by default", async () => { - const { container: container_defaultConfig } = await client.createContainer(schema); + const { container: container_defaultConfig } = await client.createContainer( + schema, + "2.0.0", + ); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { sweepEnabled, throwOnTombstoneLoad } = // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0388a184a177..1c31fc5cc311 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14104,6 +14104,9 @@ importers: '@fluidframework/odsp-driver-definitions': specifier: workspace:~ version: link:../../drivers/odsp-driver-definitions + '@fluidframework/runtime-definitions': + specifier: workspace:~ + version: link:../../runtime/runtime-definitions '@fluidframework/telemetry-utils': specifier: workspace:~ version: link:../../utils/telemetry-utils