Introduce Unified ServiceClient API for Fluid Client#27693
Introduce Unified ServiceClient API for Fluid Client#27693CraigMacomber wants to merge 33 commits into
Conversation
Introduces the foundational alpha public API for the new container management layer: - ServiceClient / ServiceContainerBase: top-level interface for creating and loading FluidContainers from any service - FluidContainer / FluidContainerWithService / FluidContainerAttached: typed container handles with attach lifecycle - DataStoreKind / DataStoreKey / DataStoreRegistry: type-safe wrappers around data store factories and their registries - Registry / RegistryKey / basicKey / registryLookup: lightweight registry plumbing used by all ServiceClient implementations - getContainerAudience: free function to obtain IAudience from an attached container without coupling to service-specific types These types are exported at the alpha stability level from @fluidframework/runtime-definitions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ct-base Adds the alpha factory layer that sits between shared objects (DDSs) and the ServiceClient data store abstraction: - dataStoreKind<T>(): wraps DataStoreOptions into a DataStoreKind with a fully wired IFluidDataStoreFactory under the hood - DataStoreOptions<TRoot, TOutput>: configuration with type/registry/ instantiateFirstTime/view callbacks - DataStoreContext: object passed to instantiateFirstTime/view, implementing SharedObjectCreator for creating root and additional shared objects - sharedObjectRegistryFromIterable(): convenience builder for ISharedObjectRegistry - SharedObjectKey/SharedObjectKindAlpha: alpha variants of shared object type markers, allowing alpha DDS kinds to be used without leaking into the beta/public API surface - StubSharedObject: minimal ISharedObject for testing that does not require a full FluidDataStoreRuntime Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also exports the new ServiceClient / DataStore / SharedObject API surface through the fluid-framework aggregator package. tree's treeDataStoreKind is rolled up via fluid-framework's `export * from "@fluidframework/tree/alpha"`, so the types referenced by its signature must be re-exported from fluid-framework for the aggregator's API Extractor rollup to resolve them. These re-exports depend only on driver-definitions and shared-object-base, so they belong with (and are required by) this commit.
Introduces serviceClientUtils.ts, shared internal infrastructure consumed
by all concrete ServiceClient implementations:
- makeServiceClientImpl(): generic factory that produces a ServiceClient
from a service-specific ServiceContainerBase class and options
- makeCodeLoader(): builds the ICodeDetailsLoader that wires a
ContainerRuntime to a service's data store registry
- convertRegistry() / normalizeRegistry(): adapt DataStoreRegistry to the
IFluidDataStoreRegistry interface expected by ContainerRuntime
- rootDataStoreId: well-known alias for the root data store ("root")
- defaultMinVersionForCollab: default minimum collaboration version ("2.0.0")
Exports everything from @fluidframework/runtime-utils/internal so service
client packages can import without depending on each other.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds createEphemeralServiceClient() to @fluidframework/local-driver, an implementation of ServiceClient backed entirely by an in-memory LocalDeltaConnectionServer. Intended for unit and integration tests that need a real collaborative container lifecycle without any network: - createEphemeralServiceClient(): factory producing a ServiceClient whose containers run in a shared in-memory server scoped to the process - closeEphemeralContainers(): cleanup helper that closes all containers and clears timer handles, preventing mocha from hanging after tests - synchronizeLocalService(): test utility that flushes pending ops across all connected containers so tests can assert stable collaborative state Also adds ephemeralServiceClient.spec.ts covering attach, close, shared server, and multi-attach uniqueness, and treeDataStore.spec.ts which tests the treeDataStoreKind() integration with the ephemeral service (detach/attach lifecycle, collaboration, schema evolution, lazy loading, and non-root data stores). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (2966 lines, 39 files), I've queued these reviewers:
How this works
|
343d45a to
035e4ad
Compare
| /** | ||
| * Type guard for narrowing unions which contain DataStoreKind<T> to DataStoreKind<T>. | ||
| */ | ||
| public static guard<T>( | ||
| value: T, | ||
| ): value is DataStoreKindImplementation<T extends DataStoreKind<infer T2> ? T2 : never> & T { | ||
| return value instanceof DataStoreKindImplementation; | ||
| } | ||
|
|
||
| /** | ||
| * Type guard for narrowing unions which contain DataStoreKind<T> to DataStoreKind<T>. | ||
| */ | ||
| public static narrowGeneric<T>( | ||
| value: T, | ||
| ): asserts value is DataStoreKindImplementation< | ||
| T extends DataStoreKind<infer T2> ? T2 : never | ||
| > & | ||
| T { | ||
| if (!DataStoreKindImplementation.guard(value)) { | ||
| throw new Error("Invalid DataStoreKindImplementation"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Nit: any reason these can't be free functions instead?
There was a problem hiding this comment.
They could be. I think it makes a for a much nicer API for them to be statics. This puts them right next to (API wise) the inherited static like narrow upCast and [hasInstance], and avoids cluttering the top level namespace with a bunch of utilities for working with this specific class. As they compile town to almost nothing, I don't think tree shaking should be a concern (which would be the main reason to make statics on an internal class into free functions)
| export const minimize: TransactionPostProcessor; | ||
|
|
||
| // @alpha @input | ||
| export type MinimumVersionForCollaboration = `2.${bigint}.0`; |
There was a problem hiding this comment.
@CraigMacomber it looks like we're migrating away from the "minimum version for collaboration" naming elsewhere. FYI.
#27656
There was a problem hiding this comment.
I'm going to leave this as open an unaddressed for now, waiting until either this is close to ready or that PR goes in to do a rename.
|
🔗 No broken links found! ✅ Your attention to detail is admirable. linkcheck output |
Bundle size comparisonBase commit: Notable changes
Per-bundle deltas
|
Description
This add a simple API as a cleaner alternative to fluid-static and aquaduct.
The implementation details are well encapsulated, making this a viable alternative to the
fluid-staticpublic API and a viable successor to the existing much larger legacy API surface we do not want to stabilize.This addresses several issues with our existing APIs:
Because this new "unified" API offers value for our own internal testing (for example in tree), it is useful, even as alpha (or internal).
Exposing this new API as alpha should be low risk, as it makes no long-term commitments.
Since we have first party in-repo use cases, it should be practical to refine and iterate on this API with use in test and examples before we consider promoting it to beta.
Requirements for this new API:
2. Can easily be used for tests including collab and reopening documents.
3. Can be used by DDS (like shared-tree) tests replacing the need for many of our mocks and test utils
4. Can be used by customers (apps and libraries requiring stable APIs when promoted past alpha). When adopted, customers should not have need of any of our legacy test-utils and mocks (so we can migrate our legacy apps off of our test utils and eventually make them internal only).
Reviewer Guidance
The review process is outlined on this wiki page.
The example in the changeset is a good place to get started, followed by the examples/tests in the tree package.
Next I'd recomend reviewing the actual types for the API followed by its implementation.
See #27078 for many more examples of how this could be implemented in more examples and for other services.