Contents
- 🚨 Breaking Changes
- Removal of direct CommonJS support (#27846)
- Require modern TypeScript module resolution (#27846)
- Remove deprecated compatibility mode APIs (#27909)
- Require a log level for every telemetry event (#27982)
- Require oldest supported clients to use Fluid Framework 2.0 or later (#28127)
- Build with TypeScript 6 (#27846)
- Deprecated log level aliases have been removed (#27983)
- Container APIs use Fluid-owned collection types (#27908)
- Pending local state uses Fluid-owned iterator types (#27908)
- Presence maps use Fluid-owned iterator types (#27908)
- Shared maps use Fluid-owned map types (#27908)
- Tree containers use Fluid-owned collection types (#27908)
- ✨ New Features
- Enable point-in-time loading on the standard ODSP document service factory (#28007)
- Add reference-only capture for reusable container baselines (#28006)
- New ILayerCompatSupportRequirements property on LocalDocumentServiceFactory and OdspDocumentServiceFactoryCore (#27516)
- Collaborative text editors can track selections across edits (#27907)
- Add server timestamps to beta version mark resolution APIs (#28037)
- 🌳 SharedTree DDS Changes
- Fixed bug in no-change constraint revert precondition (#27989)
- Bug fix: forking during changed event callback is now safe (#28008)
- asAlpha now supports ITree (#28011)
- Formatted text uniform runs now account for optional formatting fields (#28014)
- Rename TreeBranch and TreeBranchAlpha to UntypedTreeView (#27932)
- Remove the deprecated TreeAlpha.branch API (#27932)
- The treeChanged event reserves its listener argument for event data (#27951)
- Deprecated Tree APIs are removed for 3.0 (#27911)
- retainHistory now retains history in summaries (#28036)
- SharedTree schema errors now explain the mismatch (#27950)
- Text nodes can now track insertion positions across edits (#28045)
- New alpha APIs for inspecting history and restoring past states (#28012)
- TreeViewAlpha can now query whether a staged schema upgrade has been applied (#27829)
- Promote array node change event deltas to beta (#27942)
- New alpha API for attaching custom metadata to commits (#28064)
- Tree contexts now support beta transactions (#28116)
- Bug fix: commit metadata access after history trimming (#28135)
- Expose schema incompatibility details on TreeViewBeta (#28142)
- Create an independent TreeView with beta APIs (#28138)
- Schema compatibility snapshots support custom filename prefixes and suffixes (#28100)
- Preserve enabled staged schema upgrades by default (#28165)
- 🐛 Bug Fixes
⚠️ Deprecations- Legacy API Changes
- Other Changes
🚨 Breaking Changes
Removal of direct CommonJS support (#27846)
Direct require() import is no longer directly supported. Package is transpiled as ECMAScript Module.
See Removal of direct CommonJS support in v3.0 for more information.
Change details
Commit: 91c7854
Affected packages:
- @fluid-experimental/attributor
- @fluid-experimental/data-objects
- @fluid-experimental/dds-interceptions
- @fluid-experimental/ink
- @fluid-experimental/last-edited
- @fluid-experimental/oldest-client-observer
- @fluid-experimental/ot
- @fluid-experimental/pact-map
- @fluid-experimental/property-changeset
- @fluid-experimental/property-common
- @fluid-experimental/property-dds
- @fluid-experimental/property-properties
- @fluid-experimental/sequence-deprecated
- @fluid-experimental/sharejs-json1
- @fluid-experimental/tree
- @fluid-private/stochastic-test-utils
- @fluid-private/test-dds-utils
- @fluid-private/test-drivers
- @fluid-private/test-end-to-end-tests
- @fluid-private/test-loader-utils
- @fluid-private/test-pairwise-generator
- @fluidframework/agent-scheduler
- @fluidframework/app-insights-logger
- @fluidframework/aqueduct
- @fluidframework/azure-client
- @fluidframework/azure-service-utils
- @fluidframework/cell
- @fluidframework/container-definitions
- @fluidframework/container-loader
- @fluidframework/container-runtime
- @fluidframework/container-runtime-definitions
- @fluidframework/core-interfaces
- @fluidframework/core-utils
- @fluidframework/counter
- @fluidframework/datastore
- @fluidframework/datastore-definitions
- @fluidframework/debugger
- @fluidframework/devtools
- @fluidframework/devtools-core
- @fluidframework/driver-base
- @fluidframework/driver-definitions
- @fluidframework/driver-utils
- @fluidframework/driver-web-cache
- @fluidframework/file-driver
- @fluidframework/fluid-runner
- @fluidframework/fluid-static
- @fluidframework/fluid-telemetry
- @fluidframework/id-compressor
- @fluidframework/legacy-dds
- @fluidframework/local-driver
- @fluidframework/map
- @fluidframework/matrix
- @fluidframework/merge-tree
- @fluidframework/odsp-client
- @fluidframework/odsp-doclib-utils
- @fluidframework/odsp-driver
- @fluidframework/odsp-driver-definitions
- @fluidframework/odsp-urlresolver
- @fluidframework/ordered-collection
- @fluidframework/presence
- @fluidframework/register-collection
- @fluidframework/replay-driver
- @fluidframework/request-handler
- @fluidframework/routerlicious-driver
- @fluidframework/routerlicious-urlresolver
- @fluidframework/runtime-definitions
- @fluidframework/runtime-utils
- @fluidframework/sequence
- @fluidframework/shared-object-base
- @fluidframework/shared-summary-block
- @fluidframework/synthesize
- @fluidframework/task-manager
- @fluidframework/telemetry-utils
- @fluidframework/test-runtime-utils
- @fluidframework/test-utils
- @fluidframework/tinylicious-client
- @fluidframework/tinylicious-driver
- @fluidframework/tool-utils
- @fluidframework/tree
- @fluidframework/tree-agent
- @fluidframework/tree-agent-langchain
- @fluidframework/tree-agent-ses
- @fluidframework/type-factory
- @fluidframework/undo-redo
- fluid-framework
Require modern TypeScript module resolution (#27846)
Fluid Framework Client packages no longer include type declaration compatibility entrypoints for TypeScript's legacy Node10 resolution mode ("moduleResolution": "node" or "node10"). Applications upgrading to Fluid Framework 3.0 must use one of the following supported configurations:
"module": "Node16"with"moduleResolution": "Node16""module": "NodeNext"with"moduleResolution": "NodeNext""module": "ESNext"with"moduleResolution": "Bundler"
Existing public package entrypoints exposed through package.json exports, including /alpha, /beta, and /legacy, remain available under supported module resolution modes.
See Removal of Node10 resolutions in v3.0 for more information.
Change details
Commit: 91c7854
Affected packages:
- @fluid-experimental/oldest-client-observer
- @fluidframework/agent-scheduler
- @fluidframework/app-insights-logger
- @fluidframework/aqueduct
- @fluidframework/azure-client
- @fluidframework/azure-service-utils
- @fluidframework/cell
- @fluidframework/container-definitions
- @fluidframework/container-loader
- @fluidframework/container-runtime
- @fluidframework/container-runtime-definitions
- @fluidframework/core-interfaces
- @fluidframework/core-utils
- @fluidframework/counter
- @fluidframework/datastore
- @fluidframework/datastore-definitions
- @fluidframework/debugger
- @fluidframework/devtools
- @fluidframework/devtools-core
- @fluidframework/driver-base
- @fluidframework/driver-definitions
- @fluidframework/driver-utils
- @fluidframework/driver-web-cache
- @fluidframework/file-driver
- @fluidframework/fluid-runner
- @fluidframework/fluid-static
- @fluidframework/fluid-telemetry
- @fluidframework/id-compressor
- @fluidframework/legacy-dds
- @fluidframework/local-driver
- @fluidframework/map
- @fluidframework/matrix
- @fluidframework/merge-tree
- @fluidframework/odsp-client
- @fluidframework/odsp-doclib-utils
- @fluidframework/odsp-driver
- @fluidframework/odsp-driver-definitions
- @fluidframework/odsp-urlresolver
- @fluidframework/ordered-collection
- @fluidframework/register-collection
- @fluidframework/replay-driver
- @fluidframework/request-handler
- @fluidframework/routerlicious-driver
- @fluidframework/routerlicious-urlresolver
- @fluidframework/runtime-definitions
- @fluidframework/runtime-utils
- @fluidframework/sequence
- @fluidframework/shared-object-base
- @fluidframework/shared-summary-block
- @fluidframework/synthesize
- @fluidframework/task-manager
- @fluidframework/telemetry-utils
- @fluidframework/test-runtime-utils
- @fluidframework/test-utils
- @fluidframework/tinylicious-client
- @fluidframework/tinylicious-driver
- @fluidframework/tool-utils
- @fluidframework/tree
- @fluidframework/tree-agent
- @fluidframework/tree-agent-langchain
- @fluidframework/tree-agent-ses
- @fluidframework/type-factory
- @fluidframework/undo-redo
- fluid-framework
Remove deprecated compatibility mode APIs (#27909)
Deprecated CompatibilityMode exports and overloads have been removed from @fluidframework/fluid-static, @fluidframework/azure-client, and @fluidframework/tinylicious-client.
Use OldestSupportedClientVersion SemVer strings instead:
- Pass
oldestSupportedClienttocreateTreeContainerRuntimeFactory. - Pass an
OldestSupportedClientVersionas theoldestSupportedClientargument toAzureClient.createContainer,AzureClient.getContainer,AzureClient.viewContainerVersion,TinyliciousClient.createContainer, andTinyliciousClient.getContainer. - Legacy mode
"1"has no Client 3.0 equivalent. Upgrade every collaborating 1.x deployment before adopting Client 3.0. - Replace legacy mode
"2"witholdestSupportedClient: "2.0.0"or a later supported version.
Client 3.0 requires oldestSupportedClient values of "2.0.0" or later.
See Remove CompatibilityMode and advance the minimum collaboration version to 2.0.0 for more information.
Change details
Commit: 103cc0b
Affected packages:
- @fluidframework/fluid-static
- @fluidframework/azure-client
- @fluidframework/tinylicious-client
Require a log level for every telemetry event (#27982)
The logLevel parameter of ITelemetryBaseLogger.send and the inherited ITelemetryLoggerExt.send is now required. Callers must select a LogLevel for every event they log.
Explicitly specifying a level makes logging intent part of every call site, which enables consistent filtering and sampling of telemetry.
Migration for callers
Pass a LogLevel for every event. To preserve the behavior of a call that previously omitted the level, use LogLevel.essential:
import { LogLevel } from "@fluidframework/core-interfaces";
// Before
logger.send({ category: "generic", eventName: "ExampleEvent" });
// After
logger.send(
{ category: "generic", eventName: "ExampleEvent" },
LogLevel.essential,
);Migration for logger implementations
This is a compile-time requirement on callers only; nothing about how events are dispatched at runtime has changed.
Logger implementations should keep declaring logLevel as optional and treat an omitted level as LogLevel.essential:
import {
LogLevel,
type ITelemetryBaseEvent,
type ITelemetryBaseLogger,
} from "@fluidframework/core-interfaces";
class MyLogger implements ITelemetryBaseLogger {
public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void {
const level = logLevel ?? LogLevel.essential;
// ...
}
}Fluid supports running with a mix of package versions, so code compiled before logLevel became required still calls send(event) with a single argument, and will for as long as those versions are supported. An implementation that assumes logLevel is always defined can therefore silently drop those events or handle them at the wrong level.
This layer-compatibility guidance can be retired only after the compatibility window for callers that may omit logLevel has closed in a future coordinated breaking change. See the ITelemetryBaseLogger API documentation and microsoft/FluidFramework#27595 for more information.
Change details
Commit: f2410e1
Affected packages:
- @fluidframework/core-interfaces
- @fluidframework/telemetry-utils
- @fluidframework/azure-client
- @fluid-internal/devtools-view
- fluid-framework
Require oldest supported clients to use Fluid Framework 2.0 or later (#28127)
Client 3.0 narrows OldestSupportedClientVersion to stable 2.x versions and 3.x minor checkpoints whose patch is zero. The deprecated MinimumVersionForCollab alias inherits the same restriction and remains available until Client 4.0.
Container runtimes now reject values below "2.0.0" and prerelease values. APIs that still permit the setting to be omitted use "2.0.0", with the same runtime defaults and validation as explicitly passing "2.0.0".
Before upgrading an application to Client 3.0, upgrade every active deployment that must collaborate to Fluid Framework 2.0.0 or later. Explicit compatibility settings must use the canonical property or Azure, ODSP, or Tinylicious service-client argument with a stable 2.x version or a 3.x minor checkpoint such as "3.1.0":
const { container } = await azureClient.getContainer(
id,
schema,
"2.0.0", // oldestSupportedClient
);See microsoft/FluidFramework#27460 for migration context.
Change details
Commit: 9895614
Affected packages:
- @fluidframework/runtime-definitions
- @fluidframework/container-runtime
- @fluidframework/datastore
- @fluidframework/tree
- @fluidframework/aqueduct
- @fluidframework/fluid-static
- @fluidframework/azure-client
- @fluidframework/odsp-client
- @fluidframework/tinylicious-client
- @fluidframework/test-runtime-utils
- @fluidframework/test-utils
- fluid-framework
Build with TypeScript 6 (#27846)
FluidFramework Client SDK is now built using TypeScript 6. Consumers should build with TypeScript v6 or v7 or compatible tooling.
Change details
Commit: 91c7854
Affected packages:
- @fluidframework/agent-scheduler
- @fluidframework/app-insights-logger
- @fluidframework/aqueduct
- @fluid-experimental/attributor
- @fluidframework/azure-client
- @fluidframework/azure-end-to-end-tests
- @fluidframework/azure-service-utils
- @fluidframework/cell
- @fluidframework/container-definitions
- @fluidframework/container-loader
- @fluidframework/container-runtime
- @fluidframework/container-runtime-definitions
- @fluidframework/core-interfaces
- @fluidframework/core-utils
- @fluidframework/counter
- @fluid-experimental/data-objects
- @fluidframework/datastore
- @fluidframework/datastore-definitions
- @fluid-experimental/dds-interceptions
- @fluidframework/debugger
- @fluidframework/devtools
- @fluidframework/devtools-core
- @fluidframework/driver-base
- @fluidframework/driver-definitions
- @fluidframework/driver-utils
- @fluidframework/driver-web-cache
- @fluid-tools/fetch-tool
- @fluidframework/file-driver
- fluid-framework
- @fluidframework/fluid-runner
- @fluidframework/fluid-static
- @fluidframework/fluid-telemetry
- @fluidframework/id-compressor
- @fluid-experimental/ink
- @fluid-experimental/last-edited
- @fluidframework/legacy-dds
- @fluidframework/local-driver
- @fluidframework/map
- @fluidframework/matrix
- @fluidframework/merge-tree
- @fluidframework/odsp-client
- @fluidframework/odsp-doclib-utils
- @fluidframework/odsp-driver
- @fluidframework/odsp-driver-definitions
- @fluid-experimental/odsp-end-to-end-tests
- @fluidframework/odsp-urlresolver
- @fluid-experimental/oldest-client-observer
- @fluidframework/ordered-collection
- @fluid-experimental/ot
- @fluid-experimental/pact-map
- @fluidframework/presence
- @fluid-experimental/property-changeset
- @fluid-experimental/property-common
- @fluid-experimental/property-dds
- @fluid-experimental/property-properties
- @fluidframework/quill-react
- @fluidframework/react
- @fluidframework/register-collection
- @fluidframework/replay-driver
- @fluidframework/request-handler
- @fluidframework/routerlicious-driver
- @fluidframework/routerlicious-urlresolver
- @fluidframework/runtime-definitions
- @fluidframework/runtime-utils
- @fluidframework/sequence
- @fluid-experimental/sequence-deprecated
- @fluidframework/shared-object-base
- @fluidframework/shared-summary-block
- @fluid-experimental/sharejs-json1
- @fluid-private/stochastic-test-utils
- @fluidframework/synthesize
- @fluidframework/task-manager
- @fluidframework/telemetry-utils
- @fluid-private/test-dds-utils
- @fluid-private/test-drivers
- @fluid-private/test-end-to-end-tests
- @fluid-private/test-loader-utils
- @fluid-private/test-pairwise-generator
- @fluidframework/test-runtime-utils
- @fluidframework/test-utils
- @fluid-private/test-version-utils
- @fluidframework/tinylicious-client
- @fluidframework/tinylicious-driver
- @fluidframework/tool-utils
- @fluid-experimental/tree
- @fluidframework/tree
- @fluidframework/tree-agent
- @fluidframework/tree-agent-langchain
- @fluidframework/tree-agent-ses
- @fluidframework/type-factory
- @fluidframework/undo-redo
Deprecated log level aliases have been removed (#27983)
The deprecated LogLevel values default and error have been removed. They were aliases for existing numeric levels, and the semantically clearer LogLevel.info (20) and LogLevel.essential (30) should be used instead.
Migration
The replacement for LogLevel.default depends on how the value is used:
- For an event's
logLevel(for example thelogLevelargument toITelemetryBaseLogger.send), useLogLevel.essential. - For a logger's
minLogLevelthreshold, useLogLevel.info.
The replacement for LogLevel.error is always LogLevel.essential.
// Before
// ...
logger.send(event, LogLevel.default);
logger.send(errorEvent, LogLevel.error);
logger.minLogLevel = LogLevel.default;
// ...
// After
// ...
logger.send(event, LogLevel.essential);
logger.send(errorEvent, LogLevel.essential);
logger.minLogLevel = LogLevel.info;
// ...See issue #26969 for removal tracking.
Change details
Commit: 5cfbf09
Affected packages:
- @fluidframework/core-interfaces
- fluid-framework
Container APIs use Fluid-owned collection types (#27908)
The aggregated ReadonlyArrayNode, TreeMapNode, TreeRecordNode, IDirectory, and ISharedMap APIs now use Fluid-owned collection and iterator interfaces instead of TypeScript's built-in container types. This prevents changes to TypeScript's standard library from introducing unintended requirements for Fluid implementations.
Migration
Most existing assignments remain structurally compatible. Methods available only on newer built-in collection types are not available on Fluid containers.
Change details
Commit: 5035f7c
Affected packages:
- fluid-framework
Pending local state uses Fluid-owned iterator types (#27908)
PendingLocalStateStore iteration methods now return FluidIterableIterator instead of TypeScript's built-in iterator types. This prevents changes to TypeScript's standard iterator interfaces from affecting the container loader API.
Migration
The returned iterators continue to support next(), spreading, and for...of. Methods available only on newer built-in iterator types are not available.
for (const [key, pendingState] of pendingLocalStateStore.entries()) {
// ...
}Change details
Commit: 5035f7c
Affected packages:
- @fluidframework/container-loader
Presence maps use Fluid-owned iterator types (#27908)
StateMap.keys() now returns FluidIterableIterator instead of TypeScript's built-in IterableIterator. This keeps the Presence API independent of additions to TypeScript's standard iterator interfaces.
Migration
The returned iterator continues to support next(), spreading, and for...of. Methods available only on newer built-in iterator types are not available.
for (const key of stateMap.keys()) {
// ...
}Change details
Commit: 5035f7c
Affected packages:
- @fluid-internal/presence-definitions
- @fluidframework/presence
Shared maps use Fluid-owned map types (#27908)
IDirectory and ISharedMap now extend the Fluid-owned FluidMap interface instead of TypeScript's built-in Map interface. IDirectoryBeta has been removed because IDirectory now provides the Fluid-owned map contract.
Migration
Consumers using IDirectoryBeta should use IDirectory instead. Methods available only on newer built-in map types are not available on shared maps.
Change details
Commit: 5035f7c
Affected packages:
- @fluidframework/map
Tree containers use Fluid-owned collection types (#27908)
ReadonlyArrayNode, TreeMapNode, and TreeRecordNode now use FluidReadonlyArray, FluidReadonlyMap, and FluidIterableIterator instead of TypeScript's built-in collection and iterator types. This prevents changes to TypeScript's standard library from introducing unintended requirements for Tree implementations.
Migration
Most existing assignments remain structurally compatible. Methods available only on newer built-in collection types, such as toReversed and toSorted, are not available on Tree containers.
Change details
Commit: 5035f7c
Affected packages:
- @fluidframework/tree
✨ New Features
Enable point-in-time loading on the standard ODSP document service factory (#28007)
OdspDocumentServiceFactoryCore now exposes the optional createPointInTimeDocumentService capability. OdspDocumentServiceFactory inherits this capability, so hosts can use the standard factory with loadContainerToSequenceNumber for sequence-number-based document loading. Factories that do not support point-in-time loading leave the capability undefined.
const factory = new OdspDocumentServiceFactory(
getStorageToken,
getWebsocketToken,
);
if (factory.createPointInTimeDocumentService !== undefined) {
const documentService = await factory.createPointInTimeDocumentService(
resolvedUrl,
targetSequenceNumber,
);
}Change details
Commit: 1336342
Affected packages:
- @fluidframework/odsp-driver
Add reference-only capture for reusable container baselines (#28006)
captureFullContainerState now accepts a blobCaptureMode option. The default "inline" mode remains self-contained, while "reference" omits structural and attachment blob payloads so repeated online loads can fetch them from live storage.
const baseline = await captureFullContainerState({
urlResolver,
documentServiceFactory,
request,
blobCaptureMode: "reference",
});
const container = await loadExistingContainer({
...loaderProps,
request,
pendingLocalState: baseline,
});Change details
Commit: 4374770
Affected packages:
- @fluidframework/container-loader
New ILayerCompatSupportRequirements property on LocalDocumentServiceFactory and OdspDocumentServiceFactoryCore (#27516)
A new optional property, ILayerCompatSupportRequirements, has been added to LocalDocumentServiceFactory and OdspDocumentServiceFactoryCore.
The Driver layer uses this property to publish the requirements that the Loader layer must meet to be compatible with it. Because the Driver has no reference to the Loader, it cannot validate the Loader directly; instead the Loader reads these requirements and validates itself against them on the Driver's behalf. This enables the Loader / Driver compatibility check to run in both directions.
Change details
Commit: f8bcc56
Affected packages:
- @fluidframework/local-driver
- @fluidframework/odsp-driver
Collaborative text editors can track selections across edits (#27907)
useTreeSynchronizedString now returns SynchronizedString.setSelection, allowing text editors to seed and update the selection range tracked by those anchors. The plain text React editor uses this API to preserve textarea selections across collaborative edits without duplicating per-delta cursor adjustment logic.
Change details
Commit: 23c8323
Affected packages:
- @fluidframework/react
Add server timestamps to beta version mark resolution APIs (#28037)
The @beta IVersionMarkResolver APIs now expose the server timestamp of a resolved mark while remaining compatible with callers using the previous result and listener shapes:
- The resolved variants returned by
sealAndCaptureVersionMark()andresolve()populate an optionaltimestampproperty. - The
onBatchSequenced()listener can receivetimestampas its optional third argument when an incoming batch resolves a pending mark.
Change details
Commit: 102bfc9
Affected packages:
- @fluidframework/container-runtime
🌳 SharedTree DDS Changes
Fixed bug in no-change constraint revert precondition (#27989)
The no-change constraint, when evaluated as a precondition to revert, is now violated by concurrent changes made to already removed content. Before this version, it was only violated by concurrent changes made in the document tree. This shift makes the behavior of the constraint consistent across revert and non-revert usages.
Change details
Commit: f02e424
Affected packages:
- @fluidframework/tree
- fluid-framework
Bug fix: forking during changed event callback is now safe (#28008)
Forking (beta) a view during the callback for the "changed" event (alpha) emitted when a transaction is committed would create a fork with malformed change data. This could result in asserts being triggered when utilizing the fork (including, but not limited to, error code 0x7ce).
Change details
Commit: 53eb97d
Affected packages:
- @fluidframework/tree
- fluid-framework
asAlpha now supports ITree (#28011)
The asAlpha function now accepts an ITree and returns its ITreeAlpha API.
import { asAlpha, type ITree } from "@fluidframework/tree/alpha";
declare const tree: ITree;
const alphaTree = asAlpha(tree);Change details
Commit: 4ef2d87
Affected packages:
- @fluidframework/tree
- fluid-framework
Formatted text uniform runs now account for optional formatting fields (#28014)
FormattedText.Members.getUniformRun now ends a uniform run when an optional formatting field is present on only one side of a character boundary. This prevents characters with different formatting from being included in the same uniform run.
Change details
Commit: 7781c1a
Affected packages:
- @fluidframework/tree
- fluid-framework
Rename TreeBranch and TreeBranchAlpha to UntypedTreeView (#27932)
UntypedTreeView and UntypedTreeViewAlpha replace the beta TreeBranch and alpha TreeBranchAlpha interfaces, clarifying that they represent tree views without known schemas. The old names remain available as deprecated compatibility aliases and will be removed in a future release.
Update API imports and type annotations to use the new names:
// Before
import type { TreeBranch } from "fluid-framework/beta";
import type { TreeBranchAlpha } from "fluid-framework/alpha";
const betaBranch: TreeBranch = betaView.fork();
const alphaBranch: TreeBranchAlpha = alphaView.fork();
// After
import type { UntypedTreeView } from "fluid-framework/beta";
import type { UntypedTreeViewAlpha } from "fluid-framework/alpha";
const betaForkedView: UntypedTreeView = betaView.fork();
const alphaForkedView: UntypedTreeViewAlpha = alphaView.fork();Change details
Commit: 22e5b4e
Affected packages:
- @fluidframework/tree
- fluid-framework
Remove the deprecated TreeAlpha.branch API (#27932)
The deprecated alpha TreeAlpha.branch(node) API has been removed. Use TreeAlpha.context(node) and check isView() to access the untyped view for a hydrated node:
const context = TreeAlpha.context(node);
if (context.isView()) {
// `context` is an UntypedTreeViewAlpha here.
}Change details
Commit: 22e5b4e
Affected packages:
- @fluidframework/tree
- fluid-framework
The treeChanged event reserves its listener argument for event data (#27951)
TreeChangeEvents.treeChanged now declares its first listener argument as optional unknown. The event's runtime behavior has not changed, but the declaration reserves that position for event data that experimental or future APIs may provide.
Most listeners require no changes. Listeners that declare their own optional first parameter should remove it or use a wrapper so they do not interpret event data as application data. Such listeners were already unlikely to function correctly - these changes just make the contract for use more explicit. For example, use a zero-argument inline callback when subscribing to the stable event:
Tree.on(node, "treeChanged", () => {
// Read the updated tree here.
});Change details
Commit: 33d8624
Affected packages:
- @fluidframework/tree
- fluid-framework
Deprecated Tree APIs are removed for 3.0 (#27911)
The following deprecated APIs have been removed:
- The
IsListener,Listenable,Listeners, andOfftype aliases. Import IsListener, Listenable, Listeners, and Off from@fluidframework/core-interfacesorfluid-frameworkinstead. asTreeViewAlpha. Use asAlpha instead.TreeAlpha.branch. Use TreeAlpha.context(node) and call isBranch() on the returned context to narrow it to a branch.- The
TableSchema.InsertColumnsParameters,TableSchema.InsertRowsParameters, andTableSchema.SetCellParametersinterfaces and the Table methods that accepted them. Use the positional insertColumns, insertRows, and setCell overloads instead. - The
TableSchema.Table.removeCell(key)overload. Pass the row and column as separate arguments to removeCell instead.
The deprecated @system typeNameSymbol API has been replaced by schemaIdentifierBrand, which remains @system but is not deprecated. The brand is retained as a compile-time optimization that enables TypeScript to handle larger schema unions. Neither symbol is available as a runtime export; they appear only in type declarations. Most users do not need to refer to either symbol. Code that explicitly referenced typeNameSymbol should reference schemaIdentifierBrand.
Change details
Commit: c0f78a7
Affected packages:
- @fluidframework/tree
- fluid-framework
retainHistory now retains history in summaries (#28036)
The retainHistory option on SharedTreeOptions is documented as causing growth in summaries/snapshots as well as in memory, but it only ever prevented trunk commits from being evicted from memory. Summaries continued to contain just the collaboration window, so retained history was discarded at the next summary and was unavailable to clients that loaded from it.
Summaries produced by a client with retainHistory enabled now contain the full trunk, matching the option's documented behavior. History accumulated while the flag is enabled survives summarization and is available to clients that join later. History is only retained from the point at which the flag is enabled: commits that were already evicted by a prior session cannot be recovered.
There is no change to the default (retainHistory: false) behavior, and no change to the persisted format.
Change details
Commit: 3b665a2
Affected packages:
- @fluidframework/tree
- fluid-framework
SharedTree schema errors now explain the mismatch (#27950)
Schema validation errors now report the mismatch category and attach relevant diagnostic context. Depending on the mismatch, tagged telemetry properties identify the node type, field kind, child count, expected leaf value type, actual value type, unexpected fields, or path, making invalid content easier to diagnose while allowing consumers to filter potentially sensitive user data.
When a view schema cannot access a document's stored schema, the error now reports the first schema mismatch and explains whether to initialize the document, upgrade its stored schema, use a compatible view schema, or explicitly migrate the document.
Change details
Commit: 0e44043
Affected packages:
- @fluidframework/tree
- fluid-framework
Text nodes can now track insertion positions across edits (#28045)
PlainText and FormattedText nodes now provide createInsertionAnchor, which returns an ArrayPlaceAnchor whose character index updates as the text is edited. Dispose the anchor when it is no longer needed.
Change details
Commit: 8c209dc
Affected packages:
- @fluidframework/tree
- fluid-framework
New alpha APIs for inspecting history and restoring past states (#28012)
UntypedTreeViewAlpha (formerly TreeBranchAlpha) now exposes a branchHistory property which returns a TreeBranchHistory object with:
commitCount: the number of commits currently in the branch's history. This number grows when a new edit is made on the branch, when a branch containing new commits is merged into it, or when it is rebased onto a branch containing new commits. It shrinks when past commits are trimmed from the history.getHeadCommit(): returns theTreeBranchCommitMetadatafor the branch's head commit, orundefinedif the branch has no commits. EachTreeBranchCommitMetadataexposes the commit'srevisionstring and itsparentcommit metadata, so the history can be walked backwards from the head.
A revision obtained this way can be passed to either of two new methods on UntypedTreeViewAlpha implementations:
revertTo(revision): applies a new change which reverts all changes made sincerevision. The generated change is subject to the same merge semantics as the reverts of individual commits, so concurrent changes sequenced before the revert which affect different parts of the document are not overwritten.rewindTo(revision): switches the view to a new underlying branch whose head is the commit atrevision, without applying a change. The original underlying branch is disposed unless it is the main branch or a shared branch, so considerfork()ing before rewinding if it needs to be retained.
How much history is available depends on how many commits the client retains; see the retainHistory option on SharedTreeOptions.
Change details
Commit: 920e946
Affected packages:
- fluid-framework
- @fluidframework/tree
TreeViewAlpha can now query whether a staged schema upgrade has been applied (#27829)
A new isStagedUpgradeEnabled method on TreeViewAlpha checks whether a given SchemaUpgrade token has already been applied to a document's stored schema.
This is useful when gradually rolling out a staged schema upgrade via feature flags — for example, to conditionally include the upgrade token in the view configuration after a flag rollback, or to show UI that depends on the upgraded schema.
const view = tree.viewWith(
new TreeViewConfigurationAlpha({
schema: mySchema,
stagedUpgradePolicy: featureFlag.isEnabled
? StagedSchemaUpgradePolicy.enabledStagedUpgrades(myUpgrade)
: StagedSchemaUpgradePolicy.restrictive,
}),
);
// Show a "create poll" button only if the document supports the new poll schema
if (view.isStagedUpgradeEnabled(myUpgrade)) {
showCreatePollButton();
}Change details
Commit: 223545b
Affected packages:
- @fluidframework/tree
- fluid-framework
Promote array node change event deltas to beta (#27942)
TreeBeta.on now provides detailed delta payloads for array nodes. The nodeChanged event reports retain, insert, and remove operations for direct array changes. The treeChanged event also identifies retained elements whose subtrees changed.
The array delta payload and operation types are now exported from the beta entrypoint. Existing TreeAlpha.on support remains, but is now deprecated in favor of the TreeBeta API.
Examples
For example, inserting 99 at index 1 in an array containing [1, 2, 3] produces the following delta.
[
{ type: "retain", count: 1 },
{ type: "insert", count: 1 },
{ type: "retain", count: 2 },
];Removing the value at index 1 from an array containing [1, 2, 3] produces the following delta.
[
{ type: "retain", count: 1 },
{ type: "remove", count: 1 },
{ type: "retain", count: 1 },
];The following example applies an array node's direct changes to an external array without comparing full snapshots.
TreeBeta.on(arrayNode, "nodeChanged", ({ delta }) => {
// Fall back to a full synchronization when a granular delta is unavailable.
if (delta === undefined) {
synchronizeAllItems(arrayNode);
return;
}
// Track the current position in both the tree array and the displayed array.
let index = 0;
for (const operation of delta) {
switch (operation.type) {
case "retain":
// Skip elements that were not inserted or removed.
index += operation.count;
break;
case "remove":
// Remove elements at the current position without advancing it.
displayedItems.splice(index, operation.count);
break;
case "insert":
// Read inserted values from the updated tree and add them to the display.
displayedItems.splice(
index,
0,
...arrayNode.slice(index, index + operation.count),
);
index += operation.count;
break;
}
}
});Change details
Commit: 0161938
Affected packages:
- @fluidframework/tree
- fluid-framework
New alpha API for attaching custom metadata to commits (#28064)
Applications can now attach arbitrary, JSON-serializable metadata to a commit, replicate it to collaborating clients, and persist it in the document.
Supply it via the new customMetadata field on RunTransactionParamsAlpha:
view.runTransaction(
() => {
view.root.insertAtEnd("new item");
},
{ customMetadata: { author: "alice", intent: "add-item" } },
);The commit produced by reverting a Revertible, or by revertTo, can be annotated the same way via a new options argument:
revertible.revert({ customMetadata: { author: "alice", intent: "undo-add" } });
view.revertTo(revision, {
customMetadata: { author: "alice", intent: "undo-add" },
});Read it back while walking the branch's history, via the new custom property on TreeBranchCommitMetadata:
for (
let commit = view.branchHistory.getHead();
commit !== undefined;
commit = commit.getParent()
) {
const metadata = commit.custom;
}Because a commit may be produced by nested transactions, each of which may supply metadata, custom is the flattened combination of them all, with the outermost transaction winning on conflicting properties. The structural view is available as commit.customTree, a CustomMetadataTree mirroring the transaction nesting — the same relationship labels.tree has to a change's label set.
Metadata shares the lifetime of the commit it is attached to, so it is dropped when that commit is trimmed from the trunk, or lasts as long as the document under the retainHistory option on SharedTreeOptions. It also travels on every annotated op and occupies summary space for as long as its commit survives, so it should be kept small.
Persisting the metadata requires new op and summary format versions, which are written only when minVersionForCollab is set to 2.117.0 or later; until then, metadata is kept in memory for the local session but is neither replicated nor persisted. Raising that floor makes every subsequent op and summary use the new versions, whether or not any commit carries metadata, so deploy metadata-capable code everywhere first. Lowering it again is lossy: a client configured to write the older format can still read metadata but strips it when encoding.
Change details
Commit: 9033716
Affected packages:
- fluid-framework
- @fluidframework/tree
Tree contexts now support beta transactions (#28116)
The new TreeBeta.context method gets a TreeContextBeta for a tree node. Use this context to run synchronous or asynchronous transactions on hydrated and unhydrated nodes. The transaction methods accept RunTransactionParamsBeta, which supports transaction labels.
Use isView() to determine if the context is associated with an UntypedTreeView. If isView() returns true, TypeScript narrows the context to UntypedTreeView. The view-specific transaction overloads are then available. These overloads let a transaction callback request a rollback and return a value for each result.
The following example runs a transaction on any tree node context. It then uses isView() to run a transaction that can request a rollback.
import { TreeBeta } from "@fluidframework/tree/beta";
// ...
const context = TreeBeta.context(myNode);
const updateResult = context.runTransaction(() => {
myNode.title = "Updated title";
return { value: myNode.title };
});
if (context.isView()) {
const validatedResult = context.runTransaction(() => {
myNode.title = proposedTitle;
// Roll back all edits in this transaction when the title is not valid.
if (proposedTitle.length === 0) {
return { rollback: true, value: "Title must not be empty" };
}
return { rollback: false, value: myNode.title };
});
if (!validatedResult.success) {
showValidationError(validatedResult.value);
}
}Change details
Commit: 9e036be
Affected packages:
- @fluidframework/tree
- fluid-framework
Bug fix: commit metadata access after history trimming (#28135)
Before this change, accessing the properties on a TreeBranchCommitMetadata object could throw an error with any the following error codes: 0xa5e, 0xa5f, 0xa60, 0xd36. This would happen when the access was made after the corresponding commit was trimmed from history:
const commitMetadata = view.branchHistory.getHead();
// ...History trimming occurs...
// Would assert:
console.log(commitMetadata.revision);The properties now remain safe to access after the corresponding commit is trimmed from history.
Change details
Commit: 1e5847f
Affected packages:
- @fluidframework/tree
- fluid-framework
Expose schema incompatibility details on TreeViewBeta (#28142)
TreeViewBeta.compatibility.discrepancies now provides typed SchemaDiscrepancy objects when a view cannot access a tree because its view schema is incompatible with the stored schema. The readonly array may include application-defined schema identifiers and field keys. Each entry includes a mismatch discriminator so consumers can distinguish allowed-type, field-kind, value-schema, and node-kind differences. Allowed-type discrepancies include staged types that are absent from the stored schema in stagedView, while discrepancies on staged optional fields include viewIsStagedOptional: true. Staged-only differences remain compatible and do not produce discrepancies by themselves.
const sf = new SchemaFactory("com.example");
class Todo extends sf.object("Todo", {
title: sf.number,
}) {}
const view = asBeta(tree.viewWith(new TreeViewConfiguration({ schema: Todo })));
if (!view.compatibility.canView) {
console.error(view.compatibility.discrepancies);
}If the stored schema allows string for Todo.title, the output is:
[
{
"mismatch": "allowedTypes",
"location": { "nodeType": "com.example.Todo", "fieldKey": "title" },
"view": ["com.fluidframework.leaf.number"],
"stored": ["com.fluidframework.leaf.string"]
}
]Applications can see from mismatch: "allowedTypes" that the schemas differ in their allowed types, compare view with stored to determine which types each schema permits, and use location to find the field where the mismatch occurs.
Change details
Commit: 9c11c86
Affected packages:
- @fluidframework/tree
- fluid-framework
Create an independent TreeView with beta APIs (#28138)
The new createIndependentTreeView function creates a non-collaborative TreeViewBeta directly from a TreeViewConfiguration. Use this function for local data or tests that need beta view APIs without a Fluid container.
import {
createIndependentTreeView,
SchemaFactory,
TreeViewConfiguration,
} from "@fluidframework/tree/beta";
const view = createIndependentTreeView(
new TreeViewConfiguration({ schema: SchemaFactory.number }),
);
view.initialize(42);Change details
Commit: 1886c79
Affected packages:
- @fluidframework/tree
- fluid-framework
Schema compatibility snapshots support custom filename prefixes and suffixes (#28100)
snapshotSchemaCompatibility now accepts snapshotFileNameFormat, which can add a prefix and suffix around the version in generated snapshot filenames. The same format is used to discover historical snapshots, allowing multiple schema snapshot sets or unrelated JSON files to share a directory.
snapshotSchemaCompatibility({
// Existing options...
snapshotFileNameFormat: {
prefix: "point-schema-",
suffix: "-snapshot",
},
});Change details
Commit: 8de6212
Affected packages:
- @fluidframework/tree
- fluid-framework
Preserve enabled staged schema upgrades by default (#28165)
TreeView.upgradeSchema() now includes staged schema upgrades that are already enabled in the document, even when the view's staged upgrade policy does not select them. This prevents a schema upgrade from accidentally attempting to narrow stored schema enabled by another client.
Set includeAlreadyEnabledUpgrades to false when creating the staged upgrade policy to require upgrades to be selected explicitly:
const config = new TreeViewConfigurationAlpha({
schema: AppSchema,
stagedUpgradePolicy: {
includeAlreadyEnabledUpgrades: false,
...StagedSchemaUpgradePolicy.enabledStagedUpgrades(myUpgrade),
},
});Change details
Commit: 3f82e18
Affected packages:
- @fluidframework/tree
- fluid-framework
🐛 Bug Fixes
Full container state capture now includes recently sequenced operations (#28009)
captureFullContainerState now connects to the delta stream and catches up through the latest operation known when the connection is established. This prevents captured state from omitting operations that have been broadcast over the websocket but have not yet reached delta storage.
Change details
Commit: 6a2354f
Affected packages:
- @fluidframework/container-loader
Allow default-data-store runtime factories to receive compatibility settings (#28025)
ContainerRuntimeFactoryWithDefaultDataStoreProps now exposes the optional oldestSupportedClient and deprecated minVersionForCollab properties already supported by BaseContainerRuntimeFactoryProps. This lets callers select compatibility explicitly before the setting becomes required.
Change details
Commit: 6923367
Affected packages:
- @fluidframework/aqueduct
Correct dirty and saved event transition documentation (#28141)
The IContainerEvents documentation now correctly states that the "dirty" event represents isDirty changing from false to true, while the "saved" event represents it changing from true to false.
Change details
Commit: 9947840
Affected packages:
- @fluidframework/container-definitions
⚠️ Deprecations
Deprecate assert (#28080)
The legacy assert API is intended only for use within the Fluid Framework client codebase and is now deprecated for external consumers. Consumers should replace it with an assertion utility appropriate for their application.
The API is scheduled for removal in version 3.10.0. For more information, see issue #28084.
Change details
Commit: 9163ef3
Affected packages:
- @fluidframework/core-utils
Rename the alpha independent TreeView creation API (#28138)
The alpha independentView function is now deprecated. Use createIndependentTreeViewAlpha instead.
The function signature and behavior are unchanged.
Change details
Commit: 1886c79
Affected packages:
- @fluidframework/tree
- fluid-framework
Legacy API Changes
Make point-in-time support optional and consumer-supplied (#28055)
Point-in-time loading is now an optional implementation supplied by the host. Consumers that do not enable the feature no longer include its implementation in their dependency graph. Hosts can control when the feature is loaded by dynamically importing its dedicated entrypoint:
const factory = createOdspDocumentServiceFactory({
getStorageToken,
getWebsocketToken,
persistedCache,
hostPolicy,
pointInTimeDocumentServiceImplementation: async (props) => {
const { createPointInTimeDocumentService } = await import(
"@fluidframework/odsp-driver/legacy/point-in-time"
);
return createPointInTimeDocumentService(props);
},
});The legacy-beta getOdspPointInTimeDocumentServiceFactory helper is removed. Point-in-time consumers should use createOdspDocumentServiceFactory, which accepts tokens, cache, host policy, and optional feature implementations in one options object. Existing OdspDocumentServiceFactory and OdspDocumentServiceFactoryCore constructor signatures remain unchanged.
Change details
Commit: 9a55f89
Affected packages:
- @fluidframework/odsp-driver
Other Changes
Client packages now target ES2022 (#27846)
The TypeScript compilation target and lib for the Fluid Framework client packages have been raised from ES2021/ES2020 to ES2022. The published JavaScript now uses ES2022 language features (with correspondingly less down-leveling), so consuming these packages requires a runtime that supports ES2022. All actively supported Node.js versions and evergreen browsers already meet this requirement.
Note that Fluid Framework has not officially supported targets older than ES2022 since before 2.0: this is documented in ClientRequirements.md as well as the README for every client package.
It is possible this change could impact users of less up to date JavaScript runtimes. Impacted users can use a tool like babel to transpile out unsupported language features.
Change details
Commit: 91c7854
Affected packages:
- @fluid-example/table-document
- @fluid-experimental/attributor
- @fluid-experimental/data-objects
- @fluid-experimental/dds-interceptions
- @fluid-experimental/ink
- @fluid-experimental/last-edited
- @fluid-experimental/odsp-end-to-end-tests
- @fluid-experimental/oldest-client-observer
- @fluid-experimental/ot
- @fluid-experimental/pact-map
- @fluid-experimental/property-changeset
- @fluid-experimental/property-common
- @fluid-experimental/property-dds
- @fluid-experimental/property-properties
- @fluid-experimental/sequence-deprecated
- @fluid-experimental/sharejs-json1
- @fluid-experimental/tree
- @fluid-tools/fetch-tool
- @fluidframework/agent-scheduler
- @fluidframework/app-insights-logger
- @fluidframework/aqueduct
- @fluidframework/azure-client
- @fluidframework/azure-end-to-end-tests
- @fluidframework/azure-local-service
- @fluidframework/azure-service-utils
- @fluidframework/cell
- @fluidframework/container-definitions
- @fluidframework/container-loader
- @fluidframework/container-runtime
- @fluidframework/container-runtime-definitions
- @fluidframework/core-interfaces
- @fluidframework/core-utils
- @fluidframework/counter
- @fluidframework/datastore
- @fluidframework/datastore-definitions
- @fluidframework/debugger
- @fluidframework/devtools
- @fluidframework/devtools-core
- @fluidframework/driver-base
- @fluidframework/driver-definitions
- @fluidframework/driver-utils
- @fluidframework/driver-web-cache
- @fluidframework/file-driver
- @fluidframework/fluid-runner
- @fluidframework/fluid-static
- @fluidframework/fluid-telemetry
- @fluidframework/id-compressor
- @fluidframework/legacy-dds
- @fluidframework/local-driver
- @fluidframework/map
- @fluidframework/matrix
- @fluidframework/merge-tree
- @fluidframework/odsp-client
- @fluidframework/odsp-doclib-utils
- @fluidframework/odsp-driver
- @fluidframework/odsp-driver-definitions
- @fluidframework/odsp-urlresolver
- @fluidframework/ordered-collection
- @fluidframework/presence
- @fluidframework/quill-react
- @fluidframework/react
- @fluidframework/register-collection
- @fluidframework/replay-driver
- @fluidframework/request-handler
- @fluidframework/routerlicious-driver
- @fluidframework/routerlicious-urlresolver
- @fluidframework/runtime-definitions
- @fluidframework/runtime-utils
- @fluidframework/sequence
- @fluidframework/shared-object-base
- @fluidframework/shared-summary-block
- @fluidframework/synthesize
- @fluidframework/task-manager
- @fluidframework/telemetry-utils
- @fluidframework/test-runtime-utils
- @fluidframework/test-utils
- @fluidframework/tinylicious-client
- @fluidframework/tinylicious-driver
- @fluidframework/tool-utils
- @fluidframework/tree
- @fluidframework/tree-agent
- @fluidframework/tree-agent-langchain
- @fluidframework/tree-agent-ses
- @fluidframework/type-factory
- @fluidframework/undo-redo
- fluid-framework
🛠️ Start Building Today!
Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!