Dependency updates only.
-
Metadata can be associated with Node Schema (#23321) 58619c3c4e
Users of TreeView can now specify metadata when creating Node Schema, via
SchemaFactoryAlpha
. This metadata may include system-understood properties likedescription
.Example:
const schemaFactory = new SchemaFactoryAlpha(...); class Point extends schemaFactory.object("Point", { x: schemaFactory.required(schemaFactory.number), y: schemaFactory.required(schemaFactory.number), }, { metadata: { description: "A point in 2D space", }, }) {}
Functionality like the experimental conversion of Tree Schema to JSON Schema (getJsonSchema) leverages such system-understood metadata to generate useful information. In the case of the
description
property, it is mapped directly to thedescription
property supported by JSON Schema.Custom, user-defined properties can also be specified. These properties will not be used by the system by default, but can be used to associate common application-specific properties with Node Schema.
object
andobjectRecursive
,arrayRecursive
, andmapRecursive
now supportmetadata
in theiroptions
parameter.- (new)
arrayAlpha
- Variant ofarray
that accepts an options parameter which supportsmetadata
- (new)
mapAlpha
- Variant ofmap
that accepts an options parameter which supportsmetadata
An application is implementing search functionality. By default, the app author wishes for all app content to be potentially indexable by search, unless otherwise specified. They can leverage schema metadata to decorate types of nodes that should be ignored by search, and leverage that information when walking the tree during a search.
interface AppMetadata { /** * Whether or not nodes of this type should be ignored by search. * @defaultValue `false` */ searchIgnore?: boolean; } const schemaFactory = new SchemaFactoryAlpha(...); class Point extends schemaFactory.object("Point", { x: schemaFactory.required(schemaFactory.number), y: schemaFactory.required(schemaFactory.number), }, { metadata: { description: "A point in 2D space", custom: { searchIgnore: true, }, } }) {}
Search can then be implemented to look for the appropriate metadata, and leverage it to omit the unwanted position data from search.
These changes add the new property "metadata" to the base type from which all node schema derive. If you have existing node schema subclasses that include a property of this name, there is a chance for potential conflict here that could be breaking. If you encounter issues here, consider renaming your property or leveraging the new metadata support.
-
New alpha APIs for schema evolution (#23362) 2406e00efe
There are now
@alpha
APIs for schema evolution which support adding optional fields to object node types without a staged rollout.SharedTree has many safety checks in place to ensure applications understand the format of documents they must support. One of these checks verifies that the view schema (defined in application's code) aligns with the document schema (determined by the document data at rest). This helps to ensure that clients running incompatible versions of the application's code don't collaborate at the same time on some document, which could cause data loss or disrupt application invariants. One general solution application authors can perform is to stage the rollout of a feature which changes document schema into multiple phases:
- Release an application version which understands documents written with the new format but doesn't attempt to upgrade any documents
- Wait for this application version to saturate in the app's ecosystem
- Release an application version which upgrades documents to start leveraging the new format.
However, this process can be cumbersome for application authors: for many types of changes, an app author doesn't particularly care if older application code collaborates with newer code, as the only downside is that the older application version might not present a fully faithful experience. As an example, consider an application which renders circles on a canvas (similar to what is presented here). The application author might anticipate adding support to render the circle with various different other properties (border style, border width, background color, varying radius, etc.). Therefore, they should declare their schema using
SchemaFactoryObjectOptions.allowUnknownOptionalFields
like so:import { SchemaFactoryAlpha } from "@fluidframework/tree/alpha"; // "Old" application code/schema const factory = new SchemaFactoryAlpha("Geometry"); class Circle extends factory.object( "Circle", { x: factory.number, y: factory.number, }, { allowUnknownOptionalFields: true }, ) {}
Later, they add some of these features to their application:
import { SchemaFactoryAlpha } from "@fluidframework/tree/alpha"; // "New" application code/schema const factory = new SchemaFactoryAlpha("Geometry"); class Circle extends factory.object( "Circle", { x: factory.number, y: factory.number, // Note that radius and color must both be declared as optional fields since this application must // support opening up existing documents that didn't have this information. radius: factory.optional(factory.number), color: factory.optional(factory.string), // ex: #00FF00 }, { allowUnknownOptionalFields: true }, ) {}
When they go to deploy this newer version of the application, they could opt to start upgrading documents as soon as the newer code is rolled out, and the older code would still be able to open up (and collaborate on) documents using the newer schema version. Note that it's only important that the old application code elected to allow opening documents with unknown optional fields. This policy is not persisted into documents in any form, so applications are free to modify it at any point.
For specific API details, see documentation on
SchemaFactoryObjectOptions.allowUnknownOptionalFields
. For a more thorough discussion of this topic, see Schema Evolvability in the SharedTree README.
Dependency updates only.
-
Revertible objects can now be cloned using
RevertibleAlpha.clone()
(#23044) 5abfa015afThe
DisposableRevertible
interface has been replaced withRevertibleAlpha
. The newRevertibleAlpha
interface extendsRevertible
and includes aclone(branch: TreeBranch)
method to facilitate cloning a Revertible to a specified target branch. The source branch where theRevertibleAlpha
was created must share revision logs with the target branch where theRevertibleAlpha
is being cloned. If this condition is not met, the operation will throw an error. -
Providing unused properties in object literals for building empty ObjectNodes no longer compiles (#23162) dc3c30019e
ObjectNodes with no fields will now emit a compiler error if constructed from an object literal with fields. This matches the behavior of non-empty ObjectNodes which already gave errors when unexpected properties were provided.
class A extends schemaFactory.object("A", {}) {} const a = new A({ thisDoesNotExist: 5 }); // This now errors.
-
✨ New! Alpha APIs for indexing (#22491) cd95357ba8
SharedTree now supports indexing via two new APIs,
createSimpleTreeIndex
andcreateIdentifierIndex
.createSimpleTreeIndex
is used to create aSimpleTreeIndex
which indexes nodes based on their schema. Depending on the schema, the user specifies which field to key the node on.The following example indexes
IndexableParent
s andIndexableChild
s and returns the first node of a particular key:function isStringKey(key: TreeIndexKey): key is string { return typeof key === "string"; } const index = createSimpleTreeIndex( view, new Map([ [IndexableParent, parentKey], [IndexableChild, childKey], ]), (nodes) => nodes[0], isStringKey, [IndexableParent, IndexableChild], );
createIdentifierIndex
is used to create anIdentifierIndex
which provides an efficient way to retrieve nodes using the node identifier.Example:
const identifierIndex = createIdentifierIndex(view); const node = identifierIndex.get("node12345");
-
Unsupported merge-tree types and related exposed internals have been removed (#22696) 7a032533a6
As part of ongoing improvements, several internal types and related APIs have been removed. These types are unnecessary for any supported scenarios and could lead to errors if used. Since directly using these types would likely result in errors, these changes are not likely to impact any Fluid Framework consumers.
Removed types:
- IMergeTreeTextHelper
- MergeNode
- ObliterateInfo
- PropertiesManager
- PropertiesRollback
- SegmentGroup
- SegmentGroupCollection
In addition to removing the above types, they are no longer exposed through the following interfaces and their implementations:
ISegment
,ReferencePosition
, andISerializableInterval
.Removed functions:
- addProperties
- ack
Removed properties:
- propertyManager
- segmentGroups
The initial deprecations of the now changed or removed types were announced in Fluid Framework v2.2.0: Fluid Framework v2.2.0
-
Fix typing bug in
adaptEnum
andenumFromStrings
(#23077) cfb68388cbWhen using the return value from
adaptEnum
as a function, passing in a value who's type is a union no longer produced an incorrectly typed return value. This has been fixed.Additionally
enumFromStrings
has improved the typing of its schema, ensuring the returned object's members have sufficiently specific types. Part of this improvement was fixing the.schema
property to be a tuple over each of the schema where it was previously a tuple of a single combined schema due to a bug.One side-effect of these fixes is that narrowing of the
value
field of a node typed from the.schema
behaves slightly different, such that the node type is now a union instead of it being a single type with a.value
that is a union. This means that narrowing based on.value
property narrows which node type you have, not just the value property. This mainly matters when matching all cases like the switch statement below:const Mode = enumFromStrings(schema, ["Fun", "Bonus"]); type Mode = TreeNodeFromImplicitAllowedTypes<typeof Mode.schema>; const node = new Mode.Bonus() as Mode; switch (node.value) { case "Fun": { assert.fail(); } case "Bonus": { // This one runs break; } default: // Before this change, "node.value" was never here, now "node" is never. unreachableCase(node); }
-
SharedTree event listeners that implement
Listenable
now allow deregistration of event listeners via anoff()
function. (#23046) c59225db03The ability to deregister events via a callback returned by
on()
remains the same. Both strategies will remain supported and consumers of SharedTree events may choose which method of deregistration they prefer in a given instance.// The new behavior function deregisterViaOff(view: TreeView<MySchema>): { const listener = () => { /* ... */ }; view.events.on("commitApplied", listener); // Register view.events.off("commitApplied", listener); // Deregister } // The existing behavior (still supported) function deregisterViaCallback(view: TreeView<MySchema>): { const off = view.events.on("commitApplied", () => { /* ... */ }); // Register off(); // Deregister }
-
Allow constructing recursive maps from objects (#23070) 0185a08c6f
Previously only non-recursive maps could be constructed from objects. Now all maps nodes can constructed from objects:
class MapRecursive extends sf.mapRecursive("Map", [() => MapRecursive]) {} { type _check = ValidateRecursiveSchema<typeof MapRecursive>; } // New: const fromObject = new MapRecursive({ x: new MapRecursive() }); // Existing: const fromIterator = new MapRecursive([["x", new MapRecursive()]]); const fromMap = new MapRecursive(new Map([["x", new MapRecursive()]])); const fromNothing = new MapRecursive(); const fromUndefined = new MapRecursive(undefined);
-
SharedString DDS annotateAdjustRange (#22751) d54b9dde14
This update introduces a new feature to the
SharedString
DDS, allowing for the adjustment of properties over a specified range. TheannotateAdjustRange
method enables users to apply adjustments to properties within a given range, providing more flexibility and control over property modifications.An adjustment is a modification applied to a property value within a specified range. Adjustments can be used to increment or decrement property values dynamically. They are particularly useful in scenarios where property values need to be updated based on user interactions or other events. For example, in a rich text editor, adjustments can be used for modifying indentation levels or font sizes, where multiple users could apply differing numerical adjustments.
- Adjustments with Constraints: Adjustments can include optional minimum and maximum constraints to ensure the final value falls within specified bounds. This is particularly useful for maintaining consistent formatting in rich text editors.
- Consistent Property Changes: The feature ensures that property changes are consistent, managing both local and remote changes effectively. This is essential for collaborative rich text editing where multiple users may be making adjustments simultaneously.
- Rich Text Formatting: Adjustments can be used to modify text properties such as font size, indentation, or other formatting attributes dynamically based on user actions.
This feature is only available when the configuration
Fluid.Sequence.mergeTreeEnableAnnotateAdjust
is set totrue
. Additionally, all collaborating clients must have this feature enabled to use it. If any client does not have this feature enabled, it will lead to the client exiting collaboration. A future major version of Fluid will enable this feature by default.sharedString.annotateAdjustRange(start, end, { key: { value: 5, min: 0, max: 10 }, });
-
MergeTree
Client
Legacy API Removed (#22697) 2aa0b5e794The
Client
class in the merge-tree package has been removed. Types that directly or indirectly expose the merge-treeClient
class have also been removed.The removed types were not meant to be used directly, and direct usage was not supported:
- AttributionPolicy
- IClientEvents
- IMergeTreeAttributionOptions
- SharedSegmentSequence
- SharedStringClass
Some classes that referenced the
Client
class have been transitioned to interfaces. Direct instantiation of these classes was not supported or necessary for any supported scenario, so the change to an interface should not impact usage. This applies to the following types:- SequenceInterval
- SequenceEvent
- SequenceDeltaEvent
- SequenceMaintenanceEvent
The initial deprecations of the now changed or removed types were announced in Fluid Framework v2.4.0: Several MergeTree Client Legacy APIs are now deprecated
-
✨ New! Alpha APIs for tree data import and export (#22566) 18a23e8816
A collection of new
@alpha
APIs for importing and exporting tree content and schema from SharedTrees has been added toTreeAlpha
. These include import and export APIs forVerboseTree
,ConciseTree
and compressed tree formats.TreeAlpha.create
is also added to allow constructing trees with a more general API instead of having to use the schema constructor directly (since that doesn't handle polymorphic roots, or non-schema aware code).The function
independentInitializedView
has been added to provide a way to combine data from the existingextractPersistedSchema
and newTreeAlpha.exportCompressed
back into aTreeView
in a way which can support safely importing data which could have been exported with a different schema. This allows replicating the schema evolution process for Fluid documents stored in a service, but entirely locally without involving any collaboration services.independentView
has also been added, which is similar but handles the case of creating a new view without an existing schema or tree.Together these APIs address several use-cases:
- Using SharedTree as an in-memory non-collaborative datastore.
- Importing and exporting data from a SharedTree to and from other services or storage locations (such as locally saved files).
- Testing various scenarios without relying on a service.
- Using SharedTree libraries for just the schema system and encode/decode support.
-
Compilation no longer fails when building with TypeScript's libCheck option (#22923) a1b4cdd45e
When compiling code using Fluid Framework with TypeScript's
libCheck
(meaning without skipLibCheck), two compile errors can be encountered:> tsc node_modules/@fluidframework/merge-tree/lib/client.d.ts:124:18 - error TS2368: Type parameter name cannot be 'undefined'. 124 walkSegments<undefined>(handler: ISegmentAction<undefined>, start?: number, end?: number, accum?: undefined, splitRange?: boolean): void; ~~~~~~~~~ node_modules/@fluidframework/tree/lib/util/utils.d.ts:5:29 - error TS7016: Could not find a declaration file for module '@ungap/structured-clone'. 'node_modules/@ungap/structured-clone/esm/index.js' implicitly has an 'any' type. Try `npm i --save-dev @types/ungap__structured-clone` if it exists or add a new declaration (.d.ts) file containing `declare module '@ungap/structured-clone';` 5 import structuredClone from "@ungap/structured-clone"; ~~~~~~~~~~~~~~~~~~~~~~~~~
The first error impacts projects using TypeScript 5.5 or greater and either of the
fluid-framework
or@fluidframework/merge-tree
packages. The second error impacts projects using thenoImplicitAny
tsconfig setting and thefluid-framework
or@fluidframework/tree
packages.Both errors have been fixed.
This should allow
libCheck
to be reenabled in any impacted projects. -
A
.schema
member has been added to the alpha enum schema APIs (#22874) 645b9ed695The return value from
@alpha
APIsenumFromStrings
andadaptEnum
now has a property namedschema
which can be used to include it in a parent schema. This replaces the use oftypedObjectValues
which has been removed.Use of these APIs now look like:
const schemaFactory = new SchemaFactory("com.myApp"); const Mode = enumFromStrings(schemaFactory, ["Fun", "Cool"]); type Mode = NodeFromSchema<(typeof Mode.schema)[number]>; class Parent extends schemaFactory.object("Parent", { mode: Mode.schema }) {}
Previously, the last two lines would have been:
type Mode = NodeFromSchema<(typeof Mode)[keyof typeof Mode]>; // This no longer works class Parent extends schemaFactory.object("Parent", { mode: typedObjectValues(Mode) }) {} // This no longer works
-
TreeNodeSchemaClass now specifies its TNode as TreeNode (#22938) b669a6efdb
TreeNodeSchemaClass
'sTNode
parameter was formerlyunknown
and has been improved to be the more specificTreeNode | TreeLeafValue
. This change further narrows this toTreeNode
.TreeNodeSchema
, which is more commonly used, still permitsTNode
ofTreeNode | TreeLeafValue
, so this change should have little impact on most code, but in some edge cases it can result in slightly more specific typing. -
Array and Map nodes can now be explicitly constructed with undefined or no argument (#22946) 176335ce88
The input parameter to the constructor and
create
methods of Array and Map nodes is now optional. When the optional parameter is omitted, an empty map or array will be created.class Schema extends schemaFactory.array("x", schemaFactory.number) {} // Existing support const _fromIterable: Schema = new Schema([]); // New const _fromUndefined: Schema = new Schema(undefined); const _fromNothing: Schema = new Schema();
class Schema extends schemaFactory.map("x", schemaFactory.number) {} // Existing support const _fromIterable: Schema = new Schema([]); const _fromObject: Schema = new Schema({}); // New const _fromUndefined: Schema = new Schema(undefined); const _fromNothing: Schema = new Schema();
const Schema = schemaFactory.array(schemaFactory.number); type Schema = NodeFromSchema<typeof Schema>; // Existing support const _fromIterable: Schema = Schema.create([]); // New const _fromUndefined: Schema = Schema.create(undefined); const _fromNothing: Schema = Schema.create();
const Schema = schemaFactory.map(schemaFactory.number); type Schema = NodeFromSchema<typeof Schema>; // Existing support const _fromIterable: Schema = Schema.create([]); const _fromObject: Schema = Schema.create({}); // New const _fromUndefined: Schema = Schema.create(undefined); const _fromNothing: Schema = Schema.create();
-
Typing has been improved when an exact TypeScript type for a schema is not provided (#22763) 05197d6d3f
The Tree APIs are designed to be used in a strongly typed way, with the full TypeScript type for the schema always being provided. Due to limitations of the TypeScript language, there was no practical way to prevent less descriptive types, like
TreeNodeSchema
orImplicitFieldSchema
, from being used where the type of a specific schema was intended. Code which does this will encounter several issues with tree APIs, and this change fixes some of those issues. This change mainly fixes thatNodeFromSchema<TreeNodeSchema>
used to returnunknown
and now returnsTreeNode | TreeLeafValue
.This change by itself seems mostly harmless, as it just improves the precision of the typing in this one edge case. Unfortunately, there are other typing bugs which complicate the situation, causing APIs for inserting data into the tree to also behave poorly when given non-specific types like
TreeNodeSchema
. These APIs include cases likeTreeView.initialize
.This incorrectly allowed some usage like taking a type-erased schema and initial tree pair, creating a view of type
TreeView<ImplicitFieldSchema>
, then initializing it. With the typing being partly fixed, some unsafe inputs are still allowed when trying to initialize such a view, but some are now prevented.This use-case of modifying trees in code not that is not strongly typed by the exact schema was not intended to be supported. Despite this, it did mostly work in some cases, and has some real use-cases (like tests looping over test data consisting of pairs of schema and initial trees). To help mitigate the impact of this change, some experimental
@alpha
APIs have been introduced to help address these previously unsupported but somewhat working use-cases.Before this change:
import { TinyliciousClient } from "@fluidframework/tinylicious-client"; import { SchemaFactory, SharedTree, TreeViewConfiguration, type TreeNodeSchema, } from "fluid-framework"; // Create a ITree instance const tinyliciousClient = new TinyliciousClient(); const { container } = await tinyliciousClient.createContainer({ initialObjects: {} }, "2"); const tree = await container.create(SharedTree); const schemaFactory = new SchemaFactory("demo"); // Bad: This loses the schema aware type information. `: TreeNodeSchema` should be omitted to preserve strong typing. const schema: TreeNodeSchema = schemaFactory.array(schemaFactory.number); const config = new TreeViewConfiguration({ schema }); // This view is typed as `TreeView<TreeNodeSchema>`, which does not work well since it's missing the actual schema type information. const view = tree.viewWith(config); // Root is typed as `unknown` allowing invalid assignment operations. view.root = "invalid"; view.root = {}; // Since all assignments are allowed, valid ones still work: view.root = [];
After this change:
// Root is now typed as `TreeNode | TreeLeafValue`, still allowing some invalid assignment operations. // In the future this should be prevented as well, since the type of the setter in this case should be `never`. view.root = "invalid"; // This no longer compiles: view.root = {}; // This also no longer compiles despite being valid at runtime: view.root = [];
For code that wants to continue using an unsafe API, which can result in runtime errors if the data does not follow the schema, a new alternative has been added to address this use-case. A special type
UnsafeUnknownSchema
can now be used to opt into allowing all valid trees to be provided. Note that this leaves ensuring the data is in schema up to the user. For now these adjusted APIs can be accessed by casting the view toTreeViewAlpha<UnsafeUnknownSchema>
. If stabilized, this option will be added toTreeView
directly.const viewAlpha = view as TreeViewAlpha<UnsafeUnknownSchema>; viewAlpha.initialize([]); viewAlpha.root = [];
Additionally, this seems to have negatively impacted co-recursive schema which declare a co-recursive array as the first schema in the co-recursive cycle. Like the TypeScript language our schema system is built on, we don't guarantee exactly which recursive type will compile, but will do our best to ensure useful recursive schema can be created easily. In this case a slight change may be required to some recursive schema to get them to compile again:
For example this schema used to compile:
class A extends sf.arrayRecursive("A", [() => B]) {} { type _check = ValidateRecursiveSchema<typeof A>; } // Used to work, but breaks in this update. class B extends sf.object("B", { x: A }) {}
But now you must use the recursive functions like
objectRecursive
for types which are co-recursive with an array in some cases. In our example, it can be fixed as follows:class A extends sf.arrayRecursive("A", [() => B]) {} { type _check = ValidateRecursiveSchema<typeof A>; } // Fixed corecursive type, using "Recursive" method variant to declare schema. class B extends sf.objectRecursive("B", { x: A }) {} { type _check = ValidateRecursiveSchema<typeof B>; }
Note: while the following pattern may still compile, we recommend using the previous pattern instead since the one below may break in the future.
class B extends sf.objectRecursive("B", { x: [() => A] }) {} { type _check = ValidateRecursiveSchema<typeof B>; } // Works, for now, but not recommended. class A extends sf.array("A", B) {}
-
The strictness of input tree types when inexact schemas are provided has been improved (#22874) 645b9ed695
Consider the following code where the type of the schema is not exactly specified:
const schemaFactory = new SchemaFactory("com.myApp"); class A extends schemaFactory.object("A", {}) {} class B extends schemaFactory.array("B", schemaFactory.number) {} // Gives imprecise type (typeof A | typeof B)[]. The desired precise type here is [typeof A, typeof B]. const schema = [A, B]; const config = new TreeViewConfiguration({ schema }); const view = sharedTree.viewWith(config); // Does not compile since setter for root is typed `never` due to imprecise schema. view.root = [];
The assignment of
view.root
is disallowed since a schema with type(typeof A | typeof B)[]
could be any of:const schema: (typeof A | typeof B)[] = [A];
const schema: (typeof A | typeof B)[] = [B];
const schema: (typeof A | typeof B)[] = [A, B];
The attempted assignment is not compatible with all of these (specifically it is incompatible with the first one) so performing this assignment could make the tree out of schema and is thus disallowed.
To avoid this ambiguity and capture the precise type of
[typeof A, typeof B]
, use one of the following patterns:const schema = [A, B] as const; const config = new TreeViewConfiguration({ schema });
const config = new TreeViewConfiguration({ schema: [A, B] });
To help update existing code which accidentally depended on this bug, an
@alpha
APIunsafeArrayToTuple
has been added. Many usages of this API will produce incorrectly typed outputs. However, when givenAllowedTypes
arrays which should not contain any unions, but that were accidentally flattened to a single union, it can fix them:// Gives imprecise type (typeof A | typeof B)[] const schemaBad = [A, B]; // Fixes the type to be [typeof A, typeof B] const schema = unsafeArrayToTuple(schemaBad); const config = new TreeViewConfiguration({ schema });
-
✨ New! Alpha API for providing SharedTree configuration options (#22701) 40d3648ddf
A new alpha
configuredSharedTree
had been added. This allows providing configuration options, primarily for debugging, testing and evaluation of upcoming features. The resulting configuredSharedTree
object can then be used in-place of the regularSharedTree
imported fromfluid-framework
.import { ForestType, TreeCompressionStrategy, configuredSharedTree, typeboxValidator, } from "@fluid-framework/alpha"; // Maximum debuggability and validation enabled: const SharedTree = configuredSharedTree({ forest: ForestType.Expensive, jsonValidator: typeboxValidator, treeEncodeType: TreeCompressionStrategy.Uncompressed, }); // Opts into the under development optimized tree storage planned to be the eventual default implementation: const SharedTree = configuredSharedTree({ forest: ForestType.Optimized, });
-
✨ New! Alpha API for snapshotting Schema (#22733) 920a65f66e
extractPersistedSchema
can now be used to extra a JSON-compatible representation of the subset of a schema that gets stored in documents. This can be used write tests which snapshot an applications schema. Such tests can be used to detect schema changes which could would impact document compatibility, and can be combined with the newcomparePersistedSchema
to measure what kind of compatibility impact the schema change has. -
Fix reading of
null
from unhydrated trees (#22748) 6a75bd0616Unhydrated trees containing object nodes with required fields set to
null
used to throw an error. This was a bug:null
is a valid value in tree's whose schema allow it, and this specific case now correctly returnsnull
values when appropriate without erroring. -
✨ New! Alpha SharedTree branching APIs (#22550) 8f4587c912
Several APIs have been added to allow for creating and coordinating "version-control"-style branches of the SharedTree. Use the
getBranch
entry point function to acquire a branch. For example:function makeEditOnBranch(mainView: TreeView<typeof MySchema>) { mainView.root.myData = 3; const mainBranch = getBranch(mainView); // This function accepts either a view of a SharedTree (acquired e.g. via `sharedTree.viewWith(...)`) or a `SharedTree` directly. const forkBranch = mainBranch.branch(); // This creates a new branch based on the existing branch. const forkView = forkBranch.viewWith(new TreeViewConfiguration({ schema: MySchema })); // Acquire a view of the forked branch in order to read or edit its tree. forkView.root.myData = 4; // Set the value on the fork branch to be 4. The main branch still has a value of 3. mainBranch.merge(forkBranch); // Merging the fork changes into the main branch causes the main branch to have a value of 4. // Note: The main branch (and therefore, also the `forkView`) is automatically disposed by the merge. // To prevent this, use `mainBranch.merge(forkBranch, false)`. }
Merging any number of commits into a target branch (via the
TreeBranch.merge
method) generates a revertible for each commit on the target branch. See #22644 for more information about revertible support in the branching APIs. -
SharedTree's
RestrictiveReadonlyRecord
is deprecated (#22479) 8be73d374dRestrictiveReadonlyRecord
was an attempt to implement a version of TypeScript's built-inRecord<TKey, TValue>
type that would prohibit (instead of leaving unrestricted like Record does) values under keys that do not extendTKey
.The implementation of
RestrictiveReadonlyRecord
failed to accomplish this except for the edge cases whereTKey
was exactlystring
or exactlysymbol
. Fixing this bug appears to be impossible within the current limitation of TypeScript, however this library does not require any case other thanTKey
being exactlystring
.To reduce the risk of users of the tree library using the problematic
RestrictiveReadonlyRecord
type, it has been deprecated and replaced with a more specific type that avoids the bug,RestrictiveStringRecord<TValue>
.To highlight that this new type is not intended for direct use by users of tree, and instead is just used as part of the typing of its public API,
RestrictiveStringRecord
has been tagged with@system
. See API Support Levels for more details. -
Fix
.create
on structurally named MapNode and ArrayNode schema (#22522) b3f91ae91cConstructing a structurally named MapNode or ArrayNode schema (using the overload of
SchemaFactory.map
orSchemaFactory.array
which does not take an explicit name), returned aTreeNodeSchema
instead of aTreeNodeSchemaNonClass
, which resulted in thecreate
static method not being exposed. This has been fixed, and can now be used as follows:const MyMap = schemaFactory.map(schemaFactory.number); type MyMap = NodeFromSchema<typeof MyMap>; const _fromMap: MyMap = MyMap.create(new MyMap()); const _fromIterable: MyMap = MyMap.create([]); const _fromObject: MyMap = MyMap.create({});
This change causes some types to reference
TreeNodeSchemaNonClass
which did not reference it before. WhileTreeNodeSchemaNonClass
is@system
(See Fluid Releases and API Support Levels for details) and thus not intended to be referred to by users of Fluid, this change caused the TypeScript compiler to generate references to it in more cases when compilingd.ts
files. Since the TypeScript compiler is unable to generate references toTreeNodeSchemaNonClass
with how it was nested ininternalTypes.js
, this change could break the build of packages exporting types referencing structurally named map and array schema. This has been mitigated by movingTreeNodeSchemaNonClass
out ofinternalTypes.js
: any code importingTreeNodeSchemaNonClass
(and thus disregarding the@system
restriction) can be fixed by importing it from the top level instead of theinternalTypes.js
-
Non-leaf field access has been optimized (#22717) 6a2b68103c
When reading non-leaf children which have been read previously, they are retrieved from cache faster. Several operations on subtrees under arrays have been optimized, including reading of non-leaf nodes for the first time. Overall this showed a roughly 5% speed up in a read heavy test application (the BubbleBench example) but gains are expected to vary a lot based on use-case.
-
✨ New! Alpha APIs for producing SharedTree schema from enums (#20035) 5f9bbe011a
adaptEnum
andenumFromStrings
have been added to@fluidframework/tree/alpha
andfluid-framework/alpha
. These unstable alpha APIs are relatively simple helpers on-top of public APIs (source: schemaCreationUtilities.ts): thus if these change or stable alternatives are needed, an application can replicate this functionality using these implementations as an example.
-
Add /alpha import path to @fluidframework/tree and fluid-framework packages (#22483) 12242cfdb5a
@fluidframework/tree
andfluid-framework
now have a/alpha
import path where their@alpha
APIs are exported. -
Export SharedTree beta APIs from fluid-framework/beta (#22469) c51f55c01a6
fluid-framework/beta
now contains the@beta
APIs from@fluidframework/tree/beta
. -
Implicitly constructed object nodes now only consider own properties during validation (#22453) 27faa56f5ae
When determining if some given data is compatible with a particular ObjectNode schema, both inherited and own properties were considered. However, when constructing the node from this data, only own properties were used. This allowed input which provided required values in inherited fields to pass validation. When the node was constructed, it would lack these fields, and end up out of schema. This has been fixed: both validation and node construction now only consider own properties.
This may cause some cases which previously exhibited data corruption to now throw a usage error reporting the data is incompatible. Such cases may need to copy data from the objects with inherited properties into new objects with own properties before constructing nodes from them.
-
A
@beta
version ofnodeChanged
which includes the list of properties has been added (#22229) aae34dd9fe1const factory = new SchemaFactory("example"); class Point2d extends factory.object("Point2d", { x: factory.number, y: factory.number, }) {} const point = new Point2d({ x: 0, y: 0 }); TreeBeta.on(point, "nodeChanged", (data) => { const changed: ReadonlySet<"x" | "y"> = data.changedProperties; if (changed.has("x")) { // ... } });
The payload of the
nodeChanged
event emitted by SharedTree'sTreeBeta
includes achangedProperties
property that indicates which properties of the node changed.For object nodes, the list of properties uses the property identifiers defined in the schema, and not the persisted identifiers (or "stored keys") that can be provided through
FieldProps
when defining a schema. See the documentation forFieldProps
for more details about the distinction between "property keys" and "stored keys".For map nodes, every key that was added, removed, or updated by a change to the tree is included in the list of properties.
For array nodes, the set of properties will always be undefined: there is currently no API to get details about changes to an array.
Object nodes revieve strongly types sets of changed keys, allowing compile time detection of incorrect keys:
TreeBeta.on(point, "nodeChanged", (data) => { // @ts-expect-error Strong typing for changed properties of object nodes detects incorrect keys: if (data.changedProperties.has("z")) { // ... } });
The existing stable "nodeChanged" event's callback now is given a parameter called
unstable
of typeunknown
which is used to indicate that additional data can be provided there. This could break existing code using "nodeChanged" in a particularly fragile way.function f(optional?: number) { // ... } Tree.on(point, "nodeChanged", f); // Bad
Code like this which is implicitly discarding an optional argument from the function used as the listener will be broken. It can be fixed by using an inline lambda expression:
function f(optional?: number) { // ... } Tree.on(point, "nodeChanged", () => f()); // Safe
-
The PropertyManager class and related functions and properties are deprecated (#22183) cbba69554f
The
PropertyManager
class, along with thepropertyManager
properties andaddProperties
functions on segments and intervals, are not intended for external use. These elements will be removed in a future release for the following reasons:- There are no scenarios where they need to be used directly.
- Using them directly will cause eventual consistency problems.
- Upcoming features will require modifications to these mechanisms.
-
Compile-time type narrowing based on a TreeNode's NodeKind (#22222) 4d3bc876ae
TreeNode
's schema-aware APIs implementWithType
, which now has aNodeKind
parameter that can be used to narrowTreeNode
s based onNodeKind
.Example:
function getKeys(node: TreeNode & WithType<string, NodeKind.Array>): number[]; function getKeys(node: TreeNode & WithType<string, NodeKind.Map | NodeKind.Object>): string[]; function getKeys(node: TreeNode): string[] | number[]; function getKeys(node: TreeNode): string[] | number[] { const schema = Tree.schema(node); switch (schema.kind) { case NodeKind.Array: { const arrayNode = node as TreeArrayNode; const keys: number[] = []; for (let index = 0; index < arrayNode.length; index++) { keys.push(index); } return keys; } case NodeKind.Map: return [...(node as TreeMapNode).keys()]; case NodeKind.Object: return Object.keys(node); default: throw new Error("Unsupported Kind"); } }
-
✨ New!
Record
-typed objects can now be used to construct MapNodes (#22042) 25deff344bYou can now construct MapNodes from
Record
typed objects, similar to how maps are expressed in JSON.Before this change, an
Iterable<string, Child>
was required, but now an object like{key1: Child1, key2: Child2}
is allowed.Full example using this new API:
class Schema extends schemaFactory.map("ExampleMap", schemaFactory.number) {} const fromRecord = new Schema({ x: 5 });
This new feature makes it possible for schemas to construct a tree entirely from JSON-compatible objects using their constructors, as long as they do not require unhydrated nodes to differentiate ambiguous unions, or IFluidHandles (which themselves are not JSON compatible).
Due to limitations of TypeScript and recursive types, recursive maps do not advertise support for this feature in their typing, but it works at runtime.
-
New SharedTree configuration option:
ITreeConfigurationOptions.preventAmbiguity
(#22048) 966906a034The new
ITreeConfigurationOptions.preventAmbiguity
flag can be set to true to enable checking of some additional rules when constructing theTreeViewConfiguration
.This example shows an ambiguous schema:
const schemaFactory = new SchemaFactory("com.example"); class Feet extends schemaFactory.object("Feet", { length: schemaFactory.number }) {} class Meters extends schemaFactory.object("Meters", { length: schemaFactory.number }) {} const config = new TreeViewConfiguration({ // This combination of schema can lead to ambiguous cases, and will error since preventAmbiguity is true. schema: [Feet, Meters], preventAmbiguity: true, }); const view = tree.viewWith(config); // This is invalid since it is ambiguous which type of node is being constructed. // The error thrown above when constructing the TreeViewConfiguration is because of this ambiguous case: view.initialize({ length: 5 });
See the documentation on
ITreeConfigurationOptions.preventAmbiguity
for a more complete example and more details. -
Tree.schema
now returnsTreeNodeSchema
(#22185) bfe8310a94The typing of
Tree.schema
has changed from:schema<T extends TreeNode | TreeLeafValue>(node: T): TreeNodeSchema<string, NodeKind, unknown, T>;
to:
schema(node: TreeNode | TreeLeafValue): TreeNodeSchema;
The runtime behavior is unaffected: any code which worked and still compiles is fine and does not need changes.
Tree.schema
was changed to mitigate two different issues:- It tried to give a more specific type based on the type of the passed in value.
When the type of the input is not known precisely (for example it is a union of node types like
Foo | Bar
, orTreeNode
or evenTreeNode | TreeLeafValue
), this was fine since schema are covariant over their node type. However when the input was more specific that the schema type, for example the type is simply0
, this would result in unsound typing, since the create function could actually return values that did not conform with that schema (for exampleschema.create(1)
for the number schema typed with0
would return1
with type0
). - The node type was provided to the incorrect type parameter of TreeNodeSchema.
The
TNode
parameter is the third one, not the fourth. The fourth isTBuild
which sets the input accepted to its create function or constructor. Thus this code accidentally leftTNode
unset (which is good due to the above issue), but invalidly setTBuild
.TBuild
is contravariant, so it has the opposite issue that settingTNode
would have: if your input is simply typed as something general likeTreeNode
, then the returned schema would claim to be able to construct an instance given anyTreeNode
. This is incorrect, and this typing has been removed.
Fortunately it should be rare for code to be impacted by this issue. Any code which manually specified a generic type parameter to
Tree.schema()
will break, as well as code which assigned its result to an overly specifically typed variable. Code which usedtypeof
on the returned schema could also break, though there are few use-cases for this so such code is not expected to exist. Currently it's very difficult to invoke the create function or constructor associated with aTreeNodeSchema
as doing so already requires narrowing toTreeNodeSchemaClass
orTreeNodeSchemaNonClass
. It is possible some such code exists which will need to have an explicit cast added because it happened to work with the more specific (but incorrect) constructor input type. - It tried to give a more specific type based on the type of the passed in value.
When the type of the input is not known precisely (for example it is a union of node types like
-
Recursive SharedTree schemas using MapNodes no longer produce invalid d.ts files (#22106) 554fc5a94e
Consider a recursive SharedTree schema like the following, which follows all our recommended best practices:
export class RecursiveMap extends schema.mapRecursive("RM", [() => RecursiveMap]) {} { type _check = ValidateRecursiveSchema<typeof RecursiveMap>; }
This schema would work when used from within its compilation unit, but would generate d.ts that fails to compile when exporting it:
declare const RecursiveMap_base: import("@fluidframework/tree").TreeNodeSchemaClass< "com.example.RM", import("@fluidframework/tree").NodeKind.Map, import("@fluidframework/tree").TreeMapNodeUnsafe<readonly [() => typeof RecursiveMap]> & import("@fluidframework/tree").WithType<"com.example.RM">, { [Symbol.iterator](): Iterator<[string, RecursiveMap], any, undefined>; }, false, readonly [() => typeof RecursiveMap] >; export declare class RecursiveMap extends RecursiveMap_base {}
This results in the compile error in TypeScript 5.4.5:
error TS2310: Type 'RecursiveMap' recursively references itself as a base type.
With this change, that error is fixed by modifying the
TreeMapNodeUnsafe
type it references to inline the definition ofReadonlyMap
instead of using the one from the TypeScript standard library. -
✨ New! When unambiguous, ArrayNodes can now be constructed from Maps and MapNodes from arrays (#22036) 25e74f9f3b
Since the types for ArrayNodes and MapNodes indicate they can be constructed from iterables, it should work, even if those iterables are themselves arrays or maps. To avoid this being a breaking change, a priority system was introduced. ArrayNodes will only be implicitly constructable from JavaScript Map objects in contexts where no MapNodes are allowed. Similarly MapNodes will only be implicitly constructable from JavaScript Array objects in contexts where no ArrayNodes are allowed.
In practice, the main case in which this is likely to matter is when implicitly constructing a map node. If you provide an array of key value pairs, this now works instead of erroring, as long as no ArrayNode is valid at that location in the tree.
class MyMapNode extends schemaFactory.map("x", schemaFactory.number) {} class Root extends schemaFactory.object("root", { data: MyMapNode }) {} // This now works (before it compiled, but error at runtime): const fromArray = new Root({ data: [["x", 5]] });
Prior versions used to have to do:
new Root({ data: new MyMapNode([["x", 5]]) });
or:
new Root({ data: new Map([["x", 5]]) });
Both of these options still work: strictly more cases are allowed with this change.
-
Implicit TreeNode construction improvements (#21995) 977f96c1a0
ArrayNodes and MapNodes could always be explicitly constructed (using
new
) from iterables. The types also allowed using of iterables to implicitly construct array nodes and map nodes, but this did not work at runtime. This has been fixed for all cases except implicitly constructing an ArrayNode form anIterable
that is actually aMap
, and implicitly constructing a MapNode from anIterable
that is actually anArray
. These cases may be fixed in the future, but require additional work to ensure unions of array nodes and map nodes work correctly.Additionally MapNodes can now be constructed from
Iterator<readonly [string, content]>
where previously the inner arrays had to be mutable. -
Enforce use of TreeViewConfiguration's constructor (#22055) e8955579f6
TreeViewConfiguration
is@sealed
, meaning creating custom implementations of it such as assigning object literals to aTreeViewConfiguration
or sub-classing it are not supported. This reserved the ability for the Fluid Framework to add members to this class over time, informing users that they must use it in such a way where such changes are non-breaking. However, there was no compiler-based enforcement of this expectation. It was only indicated via documentation and an implicit assumption that when an API takes in a typed defined as a class, that an instance of that class must be used rather than an arbitrary object of a similar shape.With this change, the TypeScript compiler will now inform users when they invalidly provide an object literal as a
TreeViewConfiguration
.More specifically this causes code like this to produce a compile error:
// Don't do this! const view = tree.viewWith({ schema: TestNode, enableSchemaValidation: false });
The above was never intended to work, and is not a supported use of the
viewWith
since it requires aTreeViewConfiguration
which is sealed. Any code using the above pattern will break in Fluid Framework 2.2 and above. Such code will need to be updated to the pattern shown below. Any code broken by this change is technically unsupported and only worked due to a gap in the type checking. This is not considered a breaking change. The correct way to get aTreeViewConfiguration
is by using its constructor:// This pattern correctly initializes default values and validates input. const view = tree.viewWith(new TreeViewConfiguration({ schema: TestNode }));
Skipping the constructor causes the following problems:
TreeViewConfiguration
does validation in its constructor, so skipping it also skips the validation which leads to much less friendly error messages for invalid schema.- Skipping the constructor also discards any default values for options like
enableSchemaValidation
. This means that code written in that style would break if more options were added. Since such changes are planned, it is not practical to support this pattern.
-
New
isFluidHandle
type guard to check if an object is anIFluidHandle
(#22029) 7827d1040aThe
isFluidHandle
type guard function is now exported and can be used to detect which objects areIFluidHandle
s. SinceIFluidHandle
often needs special handling (for example when serializing since it's not JSON compatible), having a dedicated detection function for it is useful. Doing this detection was possible previously using thetree
package's schema system viaTree.is(value, new SchemaFactory("").handle)
, but can now be done with justisFluidHandle(value)
. -
Add a function
isRepoSuperset
to determine if changes to a document schema are backward-compatible (#22045) f6fdc95bb3Note: These changes are not customer-facing and make progress toward future plans in Tree's schema evolution space.
-
Detect arrayNode iterator invalidation (#21760) 6fd320c385
When
arrayNode
s are edited concurrently during iteration, an error will be thrown. -
Some SharedDirectory/SharedMap-related APIs have been sealed (#21836) b1d0427eab
Note that this is a documentation only change. There is no runtime or type-level impact.
Some top-level APIs within
@fluidframework/map
andfluid-framework
have been updated to reflect their sealed/readonly nature. That is, they are not to be implemented externally to Fluid Framework and not changed. This was already the case, but the documentation was not clear.Updated APIs:
- IDirectory sealed
- IDirectoryEvents sealed
- IDirectoryValueChanged sealed and path property is readonly
- ISharedDirectory sealed
- ISharedDirectoryEvents sealed
- IValueChanged sealed
-
tree: Improved performance for accessing identifiers in shortId API (#21944) 6b4cf26d9c
Users should see improved performance when calling the
Tree.shortId
API. Identifier field keys are now cached in the schema for faster access. -
✨ New! Debug visualizers for TreeNodes in NodeJS and browsers (#21895) 0d197fefec
TreeNodes now have custom debug visualizers to improve the debug experience in NodeJS and in browsers. Note that custom formatters must be enabled in the browser developer tools for that visualizer to be used.
-
Using "delete" on tree fields now throws an error instead of not working correctly (#21609) 416849b1fd
TypeScript allows
delete
on object node optional fields if theexactOptionalPropertyTypes
tsconfig setting is not enabled. This does not work correctly at runtime and now produces an informative error. -
Improved error reporting (#21940) 3b8a366dd1
Several cases of invalid usage patterns for tree APIs have gained improved error reporting, as well as improved documentation on the APIs detailing what usage is supported. These improvements include:
- Unsupported usages of schema classes: using more than one schema class derived from a single SchemaFactory generated base class. This used to hit internal asserts, but now has a descriptive user-facing UsageError. Most of this work was done in 9fb3dcf.
- Improved detection of when prior exception may have left SharedTree in an invalid state. These cases now report a UsageError including a reference to the prior exception. This was mainly done in 9fb3dcf and b77d530.
-
fluid-framework: Type Erase ISharedObjectKind (#21081) 78f228e370
A new type,
SharedObjectKind
is added as a type erased version ofISharedObjectKind
andDataObjectClass
.This type fills the role of both
ISharedObjectKind
andDataObjectClass
in the@public
"declarative API" exposed in thefluid-framework
package.This allows several types referenced by
ISharedObjectKind
to be made@alpha
as they should only need to be used by legacy code and users of the unstable/alpha/legacy "encapsulated API".Access to these now less public types should not be required for users of the
@public
"declarative API" exposed in thefluid-framework
package, but can still be accessed for those who need them under the/legacy
import paths. The full list of such types is:SharedTree
as exported from@fluidframwork/tree
: It is still exported as@public
fromfluid-framework
asSharedObjectKind
.ISharedObjectKind
: See newSharedObjectKind
type for use in@public
APIs.ISharedObject
IChannel
IChannelAttributes
IChannelFactory
IExperimentalIncrementalSummaryContext
IGarbageCollectionData
ISummaryStats
ISummaryTreeWithStats
ITelemetryContext
IDeltaManagerErased
IFluidDataStoreRuntimeEvents
IFluidHandleContext
IProvideFluidHandleContext
Removed APIs:
DataObjectClass
: Usages replaced withSharedObjectKind
.LoadableObjectClass
: Replaced withSharedObjectKind
.LoadableObjectClassRecord
: Replaced withRecord<string, SharedObjectKind>
.
-
tree: Added support for optional schema validation on newly inserted content in SharedTree (#21011) b14e9fa607
When defining how to view a SharedTree, an application can now specify that new content inserted into the tree should be subject to schema validation at the time it is inserted, so if it's not valid according to the stored schema in the tree an error is thrown immediately.
This can be accomplished by passing an
ITreeConfigurationOptions
argument withenableSchemaValidation
set totrue
when creating aTreeConfiguration
to use with the SharedTree.Since this feature requires additional compute when inserting new content into the tree, it is not enabled by default.
-
Update to TypeScript 5.4 (#21214) 0e6256c722
Update package implementations to use TypeScript 5.4.5.
-
fluid-framework: Remove some types from
@public
that are not needed (#21326) b629cb80b0Mark the following APIs
@alpha
instead of@public
:- IBranchOrigin
- ISequencedDocumentMessage
- ISignalMessage
- ISignalMessageBase
- ITrace
-
tree: A new tree status has been added for SharedTree nodes. (#21270) 8760e321b0
TreeStatus.Created
indicates that a SharedTree node has been constructed but not yet inserted into the tree. Constraints passed to therunTransaction
API are now marked asreadonly
. -
fluid-framework: Remove several types from
@public
scope (#21142) 983e9f09f7The following types have been moved from
@public
to@alpha
:IFluidSerializer
ISharedObjectEvents
IChannelServices
IChannelStorageService
IDeltaConnection
IDeltaHandler
These should not be needed by users of the declarative API, which is what
@public
is targeting. -
sequence: Stop ISharedString extending SharedObject (#21067) 47465f4b12
ISharedString no longer extends SharedSegmentSequence and instead extends the new ISharedSegmentSequence, which may be missing some APIs.
Attempt to migrate off the missing APIs, but if that is not practical, request they be added to ISharedSegmentSequence and cast to SharedSegmentSequence as a workaround temporally.
-
Update to ES 2022 (#21292) 68921502f7
Update tsconfig to target ES 2022.
-
tree: Move several types into InternalTypes (#21482) 64d49dd362
The stable public API surface for Tree has been reduced. Several types have been moved into InternalTypes, indicating that they are not fully stable nor intended to be referenced by users of Tree.
- NodeBuilderData
- FieldHasDefault
- TreeNodeSchemaNonClass
- TreeArrayNodeBase
- ScopedSchemaName
- DefaultProvider
- typeNameSymbol
- InsertableObjectFromSchemaRecord
- ObjectFromSchemaRecord
- FieldHasDefaultUnsafe
- ObjectFromSchemaRecordUnsafe
- TreeObjectNodeUnsafe
- TreeFieldFromImplicitFieldUnsafe
- TreeNodeFromImplicitAllowedTypesUnsafe
- InsertableTreeNodeFromImplicitAllowedTypesUnsafe
- TreeArrayNodeUnsafe
- TreeMapNodeUnsafe
- InsertableObjectFromSchemaRecordUnsafe
- InsertableTreeFieldFromImplicitFieldUnsafe
- InsertableTypedNodeUnsafe
- NodeBuilderDataUnsafe
- NodeFromSchemaUnsafe
- FlexList
- TreeApi
Additionally a few more types which could not be moved due to technically limitations have been documented that they should be treated similarly.
- TreeNodeApi
- TreeNodeSchemaCore
- All *Unsafe type (use for construction of recursive schema).
- WithType
- AllowedTypes
- FieldSchemaUnsafe
Also to reduce confusion
type
was renamed totypeNameSymbol
, and is now only type exported.Tree.is
should be used to get type information fromTreeNodes
instead. -
tree: object node fields with statically known default values are now optional (#21193) 21eac41660
Makes object node fields with statically known default values (i.e.,
optional
andidentifier
fields) optional when creating trees, where they were previously required.Example:
class Foo extends schemaFactory.object("Foo", { name: schemaFactory.string, id: schemaFactory.identifier, nickname: schemaFactory.optional(schemaFactory.string), }) {} // Before const foo = new Foo({ name: "Bar", id: undefined, // Had to explicitly specify `undefined` to opt into default behavior nickname: undefined, // Had to explicitly specify `undefined` for optional field }); // After const foo = new Foo({ name: "Bar", // Can omit `id` and `nickname` fields, as both have statically known defaults! });
-
tree: Breaking change:
TreeStatus.Created
is nowTreeStatus.New
(#21278) 5a26346a14TreeStatus.Created
has been renamed toTreeStatus.New
. -
core-interfaces, tree: Unify
IDisposable
interfaces (#21184) cfcb827851Public APIs in
@fluidframework/tree
now useIDisposable
from@fluidframework/core-interfaces
replacingdisposeSymbol
with "dispose".IDisposable
in@fluidframework/core-interfaces
is now@sealed
indicating that third parties should not implement it to reserve the ability for Fluid Framework to extend it to includeSymbol.dispose
as a future non-breaking change. -
fluid-framework: Cleanup
fluid-framework
legacy exports (#21153) efee21c296Cleanup
fluid-framework
legacy exports to remove no longer required types.
-
SharedString now uses ISharedObjectKind and does not export the factory 96872186d0
Most users of
SharedString
should be unaffected as long as they stick to the factory patterns supported by ISharedObjectKind. If the actual class type is needed it can be found asSharedStringClass
. -
Deprecated members of IFluidHandle are split off into new IFluidHandleInternal interface 96872186d0
Split IFluidHandle into two interfaces,
IFluidHandle
andIFluidHandleInternal
. Code depending on the previously deprecated members of IFluidHandle can access them by usingtoFluidHandleInternal
from@fluidframework/runtime-utils/legacy
.External implementation of the
IFluidHandle
interface are not supported: this change makes the typing better convey this using theErasedType
pattern. Any existing and previously working, and now broken, external implementations ofIFluidHandle
should still work at runtime, but will need some unsafe type casts to compile. Such handle implementation may break in the future and thus should be replaced with use of handles produced by the Fluid Framework client packages. -
Minor API fixes for "@fluidframework/tree" package. 96872186d0
Changes constructor for
FieldSchema
from public to private. Users should callmakeFieldSchema
to create instance ofFieldSchema
. -
Make several driver types no longer public b7ad7d0b55
Move the following types from
@public
to@alpha
:- ITokenClaims
- IDocumentMessage
- IClientConfiguration
- IAnyDriverError
- IDriverErrorBase
- DriverErrorTypes
DriverErrorTypes
is no longer exported from thefluid-framework
package. -
Rename
AzureMember.userName
toAzureMember.name
andIMember.userId
toIMember.id
96872186d0- Renamed
AzureMember.userName
toAzureMember.name
to establish uniform naming across odsp-client and azure-client. - Renamed
IMember.userId
toIMember.id
to align with the properties received from AFR.
- Renamed
-
fluid-framework: DDS classes are no longer publicly exported 97d68aa06b
SharedDirectory now only exports its factory and the interface type. The actual concrete classes which leak implementation details are no longer exported. Users of the
SharedDirectory
type should useISharedDirectory
.Most of other internal crufts are also hided within the API surface, such as the encoded format, ILocalValue, ICreateInfo, local op metadata types, etc.
-
Packages now use package.json "exports" and require modern module resolution 97d68aa06b
Fluid Framework packages have been updated to use the package.json "exports" field to define explicit entry points for both TypeScript types and implementation code.
This means that using Fluid Framework packages require the following TypeScript settings in tsconfig.json:
"moduleResolution": "Node16"
with"module": "Node16"
"moduleResolution": "Bundler"
with"module": "ESNext"
We recommend using Node16/Node16 unless absolutely necessary. That will produce transpiled JavaScript that is suitable for use with modern versions of Node.js and Bundlers. See the TypeScript documentation for more information regarding the module and moduleResolution options.
Node10 moduleResolution is not supported; it does not support Fluid Framework's API structuring pattern that is used to distinguish stable APIs from those that are in development.
-
tree: Allow root editing and make TreeView parameterized over schema. 97d68aa06b
TreeView now is parameterized over the field schema instead of the root field type. This was needed to infer the correct input type when reassigning the root. Code providing an explicit type to TreeView, like
TreeView<Foo>
can usually be updated by replacing that withTreeView<typeof Foo>
. -
fluid-framework: Replace SharedObjectClass with new ISharedObjectKind type. 97d68aa06b
The static objects used as SharedObjectClass now explicitly implement the new ISharedObjectKind type. SharedObjectClass has been removed as ISharedObjectKind now fills that role. LoadableObjectCtor has been inlined as it only had one use: an external user of it can replace it with
(new (...args: any[]) => T)
. -
fluid-framework: Moved SharedMap to 'fluid-framework/legacy' 97d68aa06b
Please use SharedTree for new containers. SharedMap is supported for loading preexisting Fluid Framework 1.x containers only.
Fluid Framework 1.x users migrating to Fluid Framework 2.x will need to import SharedMap from the './legacy' import path.
import { SharedMap } from "fluid-framework/legacy";
-
fluid-framework: Make some interface members readonly 97d68aa06b
Remove unneeded mutability from some interface members.
-
fluid-framework: EventEmitterWithErrorHandling is no longer publicly exported (#19717) ae1d0be26d
EventEmitterWithErrorHandling is intended for authoring DDSes, and thus is only intended for use within the Fluid Framework client packages. It is no longer publicly exported: any users should fine their own solution or be upstreamed. EventEmitterWithErrorHandling is available for now as
@alpha
to make this migration less disrupting for any existing users. -
fluid-framework: SharedObject classes are no longer exported as public (#19717) ae1d0be26d
SharedObject
andSharedObjectCore
are intended for authoring DDSes, and thus are only intended for use within the Fluid Framework client packages. They is no longer publicly exported: any users should fine their own solution or be upstreamed.SharedObject
andSharedObjectCore
are available for now as@alpha
to make this migration less disrupting for any existing users. -
API tightening (#20012) 049de899dd
The Fluid Framework API has been clarified with tags applied to package exports. As we are working toward a clear, safe, and stable API surface, some build settings and imports may need to be adjusted.
Now: Most packages are specifying "exports" - import specifierss like
@fluidframework/foo/lib/internals
will become build errors. The fix is to use only public APIs from @fluidframework/foo.Coming soon: Build resolutions (
moduleResolution
in tsconfig compilerOptions) will need to be resolved with Node16, NodeNext, or a bundler that supports resolution of named import/export paths. Internally, some FF packages will use@fluidframework/foo/internal
import paths that allow packages to talk to each other using non-public APIs.Final stage: APIs that are not tagged @public will be removed from @fluidframework/foo imports.
-
Deprecated error-related enums have been removed (#19067) 59793302e5
Error-related enums
ContainerErrorType
,DriverErrorType
,OdspErrorType
andRouterliciousErrorType
were previously deprecated and are now removed. There are replacement object-based enumerations ofContainerErrorTypes
,DriverErrorTypes
,OdspErrorTypes
andRouterliciousErrorTypes
. Refer to the release notes of Fluid Framework version 2.0.0-internal.7.0.0 for details on the replacements. -
map, tree: DDS classes are no longer publicly exported (#19717) ae1d0be26d
SharedMap and SharedTree now only export their factories and the interface types. The actual concrete classes which leak implementation details are no longer exported. Users of the
SharedMap
type should useISharedMap
. Users of theSharedTree
type should useISharedTree
. -
tree: Minor API fixes for "@fluidframework/tree" package. (#19057) 3e0f218832
Rename
IterableTreeListContent
toIterableTreeArrayContent
, inlineTreeMapNodeBase
intoTreeMapNode
, renameTreeArrayNode.spread
toTreeArrayNode.spread
and removecreate
which was not supposed to be public (useTreeArrayNode.spread
instead). -
fluid-framework: ContainerSchema is now readonly (#19717) ae1d0be26d
The
ContainerSchema
type is intended for defining input to these packages. This should make the APIs more tolerant and thus be non-breaking, however its possible for some users ofContainerSchema
to use it in ways where this could be a breaking change: any such users should remove their mutations and/or use a different type.
Dependency updates only.
-
azure-client: Removed deprecated FluidStatic classes 9a451d4946
Several FluidStatic classes were unnecessarily exposed and were deprecated in an earlier release. They have been replaced with creation functions. This helps us keep implementations decoupled from usage which is easier to maintain and extend. It has very minimal impact on the public surface area of downstream packages. The removed classes are as follows:
AzureAudience
(useIAzureAudience
instead)TinyliciousAudience
(useITinyliciousAudience
instead)DOProviderContainerRuntimeFactory
FluidContainer
ServiceAudience
-
azure-client: Deprecated FluidStatic Classes (#18402) 589ec39de5
Several FluidStatic classes were unnecessarily exposed. They have been replaced with creation functions. This helps us keep implementations decoupled from usage which is easier to maintain and extend. It has very minimal impact on the public surface area of downstream packages. The deprecated classes are as follows:
AzureAudience
(useIAzureAudience
instead)TinyliciousAudience
(useITinyliciousAudience
instead)DOProviderContainerRuntimeFactory
FluidContainer
ServiceAudience
Dependency updates only.
Dependency updates only.
Dependency updates only.
-
Minimum TypeScript version now 5.1.6 871b3493dd
The minimum supported TypeScript version for Fluid 2.0 clients is now 5.1.6.
Dependency updates only.
Dependency updates only.
Dependency updates only.
Dependency updates only.
-
IntervalConflictResolver removed 8abce8cdb4
IntervalConflictResolver has been removed. Any lingering usages in application code can be removed as well. This change also marks APIs deprecated in #14318 as internal.
-
RootDataObject and RootDataObjectProps no longer exported from fluid-static or fluid-framework packages 8abce8cdb4
RootDataObject and RootDataObjectProps are internal implementations and not intended for direct use. Instead use IRootDataObject to refer to the root data object.
-
Upgraded typescript transpilation target to ES2020 8abce8cdb4
Upgraded typescript transpilation target to ES2020. This is done in order to decrease the bundle sizes of Fluid Framework packages. This has provided size improvements across the board for ex. Loader, Driver, Runtime etc. Reduced bundle sizes helps to load lesser code in apps and hence also helps to improve the perf.If any app wants to target any older versions of browsers with which this target version is not compatible, then they can use packages like babel to transpile to a older target.
Dependency updates only.
Dependency updates only.
Dependency updates only.
Dependency updates only.
Dependency updates only.
Dependency updates only.
Dependency updates only.