Skip to content

Fluid Framework v2.112.0 (minor)

Latest

Choose a tag to compare

@github-actions github-actions released this 20 Jul 18:54
b210c3e

Contents

✨ New Features

Expose whether a container runtime has staged changes (#27648)

IContainerRuntimeBase now exposes hasStagedChanges, a flag indicating whether there are any changes submitted while in Staging Mode (via enterStagingMode) that have not yet been discarded or committed. A new hasStagedChangesChanged event is emitted whenever this value changes.

This is distinct from isDirty: a container runtime can be dirty due to ordinary unacknowledged local changes without having any staged changes.

containerRuntime.on("hasStagedChangesChanged", (hasStagedChanges) => {
  // update UI to reflect whether there are staged changes awaiting commit/discard
});

const hasStagedChanges = containerRuntime.hasStagedChanges;

Change details

Commit: c12943d

Affected packages:

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

⬆️ Table of contents

Start sharing local handle payloads before attachment (#27704)

Locally created Fluid handles can now expose an optional sharePayload() method that starts sharing their payload without attaching the handle to the Fluid object graph. Blob handles implement this method, allowing applications to begin uploading a blob before serializing its handle into a DDS.

const handle = await runtime.uploadBlob(bytes);

if (isLocalFluidHandle(handle) && handle.sharePayload !== undefined) {
  handle.sharePayload();
}

Change details

Commit: 2c4d5aa

Affected packages:

  • @fluidframework/core-interfaces
  • @fluidframework/container-runtime

⬆️ Table of contents

🌳 SharedTree DDS Changes

Add Component utilities for composing open-polymorphic schema (#27628)

A new @alpha Component namespace is now exported from @fluidframework/tree (and re-exported from fluid-framework). It provides utilities for composing independently authored application "components" that contribute to a shared configuration, which is useful for implementing "open polymorphism" schema patterns where the set of allowed types for a field or collection can be extended by separate libraries.

Each component is expressed as a Component.Factory: a function which receives a lazy reference to the composed configuration and returns the content that component contributes. Because the configuration is provided lazily, components may reference (including recursively) types contributed by other components. Component.compose combines a set of components into a Component.Composed, from which the aggregated configuration and per-component content can be read.

/** Example application component content type. */
interface MyAppComponentContent {
  /**
   * Item types contributed by this component.
   * We are just typing them as TreeNodeSchema here to keep things simple.
   * Real use would often provide some static factory to be able to create instances, as well as some APIs all item nodes should implement.
   */
  readonly items: Component.LazyArray<TreeNodeSchema>;
}

type MyAppComponent = Component.Factory<MyAppComponentContent>;

// A simple component, which does not depend on any other context.
const textComponent: MyAppComponent = () => ({
  items: () => [() => TextItem],
});

// A component which creates an item type which recursively depends on all item types.
const containerComponent: MyAppComponent = (config) => ({
  items: () => [
    () => class extends sf.array("Container", config().getComposed("items")) {},
  ],
});

const appConfig = Component.compose([containerComponent, textComponent]);

// The config's items can now be used to create a TreeViewConfiguration, root schema, or whatever else is needed.
class Root extends sf.object("Root", {
  content: appConfig.getComposed("items"),
}) {}

See the worked examples in openPolymorphism.integration.ts for end-to-end usage with SharedTree schema.

Change details

Commit: 7a56d09

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Independent tree views now accept an optional telemetry logger (#27567)

The alpha independentView, independentInitializedView, and createIndependentTreeAlpha APIs now accept an optional logger on their options. Previously these standalone (non-SharedTree) views had no way to surface telemetry, so internal events—including those emitted when the tree enters a broken state—were dropped. Passing a logger forwards those events to the caller's telemetry pipeline. This makes it possible to diagnose failures in scenarios that use independent tree views, such as snapshot import/export, schema migration, and other out-of-container workflows.

Events emitted by an independent tree view are tagged with the independentView namespace. If no logger is provided, behavior is unchanged and telemetry events continue to be dropped.

The logger option is typed as ITelemetryBaseLogger from @fluidframework/core-interfaces, so any standard Fluid telemetry logger can be passed directly.

// ...
const view = independentView(new TreeViewConfiguration({ schema: MySchema }), {
  logger: myTelemetryLogger,
});
// ...

Change details

Commit: 5fbbcab

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Retain history option (#27696)

Adds a new retainHistory flag to SharedTreeOptions (defaults to false). Setting retainHistory to true will prevent SharedTree from garbage-collecting historical data about old changes. Note that this will cause unbounded growth both in memory on the client and in summaries/snapshots (the at-rest data representing a Fluid document). For these reasons, this option is only intended for debugging and experimentation.

Change details

Commit: 2fa44c6

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Add at, pop, shift, unshift, findLast, and findLastIndex methods to TreeArrayNodeAlpha (#27686)

TreeArrayNodeAlpha now has at, pop, shift, unshift, findLast, and findLastIndex methods, further aligning it with JavaScript's built-in Array API:

  • at(index) at was already implemented at runtime, and consumers compiling with lib: ES2022 or later could already see it through the inherited ReadonlyArray typings. This change adds no new runtime behavior, but makes at an explicitly declared, documented part of the API, independent of the consumer's TypeScript lib configuration.
  • unshift(...items) is an alias for insertAtStart, mirroring how push aliases insertAtEnd: it inserts new item(s) at the start of the array. Unlike Array.prototype.unshift, it does not return the new length of the array.
  • pop() removes and returns the last item in the array, or returns undefined (without modifying the array) if it is empty.
  • shift() removes and returns the first item in the array, or returns undefined (without modifying the array) if it is empty.
  • findLast(predicate, thisArg?) and findLastIndex(predicate, thisArg?) search the array from the last item to the first, returning the last matching item (or undefined) and its index (or -1) respectively, like their Array.prototype equivalents. As with Array.prototype.findLast, passing a type guard as the findLast predicate narrows the returned item's type.

These methods are available on TreeArrayNodeAlpha, which can be obtained from an existing TreeArrayNode via asAlpha, or by declaring the schema with SchemaFactoryAlpha's arrayAlpha.

Usage

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

const sf = new SchemaFactory("example");
const Inventory = sf.array("Inventory", sf.string);
const inventory = asAlpha(new Inventory(["Apples", "Bananas", "Pears"]));

// inventory: ["Apples", "Bananas", "Pears"]
inventory.unshift("Oranges", "Grapes");
// inventory: ["Oranges", "Grapes", "Apples", "Bananas", "Pears"]

inventory.at(0); // "Oranges"
inventory.at(-1); // "Pears"
inventory.at(10); // undefined

inventory.findLast((item) => item.startsWith("G")); // "Grapes"
inventory.findLastIndex((item) => item.startsWith("G")); // 1

// inventory: ["Oranges", "Grapes", "Apples", "Bananas", "Pears"]
inventory.pop(); // "Pears"
// inventory ["Oranges", "Grapes", "Apples", "Bananas"]

inventory.shift(); // "Oranges"
// inventory: ["Grapes", "Apples", "Bananas"]

Change details

Commit: 5966900

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Shared branch names (#27708)

The existing createSharedBranch alpha API now takes an optional name string parameter that is associated with the shared branch. This name can be retrieved by passing the shared branch ID to getSharedBranchName.

Note that, unlike the shared branch IDs, shared branch names are not guaranteed to be unique.

Compatibility Implications

This change breaks compatibility in the following ways:

  • A document written by a client running an earlier FF version cannot be opened by a client running this version.
  • A document written by a client running this version cannot be opened by a client running an earlier FF version.
  • Clients running earlier FF versions will crash upon receiving ops from clients running this version.
  • Clients running this version will crash upon receiving ops from clients running earlier FF versions.

These breaks are only applicable for clients with enableSharedBranches turned on. Other clients are unaffected.

Change details

Commit: 1f08b92

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

🐛 Bug Fixes

Other Changes

Re-export telemetry types from fluid-framework (#27567)

The fluid-framework package now re-exports the following telemetry types from @fluidframework/core-interfaces:

  • ITelemetryBaseEvent
  • ITelemetryBaseLogger
  • LogLevel
  • LogLevelConst

Consumers can now import these types directly from fluid-framework without needing a separate dependency on @fluidframework/core-interfaces.

Change details

Commit: 5fbbcab

Affected packages:

  • fluid-framework

⬆️ Table of contents

🛠️ Start Building Today!

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