Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export interface IContainer extends IEventProvider<IContainerEvents> {
attach(request: IRequest, attachProps?: {
deltaConnection?: "none" | "delayed";
}): Promise<void>;
readonly attachState: AttachState;
readonly attachState: AttachState | undefined;
readonly audience: IAudience;
readonly clientId?: string | undefined;
close(error?: ICriticalContainerError): void;
Expand Down
6 changes: 5 additions & 1 deletion packages/common/container-definitions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@
"typescript": "~5.4.5"
},
"typeValidation": {
"broken": {},
"broken": {
"Interface_IContainer": {
"backCompat": false
}
},
"entrypoint": "legacy"
}
}
3 changes: 2 additions & 1 deletion packages/common/container-definitions/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,9 @@ export interface IContainer extends IEventProvider<IContainerEvents> {

/**
* Indicates the attachment state of the container to a host service.
* Will be `undefined` when the container is being loaded and the state is not yet known.
*/
readonly attachState: AttachState;
readonly attachState: AttachState | undefined;

/**
* Get the code details that are currently specified for the container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ declare type old_as_current_for_Interface_IContainer = requireAssignableTo<TypeO
* typeValidation.broken:
* "Interface_IContainer": {"backCompat": false}
*/
// @ts-expect-error compatibility expected to be broken
declare type current_as_old_for_Interface_IContainer = requireAssignableTo<TypeOnly<current.IContainer>, TypeOnly<old.IContainer>>

/*
Expand Down
5 changes: 4 additions & 1 deletion packages/framework/fluid-static/src/fluidContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,10 @@ class FluidContainer<TContainerSchema extends ContainerSchema = ContainerSchema>
}

public get attachState(): AttachState {
return this.container.attachState;
// attachState can be undefined on IContainer while loading, but FluidContainer is only
// created after loading completes, so attachState is always defined here.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.container.attachState!;
}

public get disposed(): boolean {
Expand Down
25 changes: 18 additions & 7 deletions packages/loader/container-loader/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ export class Container
private readonly connectionTransitionTimes: number[] = [];
private _loadedFromVersion: IVersion | undefined;
private _dirtyContainer = false;
private attachmentData: AttachmentData = { state: AttachState.Detached };
private attachmentData: AttachmentData | undefined;
private readonly serializedStateManager: SerializedStateManager;
private readonly _containerId: string;

Expand Down Expand Up @@ -757,6 +757,12 @@ export class Container
this.connectionTransitionTimes[ConnectionState.Disconnected] = performanceNow();
const pendingLocalState = loadProps?.pendingLocalState;

// When loading an existing container, the attachment state is unknown until the load
// completes. Only initialize to Detached when creating a new container.
if (loadProps === undefined) {
this.attachmentData = { state: AttachState.Detached };
}

this._canReconnect = canReconnect ?? true;
this.clientDetailsOverride = clientDetailsOverride;
this.urlResolver = urlResolver;
Expand Down Expand Up @@ -1155,7 +1161,7 @@ export class Container
);
}
assert(
this.attachmentData.state === AttachState.Attached,
this.attachmentData?.state === AttachState.Attached,
0x0d1 /* "Container should be attached before close" */,
);
assert(
Expand All @@ -1170,8 +1176,8 @@ export class Container
return pendingState;
}

public get attachState(): AttachState {
return this.attachmentData.state;
public get attachState(): AttachState | undefined {
return this.attachmentData?.state;
}

/**
Expand All @@ -1181,7 +1187,11 @@ export class Container
* @returns stringified {@link IPendingDetachedContainerState} for the container
*/
public serialize(): string {
if (this.attachmentData.state === AttachState.Attached || this.closed) {
if (
this.attachmentData === undefined ||
this.attachmentData.state === AttachState.Attached ||
this.closed
) {
throw new UsageError("Container must not be attached or closed.");
}

Expand Down Expand Up @@ -1222,6 +1232,7 @@ export class Container
async () => {
if (
this._lifecycleState !== "loaded" ||
this.attachmentData === undefined ||
this.attachmentData.state === AttachState.Attached
) {
// pre-0.58 error message: containerNotValidForAttach
Expand All @@ -1243,7 +1254,7 @@ export class Container
const setAttachmentData: AttachProcessProps["setAttachmentData"] = (
attachmentData,
) => {
const previousState = this.attachmentData.state;
const previousState = this.attachmentData?.state;
this.attachmentData = attachmentData;
const state = this.attachmentData.state;
if (state !== previousState && state !== AttachState.Detached) {
Expand Down Expand Up @@ -2422,7 +2433,7 @@ export class Container
getAbsoluteUrl: this.getAbsoluteUrl,
getContainerDiagnosticId: () => this.resolvedUrl?.id,
getClientId: () => this.clientId,
getAttachState: () => this.attachState,
getAttachState: () => this.attachState as AttachState,
getConnected: () => this.connected,
getConnectionState: () => this.connectionState,
clientDetails: this._deltaManager.clientDetails,
Expand Down