Contents
✨ New Features
Add new @Alpha ServiceClient API for creating and loading Fluid containers (#27693)
This introduces an experimental (@alpha), service-agnostic API for working with Fluid containers whose root is an arbitrary data store, along with an in-memory implementation for testing.
The new surface is made up of:
ServiceClient(@fluidframework/driver-definitions): the entry point for creating and loading containers. Along with it come the supporting container types (FluidContainer,FluidContainerWithService,FluidContainerAttached), the data store model (DataStoreKind,DataStoreKey,DataStoreRegistry,DataStoreCreator), and the generic registry primitives (Registry,RegistryKey,lookupInRegistry,createBasicRegistryKey).defineDataStoreandsharedObjectRegistryFromIterable(@fluidframework/shared-object-base): build aDataStoreKindfrom a root shared object and a registry of shared object kinds.defineTreeDataStoreandinstantiateTreeFirstTime(@fluidframework/tree): a SharedTree-specific convenience wrapper that produces aDataStoreKindbacked by aTreeView.startEphemeralService(@fluidframework/local-driver): starts an in-memoryEphemeralServicefor tests. The service owns the lifetime of the in-memory documents and resources, and producesServiceClients connected to it (viaEphemeralService.newClientorEphemeralService.defaultClient). The helperscleanupEphemeralServiceandgetDefaultEphemeralServicemanage an optional default service instance.
Apart from the @fluidframework/local-driver helpers (which come from @fluidframework/local-driver/alpha), these APIs are also re-exported from fluid-framework. None reference any @legacy types.
Example:
import { startEphemeralService } from "@fluidframework/local-driver/alpha";
import {
ServiceClient,
defineTreeDataStore,
TreeViewConfiguration,
SchemaFactory,
} from "fluid-framework/alpha";
import { strict as assert } from "node:assert";
// Start an ephemeral in-memory service and get a ServiceClient connected to it.
const service = startEphemeralService();
const client: ServiceClient = service.defaultClient;
// Define a DataStoreKind which uses a SharedTree.
// In this case the schema is for a single number with an initializer that starts the it at 1.
// This schema is captures in the type allowing for strongly typed access to the data in the tree,
// where the type matches the schema based runtime enforcement of the schema.
const numberStore = defineTreeDataStore({
type: "my-app-root",
config: new TreeViewConfiguration({ schema: SchemaFactory.number }),
initializer: () => 1,
});
// Create a container in the service with the above DataStoreKind.
// Ideally this creation would use a service independent API, and only the attach call would be service dependent,
// but that is not supported yet.
const detachedContainer1 = await client.createContainer(numberStore);
const container1 = await detachedContainer1.attach();
// We now have easy and type safe access to the data in the tree, which will be synced over the service.
assert.equal(container1.data.root, 1);
// A second client can load the same container from the service, and will see the same data.
const container2 = await client.loadContainer(container1.id, numberStore);
assert.equal(container2.data.root, 1);
// Both clients can modify the data, and the changes will be synced over the service.
container2.data.root = 2;
// Since we are using an ephemeral service, we can await the synchronization using service.synchronize.
await service.synchronize();
// And now the changes are visible for all clients.
assert.equal(container1.data.root, 2);
assert.equal(container2.data.root, 2);Note that this example does a couple of things which are difficult to do with the other API surfaces:
- It creates a container, then loads a second copy of it, allowing for collaboration. There is currently no non-legacy API surface which allows this without spawning a server process. This is also cleaner than the exacting legacy API options, and can replace the test specific APIs for this as well.
- It creates a container which has a SharedTree at the root, and nothing else. This avoids depending on legacy DDS implementations, which is great for long-term document support and bundle size. This is currently impossible using
fluid-static, which forces a special root data store. It is also impossible if usingaqueduct, which forces a root directory in every data store. It can be done using the low level legacy APIs directly, but this new API for it is much simpler. - There is a common interface all services implement (
ServiceClient), making the container creation part of the code work for any service implementation.
Change details
Commit: ee47192
Affected packages:
- fluid-framework
- @fluidframework/driver-definitions
- @fluidframework/shared-object-base
- @fluidframework/tree
- @fluidframework/runtime-utils
- @fluidframework/local-driver
Add FluidReadonlyArray type independent of TypeScript lib (#27747)
FluidReadonlyArray<T> provides an equivalent of the built-in ReadonlyArray type that is independent of TypeScript lib, following the same pattern as FluidReadonlyMap and FluidMap. The interface includes stable methods through ES2023 (at(), findLast(), findLastIndex()) but excludes newer copy-on-write methods (toReversed(), toSorted(), toSpliced(), with()) that Fluid Framework implementations don't yet support. This ensures these types remain safe to implement without lib changes breaking them.
Change details
Commit: 040d35b
Affected packages:
- @fluidframework/core-interfaces
- fluid-framework
Promote Fluid container type interfaces to public (#27746)
FluidIterable, FluidIterableIterator, FluidReadonlyMap, FluidMap, and FluidReadonlyArray are promoted from @beta to @public. These sealed interfaces provide equivalents of the built-in Iterable, IterableIterator, ReadonlyMap, Map, and ReadonlyArray types that are independent of TypeScript lib. They can now be used in public API surfaces.
Change details
Commit: 33e014a
Affected packages:
- @fluidframework/core-interfaces
- fluid-framework
🌳 SharedTree DDS Changes
Add clear method to TreeMapNodeAlpha (#27765)
TreeMapNodeAlpha now has a clear method, further aligning it with JavaScript's built-in Map API. It removes all elements from the map.
The merge semantics of clear are loosely specified: either of the following may occur:
clearmay remove all elements that were in the map when the edit was authored, even if some of those elements have since been moved elsewhere in the tree (in which case they are removed from their new location).clearmay remove all elements that are in the map when the edit is sequenced, even if some of those elements were not yet in the map when the edit was authored.
This method is available on TreeMapNodeAlpha, which can be obtained from an existing TreeMapNode via asAlpha, or by declaring the schema with SchemaFactoryAlpha's mapAlpha.
const schemaFactory = new SchemaFactoryAlpha("example");
class Inventory extends schemaFactory.mapAlpha(
"Inventory",
schemaFactory.number,
) {}
const inventory = new Inventory(
new Map([
["apples", 5],
["pears", 3],
]),
);
inventory.size; // 2
inventory.clear();
inventory.size; // 0Change details
Commit: 30c889b
Affected packages:
- fluid-framework
- @fluidframework/tree
⚠️ Deprecations
Deprecate mixinSummaryHandler (#27769)
The mixinSummaryHandler function from @fluidframework/datastore is now deprecated and will be removed in a future release. There is no replacement for it.
Change details
Commit: a7d64c6
Affected packages:
- @fluidframework/datastore
🛠️ Start Building Today!
Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!