Skip to content

Releases: microsoft/FluidFramework

Fluid Framework v2.103.0 (minor)

08 Jun 23:29
0efa18f

Choose a tag to compare

Contents

⚠️ Deprecations

Deprecate ICreateAndLoadContainerProps in favor of composable building blocks (#27347)

ICreateAndLoadContainerProps is now @deprecated. It remains as a structurally-identical alias and the props types that previously extended it (ILoadExistingContainerProps, ICreateDetachedContainerProps, IRehydrateDetachedContainerProps) now extend the building blocks directly, so no caller migration is required for those.

Callers writing new props types should compose from the building blocks directly:

import type {
  IContainerHostProps,
  IContainerDriverServices,
} from "@fluidframework/container-loader/legacy";

// Equivalent to the old ICreateAndLoadContainerProps
type MyProps = IContainerHostProps & IContainerDriverServices;

// Add only what you need
interface MyHostOnlyProps extends IContainerHostProps {
  readonly extraOption: boolean;
}
  • IContainerHostProps covers the code loader plus optional policy / observability fields (options, scope, logger, configProvider, protocolHandlerBuilder, allowReconnect, clientDetailsOverride).
  • IContainerDriverServices covers the urlResolver + documentServiceFactory pair.

ICreateAndLoadContainerProps will be removed in a future major release.

Change details

Commit: 4091373

Affected packages:

  • @fluidframework/container-loader

⬆️ Table of contents

🛠️ Start Building Today!

Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!

Fluid Framework v2.102.0 (minor)

29 May 21:28
a9c500e

Choose a tag to compare

Contents

✨ New Features

Promote onAssertionFailure to @beta (#27282)

The onAssertionFailure hook, previously @alpha, has been promoted to @beta. It allows registering a handler that is invoked when an assertion failure occurs, which is useful for capturing the first error in a sequence before subsequent failures obscure the root cause.

import { onAssertionFailure } from "@fluidframework/core-utils/beta";

let firstAssertion: Error | undefined;
const unregister = onAssertionFailure((error) => {
  firstAssertion ??= error;
});

Change details

Commit: 4191b82

Affected packages:

  • @fluidframework/core-utils
  • fluid-framework

⬆️ Table of contents

⚠️ Deprecations

Service client createContainer/getContainer overloads taking CompatibilityMode are deprecated (#27212)

The createContainer and getContainer overloads on AzureClient, OdspClient, and TinyliciousClient (plus AzureClient.viewContainerVersion) that accept a CompatibilityMode ("1" / "2") argument are now deprecated. Pass a MinimumVersionForCollab SemVer string instead — it specifies the minimum collaborating client version directly.

See issue #23289 for migration details and removal tracking.

Change details

Commit: 3e951b4

Affected packages:

  • @fluidframework/azure-client
  • @fluidframework/odsp-client
  • @fluidframework/tinylicious-client

⬆️ Table of contents

Legacy API Changes

Add optional getPendingLocalState to IContainer (#27269)

IContainer now exposes getPendingLocalState?(): Promise<string>. The serialized blob can be passed back as pendingLocalState to loadExistingContainer (or ILoader.resolve) to rehydrate an attached container at the same position without data loss.

The member is optional during this minor release so external implementers of IContainer (test mocks, wrapper containers, partner runtimes) remain forward-compatible. A future breaking release will make it required.

The ContainerAlpha interface and asLegacyAlpha helper in @fluidframework/container-loader continue to expose this functionality at @legacy @alpha for callers that prefer the typed-required shape.

Lifecycle: misuse of this API can result in duplicate op submission and potential document corruption. The blob returned MUST be discarded if and when the container emits a "connected" event — any subsequent rehydrate from that blob would submit the same ops a second time. The container must also be neither closed nor disposed when calling; otherwise the call throws UsageError.

Runtime behavior is unchanged.

Change details

Commit: 1f12b8e

Affected packages:

  • @fluidframework/container-definitions

⬆️ Table of contents

createTreeContainerRuntimeFactory accepts a MinimumVersionForCollab SemVer and deprecates compatibilityMode with minVersionForCollabOverride (#27212)

A minVersionForCollaboration property on createTreeContainerRuntimeFactory accepts a MinimumVersionForCollab SemVer string and replaces compatibilityMode (CompatibilityMode values "1" and "2") with optional minVersionForCollabOverride.

See issue #27356 for migration details and removal tracking.

Change details

Commit: 3e951b4

Affected packages:

  • @fluidframework/fluid-static

⬆️ Table of contents

🛠️ Start Building Today!

Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!

Fluid Framework v2.101.1 (patch)

20 May 18:38
e0c2cfc

Choose a tag to compare

What's Changed

  • [release/client/2.101] patch bump to 2.101.1 #27357
  • Revert "feat(container-runtime): enable batchId tracking by default w… #27352

Full Changelog: client_v2.101.0...client_v2.101.1

Fluid Framework v2.100.1 (patch)

20 May 18:58
a152d28

Choose a tag to compare

What's Changed

  • [release/client/2.100] Bump client packages to 2.100.1 #27359
  • [release/client/2.100] fix(tree): Add option to heal unresolvable identifier IDs at doc load time #27353

Full Changelog: client_v2.100.0...client_v2.100.1

Fluid Framework v2.101.0 (minor)

13 May 20:40
63b5950

Choose a tag to compare

Contents

🌳 SharedTree DDS Changes

Upgrade LangChain dependencies to v1 (#27259)

@fluidframework/tree-agent-langchain (and the LangChain dev-dependencies on @fluidframework/tree-agent) now target the LangChain v1 line:

  • @langchain/core: ^0.3.80^1.1.44
  • @langchain/anthropic: ^0.3.24^1.3.28
  • @langchain/google-genai: ^0.2.16^2.1.30
  • @langchain/openai: ^0.6.12^1.4.5

LangChain v1 is backward-compatible for the message, tool, and chat-model APIs that tree-agent-langchain consumes (BaseChatModel, BaseMessage, AIMessage / HumanMessage / SystemMessage / ToolMessage, tool(), bindTools()). No source changes are required for consumers using these APIs. The new contentBlocks content-block API is opt-in.

Consumers of createLangchainChatModel who currently install @langchain/core@^0.3 should bump to @langchain/core@^1.1.43 (the lowest version that satisfies the peer ranges of all v1 sibling integrations—@langchain/google-genai@2.1.30 requires ^1.1.43).

Change details

Commit: eeebc23

Affected packages:

  • @fluidframework/tree-agent-langchain
  • @fluidframework/tree-agent

⬆️ Table of contents

🐛 Bug Fixes

Correct some PropertyDDS "MSG" error constant entries (#26971)

Several error constants referenced in other PropertyDDS packages did not exist and would produce errors with "undefined" (literal) in error message string. In the past:

  • OVERRIDEN_PROP_MUST_HAVE_SAME_CONTEXT_AS_BASE_TYPE use was replaced by OVERRIDEN_PROP_MUST_HAVE_SAME_FIELD_VALUES_AS_BASE_TYPE (but never defined).
  • CANNOT_INSERT_UNKNOWN_PROPERTY, MISMATCHING_PROPERTY_TYPEID, and CANNOT_REMOVE_NON_OPTIONAL_PROP uses were added without defining them.

Those all are now defined MSG properties.

Change details

Commit: 7aec5f1

Affected packages:

  • @fluid-experimental/property-common

⬆️ Table of contents

GC timers are now cancelled when a container closes, not just when it is disposed (#27130)

Adds an optional close() hook to IRuntime that Container calls on close. ContainerRuntime implements it by cancelling all GC timers (session expiry and unreferenced-node timers) without clearing tracked state.

This prevents the timers from causing memory leaks after a Container is closed but not disposed. In Node.js environments this also prevents the timers from keeping the event loop alive until dispose(). This can reduce the need for Mocha's --exit in tests which create containers which are closed but not disposed.

Disposing of closed containers is still recommended, but it is now less critical for avoiding timer-related hangs after close. Disposal still helps clean up resources and can reduce the size of memory leaks if references to the container are leaked.

Change details

Commit: 86c0fff

Affected packages:

  • @fluidframework/container-definitions
  • @fluidframework/container-runtime

⬆️ Table of contents

Add SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode to recover documents with corrupted identifiers (#27281)

A SharedTree bug can result in corrupted documents due to their attach summary compressing identifier-field values in a way that cannot be uncompressed. This bug manifested as remote clients processing the op throwing an error with the message "Unknown op space ID.".

This change adds an option (healUnresolvableIdentifiersOnDecode) to configuredSharedTreeBetaLegacy which will allow documents affected by this bug to load again when enabled. Enabling this option carries some risk, see documentation on the interface itself for more details.

Who is affected

Only SharedTrees attached to a container that was already attached can be impacted. Furthermore, this bug only occurs when the attached tree contains identifier fields which contain implicitly generated default values.

Change details

Commit: d9205dd

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Fix a SharedTree document corruption bug (#27292)

A SharedTree bug which could corrupt documents when attaching them to containers has been fixed. See healUnresolvableIdentifiersOnDecode on configuredSharedTreeBetaLegacy for a potential mitigation path for documents that were already corrupted by this bug.

Who is affected

Only SharedTrees attached to a container that was already attached can be impacted. Furthermore, this bug only occurs when the attached tree contains identifier fields which contain implicitly generated default values.

Change details

Commit: 6f4cdcb

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

⚠️ Deprecations

Deprecate LogLevel.default and LogLevel.error (#27207)

LogLevel.default and LogLevel.error in @fluidframework/core-interfaces are deprecated in favor of the semantically clearer LogLevel.info and LogLevel.essential.

Migration

The recommended replacement for LogLevel.default depends on how the value is used:

  • For an event's default logLevel (e.g. the logLevel argument to ITelemetryBaseLogger.send), the recommendation is LogLevel.essential.
  • For a logger's default minLogLevel (the threshold that filters events), LogLevel.info is the recommendation.

The replacement for LogLevel.error should always be LogLevel.essential.

See issue #26969 for full guidance and removal tracking (planned for v3.0).

Change details

Commit: 77ef335

Affected packages:

  • @fluidframework/core-interfaces

⬆️ Table of contents

Legacy API Changes

Add legacy beta map compatibility interfaces (#27240)

New legacy beta map interfaces make it possible to type legacy map-like DDS APIs against Fluid's stable map abstraction while preserving the legacy DDS get and set APIs.

import type {
  FluidMapLegacy,
  IDirectoryBeta,
  ISharedMapBeta,
} from "@fluidframework/map/legacy";

declare const directory: IDirectoryBeta;
declare const sharedMap: ISharedMapBeta;

const directoryMap: FluidMapLegacy<string, unknown> = directory;
const sharedMapAsLegacyMap: FluidMapLegacy<string, unknown> = sharedMap;

Change details

Commit: [b765c6a](https://github.com/microsoft/FluidFr...

Read more

Fluid Framework v2.100.0 (minor)

28 Apr 15:51
3c46f9b

Choose a tag to compare

Contents

🚨 Breaking Changes

Node 22 is now the minimum supported Node.js version (#27116)

All Fluid Framework client packages now require Node.js 22 or later. This aligns with the standing Node upgrade policy as Node 20 reaches end-of-life on April 30, 2026.

Change details

Commit: e8214d2

Affected packages:

  • @fluid-example/table-document
  • @fluid-experimental/attributor
  • @fluid-experimental/data-object-base
  • @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-internal/client-utils
  • @fluid-internal/platform-dependent
  • @fluid-internal/presence-definitions
  • @fluid-internal/presence-runtime
  • @fluid-internal/replay-tool
  • @fluid-private/test-dds-utils
  • @fluid-private/test-loader-utils
  • @fluid-tools/fetch-tool
  • @fluidframework/agent-scheduler
  • @fluidframework/app-insights-logger
  • @fluidframework/aqueduct
  • @fluidframework/azure-client
  • @fluidframework/azure-end-to-end-tests
  • @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

⬆️ Table of contents

Container runtime instantiation now requires navigator to be defined in the runtime environment (#27010)

The internal getDeviceSpec() function, which is called during container runtime instantiation to report hardware telemetry, no longer guards against navigator being null or undefined. This means loading a container runtime requires either a browser environment or Node 22+, both of which provide a built-in navigator global. Environments that do not provide navigator (e.g., older versions of Node.js) will encounter a runtime error when instantiating the container runtime.

This requirement aligns with the recent migration of the repo to Node 22 per our standing Node upgrade policy. Node 20 reaches end-of-life on April 30, 2026.

Change details

Commit: 936562b

Affected packages:

  • @fluidframework/container-runtime

⬆️ Table of contents

✨ New Features

Add SchemaFactoryAlpha.stagedOptionalRecursive for recursive staged-optional fields (#27042)

SchemaFactoryAlpha.stagedOptionalRecursive(T) is the recursive-type variant of stagedOptional (released in 2.93.0). Use it for schemas whose types are recursive - the relaxed type constraints work around TypeScript's limitations with recursive schema definitions. Pair it with ValidateRecursiveSchema for improved type safety.

Example:

const sf = new SchemaFactoryAlpha("my-app");
class TreeNode extends sf.objectRecursiveAlpha("TreeNode", {
  value: sf.number,
  child: sf.stagedOptionalRecursive([() => TreeNode]),
}) {}
type _check = ValidateRecursiveSchema<typeof TreeNode>;

See stagedOptional for the migration pattern (required to stagedOptional to optional).

Change details

Commit: a6e084e

Affected packages:

  • @fluidframework/tree

⬆️ Table of contents

🌳 SharedTree DDS Changes

Fixed incremental summary bug in SharedTree that may cause repeated summary failures eventually leading to document corruption (#26990)

Incremental summary for SharedTree is off by default. This bug only affects applications that have explicitly enabled incremental summarization.

Affected configurations

A session could be affected if all the following were true:

  • Incremental summarization was enabled (opt-in feature, off by default).
  • The SharedTree schema had incremental fields nested at least 2 levels deep. For example, a map field marked with incrementalSummaryHint that contains objects which themselves have a map field also marked with incrementalSummaryHint.
  • The document was summarized multiple times, with the outer incremental field changing in at least one summary while the inner incremental field remained unchanged.

Symptoms

Summaries would fail. Depending on the storage service, the error may appear as:

  • TypeError: Cannot read properties of undefined (reading 'trees') (for example, when using SharePoint storage)

Repeated summary failures can cause a session to accumulate ops without a summary. Once the limit of ops without a summary is reached (~10k), further ops will be rejected, making the document read-only for that session.

Mitigation and recovery

  • If a session is already affected, turning off incremental summarization will allow summaries to succeed again.
  • Upgrade to this version to prevent further summary failures.

Change details

Commit: 1514c31

Affected packages:

  • @fluidframework/tree

⬆️ Table of contents

⚠️ Deprecations

Deprecate ITelemetryLoggerExt methods and related types ([#26912](https://github.com/microsoft/FluidFr...

Read more

server v6.0.1 (patch)

24 Apr 18:55
8f612ab

Choose a tag to compare

What's Changed

  • Bump server packages to 6.0.1 #27138
  • fix(routerlicious): enforce scope checks on PATCH /root endpoint (#27#27097

Full Changelog: server_v6.0.0...server_v6.0.1

server v7.0.1 (patch)

17 Apr 22:44
18da9aa

Choose a tag to compare

What's Changed

  • Bump server packages #27087
  • Remove patchRoot feature-flag tests #27088
  • fix(routerlicious): enforce scope checks on PATCH /root endpoint (#27#27083
  • build(server): Update Typetests (#25016) #25018

Full Changelog: server_v7.0.0...server_v7.0.1

Fluid Framework v2.93.0 (minor)

16 Apr 00:14
4b6695d

Choose a tag to compare

Contents

🚨 Breaking Changes

minVersionForCollab is now non-optional (#25331)

This change is a follow-up for pull request 25130 which was released as part of 2.61.0.

The minVersionForCollab property has been made non-optional in the following @beta @legacy interfaces in the Runtime layer:

  • IFluidParentContext.minVersionForCollab in @fluidframework/runtime-definitions.
  • IFluidDataStoreContext.minVersionForCollab in @fluidframework/runtime-definitions.
  • IFluidDataStoreContextDetached.minVersionForCollab in @fluidframework/runtime-definitions.

Consumers of Fluid aren't expected to implement these interfaces directly, so no impact is expected.

Additionally the following properties now always return a value, rather than possibly returning undefined:

  • FluidDataStoreRuntime.minVersionForCollab in @fluidframework/datastore. Note that API Extractor shows this as a breaking change since FluidDataStoreRuntime is beta + legacy and non-sealed. However, FluidDataStoreRuntime is not intended to be extended directly outside of a known legacy use-case.
  • IDataObjectProps.context.minVersionForCollab in @fluidframework/aqueduct.
  • ITestFluidObject.context.minVersionForCollab in @fluidframework/test-utils
  • IProvideTestFluidObject.ITestFluidObject.context.minVersionForCollab in @fluidframework/test-utils

Change details

Commit: 9a0d027

Affected packages:

  • @fluidframework/aqueduct
  • @fluidframework/datastore
  • @fluidframework/runtime-definitions
  • @fluidframework/shared-object-base
  • @fluidframework/test-utils

⬆️ Table of contents

✨ New Features

Add Fluid-controlled map and iterator interfaces (#26788)

TreeIndex now extends FluidReadonlyMap instead of the built-in ReadonlyMap, and TreeMapNodeAlpha which extends FluidReadonlyMap instead of the built-in ReadonlyMap has been added. This works to uncouple Fluid's public API surface to the TypeScript standard library's map types, preventing future breakage when those types change.

Change details

Commit: 78e06f7

Affected packages:

  • @fluidframework/core-interfaces
  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

presence API set now at public support level (#27001)

All @fluidframework/presence APIs that had been @beta have been promoted to @public support with the exception of getPresence which has been relocated to fluid-framework. (See issue #26397 for more getPresence details.)

See Presence API overview and Presence package API details with getPresence API to get started.

Change details

Commit: 97d14a7

Affected packages:

  • @fluidframework/presence

⬆️ Table of contents

Add SchemaFactoryAlpha.stagedOptional for incremental required-to-optional field migrations (#26918)

SchemaFactoryAlpha.stagedOptional(T) enables incremental migration of a field from required to optional. It creates a field that is optional in the view schema but stored as required in the stored schema until all clients have been upgraded, avoiding the need for a coordinated simultaneous deployment.

Migration path:

  1. Start with sf.required(T) - all clients require the field.
  2. Deploy sf.stagedOptional(T) - new clients see the field as optional and can read documents whether the field is present or absent, but the stored schema stays required so old clients are not broken. Writing undefined is blocked at runtime during this phase.
  3. Deploy sf.optional(T) once all clients have been updated - the stored schema becomes optional and the field can be cleared.

Change details

Commit: fb808eb

Affected packages:

  • @fluidframework/tree

⬆️ Table of contents

🌳 SharedTree DDS Changes

Promote tree index APIs from alpha to beta (#26993)

The following APIs have been promoted from @alpha to @beta:

  • TreeIndex
  • TreeIndexKey
  • TreeIndexNodes
  • createTreeIndex
  • IdentifierIndex
  • createIdentifierIndex

Additionally, the following @fluidframework/core-interfaces types have been promoted from @alpha to @beta:

  • FluidReadonlyMap
  • FluidIterable
  • FluidIterableIterator
  • FluidMap

Change details

Commit: 37f2f17

Affected packages:

  • @fluidframework/core-interfaces
  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

⚠️ Deprecations

getPresence from @fluidframework/presence is deprecated and will be removed in a future release. (#26399)

Now getPresence is available for import from the fluid-framework package.

To prepare, make changes following this pattern:

-import { getPresence } from "@fluidframework/presence/beta";
+import { getPresence } from "fluid-framework";

See issue #26397 for more details.

Change details

Commit: d533c19

Affected packages:

  • fluid-framework
  • @fluidframework/presence

⬆️ Table of contents

🛠️ Start Building Today!

Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!

Fluid Framework v2.92.0 (minor)

01 Apr 18:55
a4ed4aa

Choose a tag to compare

Contents

✨ New Features

Array node nodeChanged events now include a delta payload (via TreeAlpha) (#26677)

The nodeChanged event for array nodes (accessed via TreeAlpha.on) now provides a delta field, a sequence of ArrayNodeDeltaOp values that describe exactly what changed in the array. This lets you efficiently sync an external representation with tree changes, without taking a snapshot of the old state or diffing the entire array.

The delta follows Quill-style semantics: each op covers a contiguous run of positions in the array before the change.

  • { type: "retain", count: N }—N elements stayed in place. Their positions are unchanged, though their contents may have changed (which would fire separate nodeChanged events on those elements).
  • { type: "insert", count: N }—N elements were inserted; read their values from the current tree at these positions.
  • { type: "remove", count: N }—N elements were removed.

Trailing unchanged elements are not represented by a trailing "retain" op.

Use TreeAlpha.on to subscribe to the richer alpha events. The data passed to the callback is typed as NodeChangedDataAlpha<TNode>:

  • Object, map, and record nodes receive NodeChangedDataProperties (with a required changedProperties set).
  • Array nodes receive NodeChangedDataDelta (with a delta field).

TreeBeta.on is unchanged and does not include delta information.

Example: Applying a Delta to a Plain Array Mirror

// Walk the delta to keep a plain JS array in sync with an array node.
// retain = advance past unchanged elements,
// insert = splice in new elements,
// remove = splice out removed elements.
const mirror: number[] = [1, 2, 3];

TreeAlpha.on(myArrayNode, "nodeChanged", ({ delta }) => {
  let readPos = 0; // position in the current (post-change) tree
  let writePos = 0; // position in the mirror array

  for (const op of delta ?? []) {
    if (op.type === "retain") {
      writePos += op.count;
      readPos += op.count;
    } else if (op.type === "insert") {
      const newItems = Array.from(
        { length: op.count },
        (_, i) => myArrayNode[readPos + i],
      );
      mirror.splice(writePos, 0, ...newItems);
      writePos += op.count;
      readPos += op.count;
    } else if (op.type === "remove") {
      mirror.splice(writePos, op.count);
    }
  }
});

Example: Narrowing the Union in a Generic Handler

TreeAlpha.on(node as TreeNode, "nodeChanged", (data) => {
  if ("delta" in data) {
    // Array node — data is NodeChangedDataDelta
    console.log("array changed, delta:", data.delta);
  } else {
    // Object/map/record node — data is NodeChangedDataProperties
    console.log("properties changed:", data.changedProperties);
  }
});

Note: The delta value may be undefined in two cases:

  • The node was created locally and has not yet been inserted into a document tree (a known temporary limitation).
  • The document was updated in a way that required multiple internal change passes in a single operation (for example, a data change combined with a schema upgrade).

Change details

Commit: bf02e33

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

🌳 SharedTree DDS Changes

Add TreeArrayNodeAlpha with a new splice method (#26740)

Adds a splice method on TreeArrayNodeAlpha that supports removing and inserting items in a single operation to align with JavaScript's Array splice API. Returns the removed items as an array. Supports negative start indices (wraps from end). Optional deleteCount (omitting removes everything from start onward). The alpha API is accessible by an asAlpha cast on existing TreeArrayNodes, or using schemaFactoryAlpha. arrayAlpha nodes are accepted wherever TreeArrayNode is expected, but not the reverse. asAlpha is bidirectional since it's the same underlying schema.

Usage

import {
  SchemaFactory,
  SchemaFactoryAlpha,
  asAlpha,
} from "@fluidframework/tree";

// Using asAlpha to cast an existing TreeArrayNode
const sf = new SchemaFactory("example");
const Inventory = sf.array("Inventory", sf.string);
const inventory = new Inventory(["Apples", "Bananas", "Pears"]);
const inventoryAlpha = asAlpha(inventory);

// Using SchemaFactoryAlpha so splice is available directly
const sf = new SchemaFactoryAlpha("example");
const Inventory = sf.arrayAlpha("Inventory", sf.string);
const inventoryAlpha = new Inventory(["Apples", "Bananas", "Pears"]);

// Remove 2 items starting at index 0, insert new items in their place
const removed = inventoryAlpha.splice(0, 2, "Oranges", "Grapes");
// removed: ["Apples", "Bananas"]
// inventory: ["Oranges", "Grapes", "Pears"]

// Removed everything from index 1 onward (omitting deleteCount)
const rest = inventoryAlpha.splice(1);
// rest: ["Grapes", "Pears"]
// inventory: ["Oranges"]

Change details

Commit: f2b0cf9

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

⚠️ Deprecations

Deprecate IIdCompressorCore interface (#26865)

The IIdCompressorCore interface is deprecated and will be removed from the public API surface in 2.100.0. This also affects the return types of createIdCompressor and deserializeIdCompressor, which currently return IIdCompressor & IIdCompressorCore but will be narrowed to IIdCompressor.

Migration

  • serialize(): Use the new serializeIdCompressor(compressor, withSession) free function instead of calling compressor.serialize(withSession) directly.
  • takeNextCreationRange(), takeUnfinalizedCreationRange(), finalizeCreationRange(), beginGhostSession(): These are internal runtime operations that should not be called by external consumers. If you depend on these APIs, please file an issue on the FluidFramework repository describing your use case.
  • Return types of createIdCompressor / deserializeIdCompressor: Stop relying on the IIdCompressorCore portion of the intersection type. Type your variables as IIdCompressor instead of IIdCompressor & IIdCompressorCore.

Change details

Commit: 2e890f6

Affected packages:

  • @fluidframework/id-compressor

⬆️ Table of contents

Legacy API Changes

The deprecated getBranch API has been removed (#26796)

To obtain a branch-like object, create a view from your tree via viewWith. Or, use TreeAlpha.context to get a view from a TreeNode.

Change details

Commit: e80a48e

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

🛠️ Start Building Today!

Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!