Skip to content

Releases: microsoft/FluidFramework

Fluid Framework v2.63.0 (minor)

14 Oct 18:23
4b609d4

Choose a tag to compare

Contents

🌳 SharedTree DDS Changes

Add FluidSerializableAsTree domain for representing trees of serializable data (alpha) (#25604)

Like JsonAsTree, but also supports Fluid Handles.

Change details

Commit: 46e6dce

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Promote Record node types and factories to beta (#25606)

Record tree node schema may now be declared using SchemaFactoryBeta in addition to SchemaFactoryAlpha.

Change details

Commit: 2e2de30

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Add TreeBeta.create (#25623)

Adds TreeBeta.create, which is a more stable version of the existing TreeAlpha.create. The only difference is the new TreeBeta.create does not support the @alpha UnsafeUnknownSchema option.

Change details

Commit: 376c2d1

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Alpha APIs for annotated allowed types have been refactored (#25595)

Staged allowed types must now be run through SchemaFactoryAlpha.types to convert them into an AllowedTypes. This change also means that it is now possible to use the produced AllowedTypesFull in non-alpha APIs since it implements AllowedTypes.

Reading data out of ImplicitAllowedTypes should now be done via normalizeAllowedTypes which now returns a AllowedTypesFull providing access to all the data in a friendly format.

Change details

Commit: c6ba37e

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Promote importConcise and exportConcise to beta (#25629)

importConcise and exportConcise were previously available via TreeAlpha. They may now also be accessed via TreeBeta.

Note that the beta form of importConcise does not support UnsafeUnknownSchema.

Change details

Commit: b5d4602

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Stabilize allowUnknownOptionalFields to beta (#25638)

When constructing object node schema with SchemaFactoryBeta.object or SchemaFactoryBeta.objectRecursive you can now provide the allowUnknownOptionalFields option as well as other metadata which were previously only available in SchemaFactoryAlpha.objectAlpha and SchemaFactoryAlpha.objectRecursive.

Additionally the alpha interface SchemaFactoryObjectOptions has been renamed to ObjectSchemaOptionsAlpha to better align with the other related types.

Change details

Commit: b7222d1

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Add SchemaFactoryAlpha.typesRecursive and SchemaFactoryAlpha.stagedRecursive (#25199)

With these new APIs, it is now possible to stage changes to recursive types.

Change details

Commit: 931e986

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

MinimumVersionForCollab is now used in place of tree's alpha FluidClientVersion (#25402)

FluidClientVersion: No longer used as the type for Fluid Client versions in APIs/codecs (for example, oldestCompatibleClient). Additionally, FluidClientVersion is now a const object with members that declare specific MinimumVersionForCollab versions. These are intended to be used with APIs that require a version (such as TreeAlpha.exportCompressed).

CodecWriteOptions and SharedTreeOptions: oldestCompatibleClient has been replaced by minVersionForCollab. See migration guide below.

TreeAlpha.exportCompressed: The options parameter previously had oldestCompatibleClient and now has minVersionForCollab. Migrating requires a rename. Existing FluidClientVersion.* values are now MinimumClientVersions.

Migrating

If an application is calling loadContainerRuntime directly and previously specified the minimum client version when initializing Shared Tree like:

    const factory = configuredSharedTree({ ..., oldestCompatibleClient: FluidClientVersion.v2_52 });

Then the new implementation depends on how the application initializes Fluid.

Applications using AzureClient/OdspClient

If an application is using the declarative model (for example, AzureClient/OdspClient), it should continue to call configuredSharedTree but specify minVersionForCollab instead:

    const factory = configuredSharedTree({ ..., minVersionForCollab: "2.52.0" });
Applications calling loadContainerRuntime

If an application is initializing the ContainerRuntime directly, it should now specify the minVersionForCollab there:

    const runtime = await loadContainerRuntime({ ..., minVersionForCollab: "2.52.0" });

Change details

Commit: 7f59e31

Affected packages:

  • @fluidframework/tree
  • 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!

Fluid Framework v2.62.0 (minor)

30 Sep 19:41
37cf630

Choose a tag to compare

Contents

🚨 Breaking Changes

The exports of @fluid-experimental/tree-react-api have been moved to the new @fluidframework/react package and placed under its /alpha exports (#25542)

@fluid-experimental/tree-react-api has been adjusted to align with Fluid Framework's API Support Levels. It has been renamed to @fluidframework/react and all existing APIs are now available under @fluidframework/react/alpha.

Since this package was under @fluid-experimental, previously it implicitly made no guarantees. Now all the APIs are @alpha, which also amounts to making no guarantees but makes it possible to promote APIs to @beta in the future to offer some stability.

To accommodate this change, all users of this package will need to adjust:

  • Package dependencies from "@fluid-experimental/tree-react-api" to "@fluidframework/react".
  • Imports from "@fluid-experimental/tree-react-api" to "@fluidframework/react/alpha".

Change details

Commit: b388c7b

Affected packages:

  • @fluid-experimental/tree-react-api
  • @fluidframework/react

⬆️ Table of contents

🌳 SharedTree DDS Changes

Added APIs for tracking observations of SharedTree content for automatic invalidation (#25459)

TreeAlpha.trackObservations and TreeAlpha.trackObservationsOnce have been added. These provide a way to run some operation which reads content from TreeNodes, then run a call back when anything observed by that operation changes.

This functionality has also been exposed in the form of React hooks and React higher order components via the @fluid-experimental/tree-react-api package. It is now possible to use these utilities to implement React applications which pass TreeNodes in their props and get all necessary invalidation from tree changes handled automatically. The recommended pattern for doing this is to use treeDataObject or TreeViewComponent at the root, then withTreeObservations or withMemoizedTreeObservations for any sub-components which read from TreeNodes. Alternatively more localized changes can be made by using PropNode to type erase TreeNodes passed in props, then use one of the usePropTreeNode or usePropTreeRecord hooks to read from them.

These APIs work with both hydrated and un-hydrated TreeNodes.

React Support

Here is a simple example of a React components which has an invalidation bug due to reading a mutable field from a TreeNode that was provided in a prop:

const builder = new SchemaFactory("example");
class Item extends builder.object("Item", { text: SchemaFactory.string }) {}
const ItemComponentBug = ({ item }: { item: Item }): JSX.Element => (
	<span>{item.text}</span> // Reading `text`, a mutable value from a React prop, causes an invalidation bug.
);

This bug can now easily be fixed using withTreeObservations or withMemoizedTreeObservations:

const ItemComponent = withTreeObservations(
	({ item }: { item: Item }): JSX.Element => <span>{item.text}</span>,
);

For components which take in TreeNodes, but merely forward them and do not read their properties, they can use PropTreeNode as shown:

const ItemParentComponent = ({ item }: { item: PropTreeNode<Item> }): JSX.Element => (
	<ItemComponent item={item} />
);

If such a component reads from the TreeNode, it gets a compile error instead of an invalidation bug. In this case the invalidation bug would be that if item.text is modified, the component would not re-render.

const InvalidItemParentComponent = ({
	item,
}: { item: PropTreeNode<Item> }): JSX.Element => (
	// @ts-expect-error PropTreeNode turns this invalidation bug into a compile error
	<span>{item.text}</span>
);

To provide access to TreeNode content in only part of a component the usePropTreeNode or usePropTreeRecord hooks can be used.

TreeAlpha.trackObservationsOnce Examples

Here is a rather minimal example of how TreeAlpha.trackObservationsOnce can be used:

cachedFoo ??= TreeAlpha.trackObservationsOnce(
  () => {
    cachedFoo = undefined;
  },
  () => nodeA.someChild.bar + nodeB.someChild.baz,
).result;

That is equivalent to doing the following:

if (cachedFoo === undefined) {
  cachedFoo = nodeA.someChild.bar + nodeB.someChild.baz;
  const invalidate = (): void => {
    cachedFoo = undefined;
    for (const u of unsubscribe) {
      u();
    }
  };
  const unsubscribe: (() => void)[] = [
    TreeBeta.on(nodeA, "nodeChanged", (data) => {
      if (data.changedProperties.has("someChild")) {
        invalidate();
      }
    }),
    TreeBeta.on(nodeB, "nodeChanged", (data) => {
      if (data.changedProperties.has("someChild")) {
        invalidate();
      }
    }),
    TreeBeta.on(nodeA.someChild, "nodeChanged", (data) => {
      if (data.changedProperties.has("bar")) {
        invalidate();
      }
    }),
    TreeBeta.on(nodeB.someChild, "nodeChanged", (data) => {
      if (data.changedProperties.has("baz")) {
        invalidate();
      }
    }),
  ];
}

Here is more complete example showing how to use TreeAlpha.trackObservationsOnce invalidate a property derived from its tree fields.

const factory = new SchemaFactory("com.example");
class Vector extends factory.object("Vector", {
  x: SchemaFactory.number,
  y: SchemaFactory.number,
}) {
  #length: number | undefined = undefined;
  public length(): number {
    if (this.#length === undefined) {
      const result = TreeAlpha.trackObservationsOnce(
        () => {
          this.#length = undefined;
        },
        () => Math.hypot(this.x, this.y),
      );
      this.#length = result.result;
    }
    return this.#length;
  }
}
const vec = new Vector({ x: 3, y: 4 });
assert.equal(vec.length(), 5);
vec.x = 0;
assert.equal(vec.length(), 4);

Change details

Commit: 21d45d5

Affected packages:

  • fluid-framework
  • @fluidframework/tree
  • @fluid-experimental/tree-react-api
  • @fluidframework/react

⬆️ Table of contents

Remove JsonValidator (#25381)

The @alpha API JsonValidator has been removed: its replacement FormatValidator must now be used.

As part of this:

  • typeboxValidator has been replaced with FormatValidatorBasic.
  • noopValidator has been replaced with FormatValidatorNoOp.

Change details

Commit: 64a9b88

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Add configuredSharedTreeBeta (#25531)

A limited subset of the options from the existing @alpha configuredSharedTree API have been stabilized to @beta in the form of configuredSharedTreeBeta.

import {
  configuredSharedTreeBeta,
  ForestTypeExpensiveDebug,
} from "fluid-framework/beta";
const SharedTree = configuredSharedTreeBeta({
  forest: ForestTypeExpensiveDebug,
});

Change details

Commit: 1e2d48f

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

⚠️ Deprecations

asTreeViewAlpha has been deprecated in favor of asAlpha. ([#25512](#255...

Read more

Fluid Framework v2.61.0 (minor)

23 Sep 02:45
b34a58a

Choose a tag to compare

Contents

✨ New Features

minVersionForCollab is now available on IFluidDataStoreContext (#25130)

minVersionForCollab is now passed down from the ContainerRuntime to the Datastore layer where it is made available for SharedObject construction. DDSes may optionally consume this value and use it to determine which sets of feature flags should be enabled.

Public type changes

  • @fluidframework/datastore: FluidDataStoreRuntime - Exposes minVersionForCollab.
  • @fluidframework/runtime-definitions: IFluidDataStoreContext - Exposes optional member minVersionForCollab. See FluidDataStoreContext for an example implementation.
  • @fluidframework/test-runtime-utils: MockFluidDataStoreContext, MockFluidDataStoreRuntime - Exposes minVersionForCollab either via a getter or as a readonly field.

Note that the new implementations are optional fields and in some cases accept undefined. This is needed for layer compatibility, and in a future release these members will no longer be optional.

Change details

Commit: 2566772

Affected packages:

  • @fluidframework/datastore
  • @fluidframework/runtime-definitions
  • @fluidframework/test-runtime-utils

⬆️ Table of contents

⚠️ Deprecations

Deprecate submitMessage on FluidDataStoreRuntime and MockFluidDataStoreRuntime (#25332)

As needed, access submitMessage via IFluidDataStoreContext/IFluidParentContext. See #24406 for details.

Change details

Commit: 687378a

Affected packages:

  • @fluidframework/datastore
  • @fluidframework/test-runtime-utils

⬆️ Table of contents

Legacy API Changes

Wrapped errors preserved as cause property (#25485)

IFluidErrorBase (internal basis for FluidFramework client errors) declares cause property matching ES2022 lib (whether targeted or not). When an error is wrapped, cause will be set to the originating error (which may or may not itself be an Error).

Change details

Commit: c69d56d

Affected packages:

  • @fluidframework/telemetry-utils

⬆️ 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.60.0 (minor)

02 Sep 20:30
8817a0b

Choose a tag to compare

Contents

🚨 Breaking Changes

Remove unnecessary and internal APIs in ISequenceIntervalCollection and related interval types (#25244)

The following APIs are now removed:

  • IInterval.clone
  • IInterval.modify
  • IInterval.union
  • ISerializableInterval
  • SequenceInterval.clone
  • SequenceInterval.modify
  • SequenceInterval.union
  • SequenceInterval.serialize
  • SequenceInterval.addPositionChangeListeners
  • SequenceInterval.removePositionChangeListeners

These APIs were never intended for public use. There is no migration path, and any usage is strongly discouraged, as it may result in severe errors or data corruption. Please remove any dependencies on these APIs as soon as possible.

Change details

Commit: 15d476e

Affected packages:

  • fluid-framework
  • @fluidframework/sequence

⬆️ Table of contents

🌳 SharedTree DDS Changes

Single-node insertion/removal APIs have been removed from TableSchema (alpha) (#25233)

There is a significant performance benefit to inserting / removing rows / columns in batches. To help encourage more performant usage patterns, single-node insertion and removal APIs have been removed. The APIs that operate on batches should be used instead.

Specifically:

  • insertColumn
    • Use insertColumns instead
  • insertRow
    • Use insertRows instead
  • removeColumn
    • Use removeColumns instead
  • removeRow
    • Use removeRows instead

Change details

Commit: 99281d2

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Range-based row/column removal methods have been added to TableSchema APIs (alpha) (#25235)

Adds range-based overloads to removeColumns and removeRows for removing contiguous ranges of rows and columns.

The removeAllColumns and removeAllRows methods have been removed, as they can be trivially implemented in terms of the new methods.

Change details

Commit: c803393

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Replace "TreeEncodingOptions.useStoredKeys" with "keys" and "KeyEncodingOptions" (#25263)

The alpha API TreeEncodingOptions has had its useStoredKeys boolean replaced with keys that takes a KeyEncodingOptions allowing for three options instead of the previous two. With the new API, it is now possible to control, for APIs which support it (like TreeAlpha.exportVerbose), if unknown optional fields will be included when exporting data using stored keys.

Additionally, the relevant options interfaces have been marked as @input, indicating that more options may be added as optional parameters in the future, and that should be considered non-breaking.

Change details

Commit: b65f2a8

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Hoist runTransaction method from TreeViewAlpha to TreeBranch (#25280)

Transactions are not view-schema-dependent, so it isn't necessary for them to be exclusive to the view type. runTransaction is now available on TreeBranch (alpha). TreeViewAlpha extends TreeBranch, so this change strictly makes the API more accessible.

Change details

Commit: a66b3b7

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

SchemaFactoryAlpha.recordRecursive now supports metadata (#25289)

SchemaFactoryAlpha.recordRecursive now support metadata like SchemaFactoryAlpha.recordAlpha and the other TreeNodeSchema creation methods on SchemaFactoryAlpha (except for SchemaFactoryAlpha.record which does not support metadata).

Change details

Commit: 8324170

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

FormatValidator added to replace JsonValidator (#25311)

The existing @alpha type JsonValidator has a new type-erased alternative, FormatValidator, which is planned to be stabilized to @beta in the future. It replaces JsonValidator in ICodecOptions. Existing code using ICodecOptions should migrate to use FormatValidator, but this is not required for adopting this release as JsonValidator is still supported.

Change details

Commit: 9db6e08

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Add SchemaFactoryBeta (#25313)

SchemaFactoryBeta is added to provide a place to partially stabilize APIs from SchemaFactoryAlpha. Initially just one APIs is added as @beta: scopedFactory. Users of the existing @alpha scopedFactory API on SchemaFactoryAlpha will need to update to use scopedFactoryAlpha if they require the returned factory to be a SchemaFactoryAlpha instance.

Change details

Commit: dca2361

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!

Fluid Framework v2.53.1 (patch)

25 Aug 21:09
a33ba13

Choose a tag to compare

What's Changed

  • fix(directory): Fix SharedDirectory.forEach() (#25299) #25301
  • Bump client patch version to 2.53.1 and update type tests #25254

Full Changelog: client_v2.53.0...client_v2.53.1

Fluid Framework v2.53.0 (minor)

18 Aug 20:00
4177e97

Choose a tag to compare

Contents

✨ New Features

Adds staged allowed types to SchemaFactoryAlpha (#25116)

This adds the staged API to SchemaFactoryAlpha. Staged allowed types can be used for schema evolution to add members to an AllowedTypes while supporting cross version collaboration.

Staged allowed types are allowed types that can be upgraded by schema upgrades. Before being upgraded, any attempt to insert or move a node to a location which requires its type to be upgraded to be valid will throw an error.

To add a new member to an AllowedTypes, add the type wrapped by staged. For example, migrating an array which previously supported only numbers to support both numbers and strings would start by deploying a version of the app using staged:

class TestArray extends schemaFactoryAlpha.arrayAlpha("TestArray", [
  SchemaFactoryAlpha.number,
  SchemaFactoryAlpha.staged(SchemaFactoryAlpha.string),
]) {}

Once enough clients have this code update, it is safe to allow writing strings to the array. To allow writing strings to the array, a code change must be made to remove the staged annotation:

class TestArray extends schemaFactoryAlpha.arrayAlpha("TestArray", [
  schemaFactoryAlpha.number,
  schemaFactoryAlpha.string,
]) {}

Then when opening old documents upgradeSchema is used to upgrade the stored schema:

view.upgradeSchema();

The @alpha API extractPersistedSchema now takes the schema as an ImplicitAnnotatedFieldSchema and an additional parameter to filter which staged upgrades it includes.

Below is a full example of how the schema migration process works. This can also be found in the tests.

// Schema A: only number allowed
const schemaA = SchemaFactoryAlpha.optional([SchemaFactoryAlpha.number]);

// Schema B: number or string (string is staged)
const schemaB = SchemaFactoryAlpha.optional([
  SchemaFactoryAlpha.number,
  SchemaFactoryAlpha.staged(SchemaFactoryAlpha.string),
]);

// Schema C: number or string, both fully allowed
const schemaC = SchemaFactoryAlpha.optional([
  SchemaFactoryAlpha.number,
  SchemaFactoryAlpha.string,
]);

// Initialize with schema A.
const configA = new TreeViewConfiguration({
  schema: schemaA,
});
const viewA = treeA.viewWith(configA);
viewA.initialize(5);

// Since we are running all the different versions of the app in the same process making changes synchronously,
// an explicit flush is needed to make them available to each other.
synchronizeTrees();

assert.deepEqual(viewA.root, 5);

// View the same document with a second tree using schema B.
const configB = new TreeViewConfiguration({
  schema: schemaB,
});
const viewB = treeB.viewWith(configB);
// B cannot write strings to the root.
assert.throws(() => (viewB.root = "test"));

// View the same document with a third tree using schema C.
const configC = new TreeViewConfiguration({
  schema: schemaC,
});
const viewC = treeC.viewWith(configC);
// Upgrade to schema C
viewC.upgradeSchema();
// Use the newly enabled schema.
viewC.root = "test";

synchronizeTrees();

// View A is now incompatible with the stored schema:
assert.equal(viewA.compatibility.canView, false);

// View B can still read the document, and now sees the string root which relies on the staged schema.
assert.deepEqual(viewB.root, "test");

Change details

Commit: 59baf03

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

StateFactory.latestMap now accepts a validator parameter (#25172)

The StateFactory.latestMap API now accepts a validator argument. The validator is a function that will be called at runtime to verify that the data is valid. This is especially useful when changing the schema of presence data.

See the presence documentation for more details.

Change details

Commit: cac39eb

Affected packages:

  • @fluidframework/presence

⬆️ Table of contents

🌳 SharedTree DDS Changes

TableSchema's "removeColumn" API now removes corresponding cells (alpha) (#25213)

Previously, the removeColumn API on Table nodes derived from TableSchema (alpha) only removed the Column node from the list of columns tracked by the table. To also remove the corresponding cells from the table (which are stored on the Row nodes), the user was required to write a custom transaction that removed the column and cells.

The motivation for this design was due to performance concerns with transactions. Those concerns are still relevant, but the data leak risk of dropping columns without removing corresponding cells seems a greater risk, and we have plans to address the performance issues with transactions.

Change details

Commit: b665ba8

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Export TreeNode not only as a type (#25226)

TreeNode can now be used as a runtime object. This enables checking if an object is a TreeNode with instanceof. TreeNode has customized instanceof support so it can detect TreeNode instances, even if they hide their prototype like POJO mode nodes do.

Change details

Commit: eefb952

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

🐛 Bug Fixes

Allow edits in arrays to be concurrent to dependent edits of transactions with violated constraints (#25191)

Before this release, making concurrent edits to an array could lead to assertion error 0x8a2 being thrown if the following conditions were met:

  • Some edit e1 was a transaction with a constraint that turned out to be violated by edits concurrent to (and sequenced before) e1
  • Some edit e2 was dependent on e1 (from before the violation of its constraint)
  • Some edit e3 was concurrent to and sequenced after both e1 and e2
  • e3 was either concurrent to or the revert of some other edit e0 that predated e1, e2, and e3.
  • e0 and e2 made edits to the same gap (that is, in the same space between nodes) in the sequence/array.

After this release, these scenarios will work as expected (that is, no assertion error thrown).

Change details

Commit: ef64bae

Affected packages:

  • @fluidframework/tree
  • fluid-framework

[⬆️ Table ...

Read more

Fluid Framework v2.52.0 (minor)

04 Aug 20:52
5fa574a

Choose a tag to compare

Contents

🌳 SharedTree DDS Changes

Change and clarify limitations related to alpha features allowUnknownOptionalFields and importVerbose (#25074)

allowUnknownOptionalFields currently has some limitations. To mitigate some bugs, importVerbose has dropped support for unknown optional fields. Previously importVerbose would tolerate some unknown optional fields, but could not validate they comply with the document stored schema. This could cause some crashes, and likely document corruption. This support has been removed: now trying to create nodes containing unknown optional fields via importVerbose will throw a UsageError. There is no longer a way to create and insert nodes which contain subtrees for which there is no schema.

Ideally exportVerbose and importVerbose could be used to round trip data while optionally preserving unknown optional fields, but this is currently not working and thus not supported.

If exporting using useStoredKeys, the unknown optional fields will be preserved but may not be able to be imported. If exporting not using useStoredKeys, unknown optional fields will be omitted.

Change details

Commit: 62dc0b0

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Fix independentInitializedView when used with ForestTypeExpensiveDebug (#25083)

Previously, when using independentInitializedView with ForestTypeExpensiveDebug and the root schema was not an optional field, an error was thrown about the tree being out of schema. This has been fixed.

Change details

Commit: 2570b65

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Allow attaching a SharedTree to an already attached container (#25102)

Before this release, attaching a SharedTree instance to an already attached container would fail with assert code 0x88f if that instance needed to include data about removed nodes in its attach summary. (This is the case when nodes are removed before attaching and there is a local branch that forks from a commit that made such a removal or from an earlier commit. This is also the case when retaining Revertible objects for those commits).

After this release, the behavior depends on the CodecWriteOptions.oldestCompatibleClient value:

  • For values < FluidClientVersion.v2_52, the behavior is the same.
  • For values >= FluidClientVersion.v2_52, the attach will succeed, but use a newer storage format.

Applications should take care to saturate their clients with FF version 2.52 (or greater) before using a CodecWriteOptions.oldestCompatibleClient that is equal to or greater than FluidClientVersion.v2_52. Failure to do so may lead clients with CodecWriteOptions.oldestCompatibleClient equal to or greater than FluidClientVersion.v2_52 to attach SharedTree instances using a storage format that is not supported by FF versions before 2.52. This means that application versions using FF versions before 2.52 will be unable to open documents where such an operation has happened.

Change details

Commit: 3e03f94

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Support unknown optional fields in TreeBeta.clone, TreeAlpha.exportConcise and TreeAlpha.exportVerbose (#25106)

Trees with unknown optional fields are now supported in TreeBeta.clone, TreeBeta.exportConcise and TreeBeta.exportVerbose.

Previously, attempts to clone or export such nodes could error in some cases, but should now work robustly.

Change details

Commit: e3a126f

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

⚠️ Deprecations

Moved MinimumVersionForCollab to @fluidframework/runtime-definitions (#25059)

MinimumVersionForCollab has been moved from @fluidframework/container-runtime to @fluidframework/runtime-definitions. The export in @fluidframework/container-runtime is now deprecated and will be removed in a future version. Consumers should import it from @fluidframework/runtime-definitions going forward.

Change details

Commit: 4a7b370

Affected packages:

  • @fluidframework/container-runtime
  • @fluidframework/runtime-utils

⬆️ Table of contents

Legacy API Changes

Introduced new interface "IRuntimeStorageService" to replace "IDocumentStorageService" between Runtime and DataStore layers (#25057)

Added an interface IRuntimeStorageService which will replace IDocumentStorageService in the DataStore layer. This is exposed by the Runtime layer to the DataStore layer. This new interface will only contain properties that are needed and used by the DataStore layer.

The following properties from IRuntimeStorageService are deprecated as they are not needed by the DataStore layer. These be removed in a future release:

  • disposed
  • dispose
  • policies
  • getSnapshotTree
  • getSnapshot
  • getVersions
  • createBlob
  • uploadSummaryWithContext
  • downloadSummary

Change details

Commit: 92012de

Affected packages:

  • @fluidframework/runtime-...
Read more

Fluid Framework v2.51.0 (minor)

21 Jul 19:42
31951ec

Choose a tag to compare

Contents

🚨 Breaking Changes

@fluid-experimental/attributable-map package removed (#25019)

The @fluid-experimental/attributable-map package has been removed without replacement.

Change details

Commit: c8bbd6d

Affected packages:

  • @fluid-experimental/attributable-map

⬆️ Table of contents

🌳 SharedTree DDS Changes

Fix adaptEnum's handling of numeric enums (#24957)

Enum entries whose values are numeric get additional properties on TypeScript's generated Enum object. These values were getting treated like enum entries at runtime by adaptEnum (@beta). This has been fixed and the runtime behavior now matches the types in this case.

If any documents were created with this API which were impacted by this bug and keeping them openable is required, they will need a workaround. Impacted schema using the union from adaptEnum can need to be updated to explicitly include the previously erroneously generated schema.

Before:

enum Mode {
  a = 1,
}
const ModeNodes = adaptEnum(schemaFactory, Mode);
const union = ModeNodes.schema;

After:

enum Mode {
  a = 1,
}
const ModeNodes = adaptEnum(schemaFactory, Mode);
// Bugged version of adaptEnum used to include this: it should not be used but must be included in the schema for legacy document compatibility.
class Workaround extends schemaFactory.object("a", {}) {}
const union = [...ModeNodes.schema, Workaround] as const;

To help detect when schema contain unexpected content, and to ensure workarounds like this are implemented properly, applications should include tests which check the schema for compatibility. See tree-cli-app's schema tests for an example of how to do this.

The schema returned by adaptEnum have also been updated to toString to include the value of the particular enum entry: this has no effect on the nodes, just the schema.

Change details

Commit: 7535d31

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

"rootChanged" event is no longer skipped if first change is setting the root to undefined (#24994)

A bug has been fixed where rootChanged would not be fired if the change is the first change since the TreeView became in-schema, and the change was setting the document root to undefined.

Change details

Commit: e6f2587

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Make POJO mode TreeArrayNodes report the array constructor as their constructor (#24988)

Make POJO mode TreeArrayNode's inherited constructor property report Array instead of the TreeNodeSchema class. This is necessary to make TreeArrayNodes appear equal to arrays according to NodeJS's assert.strict.deepEqual in NodeJS 22.

Change details

Commit: 7b4d0ab

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Legacy API Changes

New "getSnapshotTree" API on "IChannelStorageService" (#24970)

A new API, getSnapshotTree has been added to IChannelStorageService. It should return the snapshot tree for a channel. This will help channels examine their snapshot when it consists of dynamic trees and blobs, i.e., when the number of tree and blobs and / or their keys are not known in advance. This is optional for backwards compatibility, and will become required in the future.

Change details

Commit: d1241a4

Affected packages:

  • @fluidframework/datastore-definitions

⬆️ Table of contents

New "PureDataObject" implementation "TreeDataObject" added (#25025)

A new implementation of PureDataObject has been added: TreeDataObject. Where DataObject stores its contents in a SharedDirectory, TreeDataObject stores its contents in a SharedTree.

Change details

Commit: f4fbd2f

Affected packages:

  • @fluidframework/aqueduct

⬆️ Table of contents

Add API for tree-based root data object (#25030)

Adds createTreeContainerRuntimeFactory which can be used to create runtime factories which construct containers with an entry point containing a single tree-based root data object.

Change details

Commit: 30a0d7e

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.50.0 (minor)

07 Jul 23:52
4e9e046

Choose a tag to compare

Contents

🚨 Breaking Changes

IFluidHandleInternal.bind (deprecated) has been removed (#24974)

IFluidHandleInternal.bind was deprecated in 2.40 and has now been removed. See release notes entry for more details.

Change details

Commit: 07e1837

Affected packages:

  • @fluidframework/core-interfaces
  • @fluidframework/datastore
  • @fluidframework/runtime-utils
  • @fluidframework/test-runtime-utils

⬆️ Table of contents

🌳 SharedTree DDS Changes

Record node kind was added (alpha) (#24908)

Adds a new kind of node to SharedTree that models a TypeScript record. As is the case with map nodes, record nodes only support string keys.

class MyRecord extends schemaFactory.record("my-record", [
  schemaFactory.number,
  schemaFactory.string,
]) {}
const myRecord = new MyRecord({
  foo: 42,
  bar: "Hello world!",
});

const foo = myRecord.foo; // 42

delete myRecord.foo;

myRecord.baz = 37;

const keys = Object.keys(myRecord); // ["bar", "baz"]
const values = Object.values(myRecord); // ["Hello world!", 37]
const entries = Object.entries(myRecord); // [["bar", "Hello world!"], ["baz", 37]]

NodeKind enum update

This change includes the addition of a new flag to the NodeKind enum. This API notes in its documentation that users should not treat its flags as an exhaustive set.

This change may break code that treats it that way. We recommend updating your code to be more tolerant of unknown node kinds going forward.

Also see alternative options for schema-agnostic tree traversal if needed:

Additional features

In addition to the operations afforded by TypeScript records, SharedTree record nodes can be iterated (equivalent to Object.entries).

class MyRecord extends schemaFactory.record("my-record", [schemaFactory.number, schemaFactory.string]) {}
const myRecord = new MyRecord({
	foo: 42,
	bar: "Hello world!"
});

for (const [key, value] of myRecord) {
	...
}

const a = { ...myRecord }; // { foo: 42, bar: "Hello world!" }
const b = [...myRecord]; // [["foo", 42], ["bar, "Hello world!"]]

Recursive records

Recursive record schema can be defined using recordRecursive on SchemaFactoryAlpha.

class MyRecord extends schemaFactory.recordRecursive("my-record", [
  schemaFactory.string,
  () => MyRecord,
]) {}
const myRecord = new MyRecord({
  foo: "Hello world!",
  bar: new MyRecord({
    x: "foo",
    y: new MyRecord({}),
  }),
});

TableSchema update (alpha)

The TableSchema APIs have been updated to use record nodes in the schema they generate. Specifically, the Row representation now uses a record to store its column-cell pairs, rather than a map.

The node types derived from these APIs model their data in a row-major format. That is, each row in the table contains the set of cells that belong to that row, where each cell is indexed by its corresponding column.

Previously, this was modeled using a MapNode. This format proved cumbersome to interop with popular table rendering libraries like tanstack, which expect a record-like format.

The persisted format of documents containing trees derived from these APIs is the same, so this change is forward and backward compatible.

JsonDomainSchema update (alpha)

JsonObject has been updated to a record rather than a map.

The persisted format of documents containing trees derived from these APIs is the same, so this change is forward and backward compatible.

Change details

Commit: b25667b

Affected packages:

  • @fluidframework/tree
  • fluid-framework

⬆️ Table of contents

Other Changes

StateFactory.latest now accepts a validator parameter (#24432)

The StateFactory.latest API now accepts a validator argument. The validator is a function that will be called at runtime to verify that the data is valid. This is especially useful when changing the schema of presence data.

See the presence documentation for more details.

Change details

Commit: ffe5d0b

Affected packages:

  • @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.43.0 (minor)

24 Jun 01:07
629c449

Choose a tag to compare

Contents

🚨 Breaking Changes

The reason parameter on the disconnect event now accepts undefined to allow for clean, non-error disconnections (#24840)

To enable better handling of intentional disconnects (for example Container.dispose()), the reason parameter of the disconnect event on IDocumentDeltaConnectionEvents now accepts undefined as a valid value.

Old signature:

listener: (reason: IAnyDriverError) => void

New signature:

listener: (reason: IAnyDriverError | undefined) => void

Developers with listeners for the disconnect event should update their implementations to handle cases where the reason parameter is undefined.
This indicates a clean disconnect, which should not be treated as an error.

The breaking change is scheduled to be released in version 2.60.

Change details

Commit: 82a1c5a

Affected packages:

  • @fluidframework/container-loader
  • @fluidframework/driver-base
  • @fluidframework/driver-definitions

⬆️ Table of contents

✨ New Features

Persisted metadata for Shared Tree schemas (Alpha) (#24812)

The persisted metadata feature for Shared Tree allows an application author to write document-persisted metadata along with the schema. This feature is supported for both node and field schemas.

Using the persisted metadata feature

As of now, persisted metadata support is available via the SchemaFactoryAlpha API:

// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

Persisted metadata can take the shape of any JSON-serializable object, e.g.:

const persistedMetadata = { a: 2 };

Feature flag

To enable persisted metadata, use configuredSharedTree to specify the format version. The tree that is returned can be substituted in place of the default SharedTree object exported by the Fluid Framework. For example:

const tree = configuredSharedTree({
  formatVersion: SharedTreeFormatVersion.v5,
}).create(runtime);

export const MyContainerSchema = {
  initialObjects: {
    appData: tree,
  },
} satisfies ContainerSchema;

Examples

Field schemas with persisted metadata
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { a: 2 };

// Foo is an object type with metadata
class Foo extends schemaFactory.objectAlpha(
  "Foo",
  {
    // Metadata for a required number field
    bar: schemaFactory.required(schemaFactory.number, { persistedMetadata }),

    // Metadata for an optional string field
    baz: schemaFactory.optional(schemaFactory.string, { persistedMetadata }),
    // Metadata for the object type Foo
  },
  { persistedMetadata },
) {}
Recursive field schemas
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { a: 2 };

// Recursive object schema with persisted metadata
class RecursiveObject extends schemaFactory.objectRecursive(
  "RecursiveObject",
  {
    x: [() => RecursiveObject, schemaFactory.number],
  },
  { persistedMetadata },
) {}

// Recursive field schema with metadata
const recursiveField = schemaFactory.optionalRecursive(
  [() => RecursiveObject, schemaFactory.number],
  { persistedMetadata },
);
Recursive object schemas
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { a: 2 };

// Recursive array schema
class Foos extends schemaFactory.arrayRecursive("FooList", [() => Foo], {
  persistedMetadata,
}) {}

// Recursive object schema
class Foo extends schemaFactory.objectRecursive(
  "Foo",
  { fooList: Foos },
  { persistedMetadata },
) {}

// Recursive map schema
class FooMap extends schemaFactory.mapRecursive("FooMap", [() => Foo], {
  persistedMetadata,
}) {}

Change details

Commit: 3f81ab5

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

🌳 SharedTree DDS Changes

Tree's enum schema utility are now beta (#24749)

The functions singletonSchema, adaptEnum and enumFromStrings are now @beta instead of @alpha.

Change details

Commit: a23bc9e

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Add TreeAlpha.child and TreeAlpha.children APIs for generic tree traversal (#24723)

TreeAlpha.child

Access a child node or value of a TreeNode by its property key.

class MyObject extends schemaFactory.object("MyObject", {
	foo: schemaFactory.string;
	bar: schemaFactory.optional(schemaFactory.string);
}) {}

const myObject = new MyObject({
	foo: "Hello world!"
});

const foo = TreeAlpha.child(myObject, "foo"); // "Hello world!"
const bar = TreeAlpha.child(myObject, "bar"); // undefined
const baz = TreeAlpha.child(myObject, "baz"); // undefined
class MyArray extends schemaFactory.array("MyArray", schemaFactory.string) {}

const myArray = new MyArray("Hello", "World");

const child0 = TreeAlpha.child(myArray, 0); // "Hello"
const child1 = TreeAlpha.child(myArray, 1); // "World
const child2 = TreeAlpha.child(myArray, 2); // undefined

TreeAlpha.children

Get all child nodes / values of a TreeNode, keyed by their property keys.

class MyObject extends schemaFactory.object("MyObject", {
	foo: schemaFactory.string;
	bar: schemaFactory.optional(schemaFactory.string);
	baz: schemaFactory.optional(schemaFactory.number);
}) {}

const myObject = new MyObject({
	foo: "Hello world!",
	baz: 42,
});

const children = TreeAlpha.children(myObject); // [["foo", "Hello world!"], ["baz", 42]]
class MyArray extends schemaFactory.array("MyArray", schemaFactory.string) {}

const myArray = new MyArray("Hello", "World");

const children ...
Read more