Skip to content

feat(devtools-core): Visualize TreeDataObject in Devtools #24885

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

jikim-msft
Copy link
Contributor

@jikim-msft jikim-msft commented Jun 20, 2025

NOTE

This PR prototypes a devtools-core API integration and explores its potential use cases.

Background

This work originates from ADO 36175, which proposed creating an adapter in the office-bohemia repo to support per-Loop-Component (DataObject) visualization. However, implementing a full IContainer adapter was not feasible, as the required properties for the devtools-core visualizer couldn't be fulfilled.

Description

Rather than wrapping everything into an adapter, this PR takes the inverse approach. It introduces a new DecomposedIContainer interface and a DecomposedContainer class that implements it. This class is a lightweight, strict subset of the IContainer interface, exposing only the properties necessary for visualization.

Thanks to the property and event overlap (clientId, audience, connected, attached, etc.) with PureDataObject.runtime, we can effectively translate a PureDataObject into a decomposed IContainer type, enabling visualizer compatibility.

@github-actions github-actions bot added base: main PRs targeted against main branch area: dds Issues related to distributed data structures area: dds: tree area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct public api change Changes to a public API labels Jun 20, 2025
@github-actions github-actions bot removed area: dds Issues related to distributed data structures area: dds: tree labels Jun 23, 2025
@github-actions github-actions bot removed the area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct label Jun 23, 2025
@jikim-msft jikim-msft marked this pull request as ready for review June 23, 2025 23:38
@jikim-msft jikim-msft requested a review from a team as a code owner June 23, 2025 23:38
public registerDataObject(props: DataObjectProps): void {
const { runtime, dataObject } = props;

const runtimeId = `${runtime.id}-${Math.random().toString(36).slice(2, 11)}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Josmithr

As far as I remember, we were assuming 1-runtime per 1-dataobject, thus the containers will be storing unqiue runtime.id.

However, I've been keep getting A as runtime.id over different PureDataObject.runtime. Still investigating why, but in the meantime assigned random number to bypass the issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just to clarify: you're saying that runtime.id is the string literal "^" for all of the data objects?

If so, then we may have a problem, and this approach may not work. We really need some globally unique identifier for this schema to work. Ideally, we could just get the ID of the container associated with the data object. Barring that, some other unique identifier to group data objects under.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the same runtime generates a bunch of dds. Ideally, don't we want them to be shown under a same tag? This will make the runtime id different all the time. Why can't we just ${runtime.id}?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chentong7

More precisely, I am using runtime.id here to distinguish between different PureDataObject not DDSs. However, for some reason, runtime.id of different DataObject (e.g., AppData, AppDataTwo, and etc.) are all throwing same id which throws an runtime error in the devtools side.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because these two objects is generated by the same runtime. Ideally, shouldn't under this runtimeId there are two objects listed under the tag Data?
Screenshot 2025-04-07 180329

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the "^" runtime ID is due to our use of internal test infrastructure. I would love to see what IDs we get once you've prototyped integration into Loop. I do see that PureDataObject.id just returns its this.runtime.id, so I suspect each data object will have its own unique runtime.

If that's case, a follow up question is whether or not that ID is globally unique, i.e. unique even across containers. My guess is that they likely must be unique within a container, but probably not across them. If they are globally unique, then we can always create a more customized UX that lists "Data Objects" instead of "Containers". If they're not globally unique, then we have a problem.

*
* @alpha
*/
export interface DecomposedIContainer extends IEventProvider<IContainerEvents> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often does IContainer change? How do you detect when DecomposedIContainer goes out of sync with the IContainer API? It feels like you need something like a type test, but one that compares members and signatures for two different types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package's APIs should still be expressed in terms of IContainer (I left some feedback to this effect elsewhere). In that model, this package will fail to build if they become out-of-sync.

* Implementation of {@link DecomposedIContainer} that wraps an {@link IFluidDataStoreRuntime}.
* This class provides a bridge between {@link IFluidDataStoreRuntime} and the devtools system by exposing runtime properties and events as {@link IContainer} interfaces.
*
* @alpha
Copy link
Contributor

@Josmithr Josmithr Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't export this class, so it shouldn't need a release tag.

@@ -281,15 +300,15 @@ export class ContainerDevtools implements IContainerDevtools, HasContainerKey {
[ConnectContainer.MessageType]: async (untypedMessage) => {
const message = untypedMessage as ConnectContainer.Message;
if (message.data.containerKey === this.containerKey) {
this.container.connect();
this.container.connect?.();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to make these behaviors more robust before merging. We should update things such that the UI doesn't display the connect/disconnect buttons when they are not available.

Copy link
Contributor

@Josmithr Josmithr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend making this PR a draft. There is still a fair amount of work that needs to be done to this package and devtools-view to make this new infrastructure robust. I've left a handful of comments to this effect. Great proof of concept, but there is still a fair amount of work to be done to productionize it.

@@ -94,6 +96,7 @@ declare type current_as_old_for_Interface_IDevtoolsLogger = requireAssignableTo<
* typeValidation.broken:
* "Interface_IFluidDevtools": {"forwardCompat": false}
*/
// @ts-expect-error compatibility expected to be broken
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This backward incompatibility will break the office-bohemia because we recently check-in the "integrate devtools in loop" code change and that commit use registerContainerDevtools API.

/**
* The data object to register with the Devtools.
*/
dataObject: PureDataObject;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TreeDataObject is @internal. Thus using PureDataObject for now.

Copy link
Contributor

🔗 No broken links found! ✅

Your attention to detail is admirable.

linkcheck output


> fluid-framework-docs-site@0.0.0 ci:check-links /home/runner/work/FluidFramework/FluidFramework/docs
> start-server-and-test "npm run serve -- --no-open" 3000 check-links

1: starting server using command "npm run serve -- --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> fluid-framework-docs-site@0.0.0 serve
> docusaurus serve --no-open

[SUCCESS] Serving "build" directory at: http://localhost:3000/

> fluid-framework-docs-site@0.0.0 check-links
> linkcheck http://localhost:3000 --skip-file skipped-urls.txt

Crawling...

Stats:
  231978 links
    1733 destination URLs
    1964 URLs ignored
       0 warnings
       0 errors


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
base: main PRs targeted against main branch public api change Changes to a public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants