@fluidframework/container-loader 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.5.3.2.178189
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/catchUpMonitor.d.ts +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +4 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +10 -8
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +30 -31
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +180 -108
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +23 -66
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +28 -213
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +38 -6
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +1 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +2 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js.map +1 -1
- package/dist/disposal.d.ts +13 -0
- package/dist/disposal.d.ts.map +1 -0
- package/dist/disposal.js +25 -0
- package/dist/disposal.js.map +1 -0
- package/dist/loader.d.ts +1 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js.map +1 -1
- package/dist/noopHeuristic.d.ts +23 -0
- package/dist/noopHeuristic.d.ts.map +1 -0
- package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
- package/dist/noopHeuristic.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +7 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +17 -19
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/quorum.d.ts +1 -17
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js +1 -17
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +4 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +10 -8
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +30 -31
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +184 -112
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +23 -66
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +28 -213
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +38 -6
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +1 -3
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +2 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js.map +1 -1
- package/lib/disposal.d.ts +13 -0
- package/lib/disposal.d.ts.map +1 -0
- package/lib/disposal.js +21 -0
- package/lib/disposal.js.map +1 -0
- package/lib/loader.d.ts +1 -2
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js.map +1 -1
- package/lib/noopHeuristic.d.ts +23 -0
- package/lib/noopHeuristic.d.ts.map +1 -0
- package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
- package/lib/noopHeuristic.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +7 -12
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +15 -18
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/quorum.d.ts +1 -17
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js +1 -16
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/package.json +18 -14
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +1 -1
- package/src/connectionStateHandler.ts +15 -10
- package/src/container.ts +279 -139
- package/src/containerContext.ts +33 -335
- package/src/containerStorageAdapter.ts +47 -5
- package/src/contracts.ts +1 -3
- package/src/deltaManager.ts +15 -8
- package/src/disposal.ts +25 -0
- package/src/loader.ts +1 -1
- package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +18 -39
- package/src/protocolTreeDocumentStorageService.ts +1 -1
- package/src/quorum.ts +2 -31
- package/src/retriableDocumentStorageService.ts +2 -1
- package/dist/collabWindowTracker.d.ts +0 -19
- package/dist/collabWindowTracker.d.ts.map +0 -1
- package/dist/collabWindowTracker.js.map +0 -1
- package/dist/deltaManagerProxy.d.ts +0 -42
- package/dist/deltaManagerProxy.d.ts.map +0 -1
- package/dist/deltaManagerProxy.js +0 -79
- package/dist/deltaManagerProxy.js.map +0 -1
- package/lib/collabWindowTracker.d.ts +0 -19
- package/lib/collabWindowTracker.d.ts.map +0 -1
- package/lib/collabWindowTracker.js.map +0 -1
- package/lib/deltaManagerProxy.d.ts +0 -42
- package/lib/deltaManagerProxy.d.ts.map +0 -1
- package/lib/deltaManagerProxy.js +0 -74
- package/lib/deltaManagerProxy.js.map +0 -1
- package/src/deltaManagerProxy.ts +0 -109
package/src/container.ts
CHANGED
|
@@ -7,9 +7,21 @@
|
|
|
7
7
|
import merge from "lodash/merge";
|
|
8
8
|
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
10
|
+
import { IEvent } from "@fluidframework/common-definitions";
|
|
11
|
+
import {
|
|
12
|
+
TypedEventEmitter,
|
|
13
|
+
assert,
|
|
14
|
+
performance,
|
|
15
|
+
unreachableCase,
|
|
16
|
+
} from "@fluidframework/common-utils";
|
|
17
|
+
import {
|
|
18
|
+
ITelemetryProperties,
|
|
19
|
+
TelemetryEventCategory,
|
|
20
|
+
IRequest,
|
|
21
|
+
IResponse,
|
|
22
|
+
IFluidRouter,
|
|
23
|
+
FluidObject,
|
|
24
|
+
} from "@fluidframework/core-interfaces";
|
|
13
25
|
import {
|
|
14
26
|
IAudience,
|
|
15
27
|
IConnectionDetailsInternal,
|
|
@@ -27,6 +39,11 @@ import {
|
|
|
27
39
|
IBatchMessage,
|
|
28
40
|
ICodeDetailsLoader,
|
|
29
41
|
IHostLoader,
|
|
42
|
+
IFluidModuleWithDetails,
|
|
43
|
+
IProvideRuntimeFactory,
|
|
44
|
+
IProvideFluidCodeDetailsComparer,
|
|
45
|
+
IFluidCodeDetailsComparer,
|
|
46
|
+
IRuntime,
|
|
30
47
|
} from "@fluidframework/container-definitions";
|
|
31
48
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
32
49
|
import {
|
|
@@ -44,11 +61,12 @@ import {
|
|
|
44
61
|
combineAppAndProtocolSummary,
|
|
45
62
|
runWithRetry,
|
|
46
63
|
isCombinedAppAndProtocolSummary,
|
|
64
|
+
MessageType2,
|
|
65
|
+
canBeCoalescedByService,
|
|
47
66
|
} from "@fluidframework/driver-utils";
|
|
48
67
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
49
68
|
import {
|
|
50
69
|
IClient,
|
|
51
|
-
IClientConfiguration,
|
|
52
70
|
IClientDetails,
|
|
53
71
|
ICommittedProposal,
|
|
54
72
|
IDocumentAttributes,
|
|
@@ -83,7 +101,6 @@ import { Audience } from "./audience";
|
|
|
83
101
|
import { ContainerContext } from "./containerContext";
|
|
84
102
|
import { ReconnectMode, IConnectionManagerFactoryArgs, getPackageName } from "./contracts";
|
|
85
103
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
86
|
-
import { DeltaManagerProxy } from "./deltaManagerProxy";
|
|
87
104
|
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
88
105
|
import { pkgVersion } from "./packageVersion";
|
|
89
106
|
import {
|
|
@@ -94,19 +111,16 @@ import {
|
|
|
94
111
|
} from "./containerStorageAdapter";
|
|
95
112
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
96
113
|
import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
|
|
97
|
-
import {
|
|
98
|
-
|
|
99
|
-
getCodeDetailsFromQuorumValues,
|
|
100
|
-
QuorumProxy,
|
|
101
|
-
} from "./quorum";
|
|
102
|
-
import { CollabWindowTracker } from "./collabWindowTracker";
|
|
114
|
+
import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues } from "./quorum";
|
|
115
|
+
import { NoopHeuristic } from "./noopHeuristic";
|
|
103
116
|
import { ConnectionManager } from "./connectionManager";
|
|
104
117
|
import { ConnectionState } from "./connectionState";
|
|
105
118
|
import {
|
|
106
|
-
OnlyValidTermValue,
|
|
107
119
|
IProtocolHandler,
|
|
120
|
+
OnlyValidTermValue,
|
|
108
121
|
ProtocolHandler,
|
|
109
122
|
ProtocolHandlerBuilder,
|
|
123
|
+
protocolHandlerShouldProcessSignal,
|
|
110
124
|
} from "./protocol";
|
|
111
125
|
|
|
112
126
|
const detachedContainerRefSeqNumber = 0;
|
|
@@ -114,6 +128,8 @@ const detachedContainerRefSeqNumber = 0;
|
|
|
114
128
|
const dirtyContainerEvent = "dirty";
|
|
115
129
|
const savedContainerEvent = "saved";
|
|
116
130
|
|
|
131
|
+
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
132
|
+
|
|
117
133
|
/**
|
|
118
134
|
* @internal
|
|
119
135
|
*/
|
|
@@ -336,12 +352,15 @@ export interface IPendingContainerState {
|
|
|
336
352
|
|
|
337
353
|
const summarizerClientType = "summarizer";
|
|
338
354
|
|
|
355
|
+
interface IContainerLifecycleEvents extends IEvent {
|
|
356
|
+
(event: "runtimeInstantiated", listener: () => void): void;
|
|
357
|
+
(event: "disposed", listener: () => void): void;
|
|
358
|
+
}
|
|
359
|
+
|
|
339
360
|
export class Container
|
|
340
361
|
extends EventEmitterWithErrorHandling<IContainerEvents>
|
|
341
362
|
implements IContainer, IContainerExperimental
|
|
342
363
|
{
|
|
343
|
-
public static version = "^0.1.0";
|
|
344
|
-
|
|
345
364
|
/**
|
|
346
365
|
* Load an existing container.
|
|
347
366
|
* @internal
|
|
@@ -452,9 +471,9 @@ export class Container
|
|
|
452
471
|
private readonly urlResolver: IUrlResolver;
|
|
453
472
|
private readonly serviceFactory: IDocumentServiceFactory;
|
|
454
473
|
private readonly codeLoader: ICodeDetailsLoader;
|
|
455
|
-
|
|
474
|
+
private readonly options: ILoaderOptions;
|
|
456
475
|
private readonly scope: FluidObject;
|
|
457
|
-
|
|
476
|
+
private readonly subLogger: TelemetryLogger;
|
|
458
477
|
private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
|
|
459
478
|
private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
|
|
460
479
|
|
|
@@ -514,19 +533,16 @@ export class Container
|
|
|
514
533
|
private _attachState = AttachState.Detached;
|
|
515
534
|
|
|
516
535
|
private readonly storageAdapter: ContainerStorageAdapter;
|
|
517
|
-
public get storage(): IDocumentStorageService {
|
|
518
|
-
return this.storageAdapter;
|
|
519
|
-
}
|
|
520
536
|
|
|
521
537
|
private readonly _deltaManager: DeltaManager<ConnectionManager>;
|
|
522
538
|
private service: IDocumentService | undefined;
|
|
523
539
|
|
|
524
|
-
private
|
|
525
|
-
private get
|
|
526
|
-
if (this.
|
|
527
|
-
throw new
|
|
540
|
+
private _runtime: IRuntime | undefined;
|
|
541
|
+
private get runtime() {
|
|
542
|
+
if (this._runtime === undefined) {
|
|
543
|
+
throw new Error("Attempted to access runtime before it was defined");
|
|
528
544
|
}
|
|
529
|
-
return this.
|
|
545
|
+
return this._runtime;
|
|
530
546
|
}
|
|
531
547
|
private _protocolHandler: IProtocolHandler | undefined;
|
|
532
548
|
private get protocolHandler() {
|
|
@@ -551,10 +567,11 @@ export class Container
|
|
|
551
567
|
private lastVisible: number | undefined;
|
|
552
568
|
private readonly visibilityEventHandler: (() => void) | undefined;
|
|
553
569
|
private readonly connectionStateHandler: IConnectionStateHandler;
|
|
570
|
+
private readonly clientsWhoShouldHaveLeft = new Set<string>();
|
|
554
571
|
|
|
555
572
|
private setAutoReconnectTime = performance.now();
|
|
556
573
|
|
|
557
|
-
private
|
|
574
|
+
private noopHeuristic: NoopHeuristic | undefined;
|
|
558
575
|
|
|
559
576
|
private get connectionMode() {
|
|
560
577
|
return this._deltaManager.connectionManager.connectionMode;
|
|
@@ -579,18 +596,10 @@ export class Container
|
|
|
579
596
|
return this.service?.resolvedUrl;
|
|
580
597
|
}
|
|
581
598
|
|
|
582
|
-
public get loadedFromVersion(): IVersion | undefined {
|
|
583
|
-
return this._loadedFromVersion;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
599
|
public get readOnlyInfo(): ReadOnlyInfo {
|
|
587
600
|
return this._deltaManager.readOnlyInfo;
|
|
588
601
|
}
|
|
589
602
|
|
|
590
|
-
public get closeSignal(): AbortSignal {
|
|
591
|
-
return this._deltaManager.closeAbortController.signal;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
603
|
/**
|
|
595
604
|
* Tracks host requiring read-only mode.
|
|
596
605
|
*/
|
|
@@ -606,18 +615,10 @@ export class Container
|
|
|
606
615
|
return this.connectionStateHandler.connectionState;
|
|
607
616
|
}
|
|
608
617
|
|
|
609
|
-
|
|
618
|
+
private get connected(): boolean {
|
|
610
619
|
return this.connectionStateHandler.connectionState === ConnectionState.Connected;
|
|
611
620
|
}
|
|
612
621
|
|
|
613
|
-
/**
|
|
614
|
-
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
615
|
-
* configuration details returned as part of the initial connection.
|
|
616
|
-
*/
|
|
617
|
-
public get serviceConfiguration(): IClientConfiguration | undefined {
|
|
618
|
-
return this._deltaManager.serviceConfiguration;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
622
|
private _clientId: string | undefined;
|
|
622
623
|
|
|
623
624
|
/**
|
|
@@ -628,24 +629,12 @@ export class Container
|
|
|
628
629
|
return this._clientId;
|
|
629
630
|
}
|
|
630
631
|
|
|
631
|
-
/**
|
|
632
|
-
* The server provided claims of the client.
|
|
633
|
-
* Set once this.connected is true, otherwise undefined
|
|
634
|
-
*/
|
|
635
|
-
public get scopes(): string[] | undefined {
|
|
636
|
-
return this._deltaManager.connectionManager.scopes;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
public get clientDetails(): IClientDetails {
|
|
640
|
-
return this._deltaManager.clientDetails;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
632
|
private get offlineLoadEnabled(): boolean {
|
|
644
633
|
const enabled =
|
|
645
634
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
|
|
646
635
|
this.options?.enableOfflineLoad === true;
|
|
647
636
|
// summarizer will not have any pending state we want to save
|
|
648
|
-
return enabled && this.clientDetails.capabilities.interactive;
|
|
637
|
+
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
649
638
|
}
|
|
650
639
|
|
|
651
640
|
/**
|
|
@@ -656,15 +645,18 @@ export class Container
|
|
|
656
645
|
return this.getCodeDetailsFromQuorum();
|
|
657
646
|
}
|
|
658
647
|
|
|
648
|
+
private _loadedCodeDetails: IFluidCodeDetails | undefined;
|
|
659
649
|
/**
|
|
660
650
|
* Get the code details that were used to load the container.
|
|
661
651
|
* @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
|
|
662
652
|
* loaded.
|
|
663
653
|
*/
|
|
664
654
|
public getLoadedCodeDetails(): IFluidCodeDetails | undefined {
|
|
665
|
-
return this.
|
|
655
|
+
return this._loadedCodeDetails;
|
|
666
656
|
}
|
|
667
657
|
|
|
658
|
+
private _loadedModule: IFluidModuleWithDetails | undefined;
|
|
659
|
+
|
|
668
660
|
/**
|
|
669
661
|
* Retrieves the audience associated with the document
|
|
670
662
|
*/
|
|
@@ -684,38 +676,33 @@ export class Container
|
|
|
684
676
|
/**
|
|
685
677
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
686
678
|
*/
|
|
687
|
-
public async getEntryPoint
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
// Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
|
|
691
|
-
if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
|
|
692
|
-
throw new UsageError("The container is disposing or disposed");
|
|
679
|
+
public async getEntryPoint(): Promise<FluidObject | undefined> {
|
|
680
|
+
if (this._disposed) {
|
|
681
|
+
throw new UsageError("The context is already disposed");
|
|
693
682
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const contextChangedHandler = () => {
|
|
697
|
-
resolve();
|
|
698
|
-
this.off("disposed", disposedHandler);
|
|
699
|
-
};
|
|
700
|
-
const disposedHandler = (error) => {
|
|
701
|
-
reject(error ?? "The Container is disposed");
|
|
702
|
-
this.off("contextChanged", contextChangedHandler);
|
|
703
|
-
};
|
|
704
|
-
this.once("contextChanged", contextChangedHandler);
|
|
705
|
-
this.once("disposed", disposedHandler);
|
|
706
|
-
});
|
|
707
|
-
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
708
|
-
// should have set this._context; making sure.
|
|
709
|
-
assert(
|
|
710
|
-
this._context !== undefined,
|
|
711
|
-
0x5a2 /* Context still not defined after contextChanged event */,
|
|
712
|
-
);
|
|
683
|
+
if (this._runtime !== undefined) {
|
|
684
|
+
return this._runtime.getEntryPoint?.();
|
|
713
685
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
686
|
+
return new Promise<FluidObject | undefined>((resolve, reject) => {
|
|
687
|
+
const runtimeInstantiatedHandler = () => {
|
|
688
|
+
assert(
|
|
689
|
+
this._runtime !== undefined,
|
|
690
|
+
0x5a3 /* runtimeInstantiated fired but runtime is still undefined */,
|
|
691
|
+
);
|
|
692
|
+
resolve(this._runtime.getEntryPoint?.());
|
|
693
|
+
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
694
|
+
};
|
|
695
|
+
const disposedHandler = () => {
|
|
696
|
+
reject(new Error("ContainerContext was disposed"));
|
|
697
|
+
this._lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
698
|
+
};
|
|
699
|
+
this._lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
700
|
+
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
701
|
+
});
|
|
717
702
|
}
|
|
718
703
|
|
|
704
|
+
private readonly _lifecycleEvents = new TypedEventEmitter<IContainerLifecycleEvents>();
|
|
705
|
+
|
|
719
706
|
/**
|
|
720
707
|
* @internal
|
|
721
708
|
*/
|
|
@@ -800,13 +787,16 @@ export class Container
|
|
|
800
787
|
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
801
788
|
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
802
789
|
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
803
|
-
containerLoadedFromVersionId: () => this.
|
|
804
|
-
containerLoadedFromVersionDate: () => this.
|
|
790
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
791
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
805
792
|
// message information to associate errors with the specific execution state
|
|
806
793
|
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
807
794
|
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
808
795
|
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
809
|
-
dmLastMsqSeqClientId: () =>
|
|
796
|
+
dmLastMsqSeqClientId: () =>
|
|
797
|
+
this.deltaManager?.lastMessage?.clientId === null
|
|
798
|
+
? "null"
|
|
799
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
810
800
|
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
811
801
|
connectionStateDuration: () =>
|
|
812
802
|
performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
@@ -871,6 +861,9 @@ export class Container
|
|
|
871
861
|
this.connect();
|
|
872
862
|
}
|
|
873
863
|
},
|
|
864
|
+
clientShouldHaveLeft: (clientId: string) => {
|
|
865
|
+
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
866
|
+
},
|
|
874
867
|
},
|
|
875
868
|
this.deltaManager,
|
|
876
869
|
pendingLocalState?.clientId,
|
|
@@ -994,6 +987,11 @@ export class Container
|
|
|
994
987
|
}
|
|
995
988
|
} finally {
|
|
996
989
|
this._lifecycleState = "closed";
|
|
990
|
+
|
|
991
|
+
// There is no user for summarizer, so we need to ensure dispose is called
|
|
992
|
+
if (this.client.details.type === summarizerClientType) {
|
|
993
|
+
this.dispose(error);
|
|
994
|
+
}
|
|
997
995
|
}
|
|
998
996
|
}
|
|
999
997
|
|
|
@@ -1010,7 +1008,8 @@ export class Container
|
|
|
1010
1008
|
this.mc.logger.sendTelemetryEvent(
|
|
1011
1009
|
{
|
|
1012
1010
|
eventName: "ContainerDispose",
|
|
1013
|
-
|
|
1011
|
+
// Only log error if container isn't closed
|
|
1012
|
+
category: !this.closed && error !== undefined ? "error" : "generic",
|
|
1014
1013
|
},
|
|
1015
1014
|
error,
|
|
1016
1015
|
);
|
|
@@ -1024,7 +1023,8 @@ export class Container
|
|
|
1024
1023
|
|
|
1025
1024
|
this.connectionStateHandler.dispose();
|
|
1026
1025
|
|
|
1027
|
-
|
|
1026
|
+
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
1027
|
+
this._runtime?.dispose(maybeError);
|
|
1028
1028
|
|
|
1029
1029
|
this.storageAdapter.dispose();
|
|
1030
1030
|
|
|
@@ -1047,6 +1047,7 @@ export class Container
|
|
|
1047
1047
|
}
|
|
1048
1048
|
} finally {
|
|
1049
1049
|
this._lifecycleState = "disposed";
|
|
1050
|
+
this._lifecycleEvents.emit("disposed");
|
|
1050
1051
|
}
|
|
1051
1052
|
}
|
|
1052
1053
|
|
|
@@ -1063,6 +1064,11 @@ export class Container
|
|
|
1063
1064
|
if (!this.offlineLoadEnabled) {
|
|
1064
1065
|
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
1065
1066
|
}
|
|
1067
|
+
if (this.closed || this._disposed) {
|
|
1068
|
+
throw new UsageError(
|
|
1069
|
+
"Pending state cannot be retried if the container is closed or disposed",
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1066
1072
|
assert(
|
|
1067
1073
|
this.attachState === AttachState.Attached,
|
|
1068
1074
|
0x0d1 /* "Container should be attached before close" */,
|
|
@@ -1074,7 +1080,7 @@ export class Container
|
|
|
1074
1080
|
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1075
1081
|
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1076
1082
|
const pendingState: IPendingContainerState = {
|
|
1077
|
-
pendingRuntimeState: this.
|
|
1083
|
+
pendingRuntimeState: this.runtime.getPendingLocalState(),
|
|
1078
1084
|
baseSnapshot: this.baseSnapshot,
|
|
1079
1085
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1080
1086
|
savedOps: this.savedOps,
|
|
@@ -1098,7 +1104,7 @@ export class Container
|
|
|
1098
1104
|
0x0d3 /* "Should only be called in detached container" */,
|
|
1099
1105
|
);
|
|
1100
1106
|
|
|
1101
|
-
const appSummary: ISummaryTree = this.
|
|
1107
|
+
const appSummary: ISummaryTree = this.runtime.createSummary();
|
|
1102
1108
|
const protocolSummary = this.captureProtocolSummary();
|
|
1103
1109
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1104
1110
|
|
|
@@ -1144,7 +1150,7 @@ export class Container
|
|
|
1144
1150
|
if (!hasAttachmentBlobs) {
|
|
1145
1151
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
1146
1152
|
// semantics around what the attach means as far as async code goes.
|
|
1147
|
-
const appSummary: ISummaryTree = this.
|
|
1153
|
+
const appSummary: ISummaryTree = this.runtime.createSummary();
|
|
1148
1154
|
const protocolSummary = this.captureProtocolSummary();
|
|
1149
1155
|
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1150
1156
|
|
|
@@ -1153,6 +1159,7 @@ export class Container
|
|
|
1153
1159
|
// starting to attach the container to storage.
|
|
1154
1160
|
// Also, this should only be fired in detached container.
|
|
1155
1161
|
this._attachState = AttachState.Attaching;
|
|
1162
|
+
this.runtime.setAttachState(AttachState.Attaching);
|
|
1156
1163
|
this.emit("attaching");
|
|
1157
1164
|
if (this.offlineLoadEnabled) {
|
|
1158
1165
|
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
@@ -1181,7 +1188,7 @@ export class Container
|
|
|
1181
1188
|
"containerAttach",
|
|
1182
1189
|
this.mc.logger,
|
|
1183
1190
|
{
|
|
1184
|
-
cancel: this.
|
|
1191
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
1185
1192
|
}, // progress
|
|
1186
1193
|
);
|
|
1187
1194
|
}
|
|
@@ -1210,11 +1217,12 @@ export class Container
|
|
|
1210
1217
|
}
|
|
1211
1218
|
|
|
1212
1219
|
// take summary and upload
|
|
1213
|
-
const appSummary: ISummaryTree = this.
|
|
1220
|
+
const appSummary: ISummaryTree = this.runtime.createSummary(redirectTable);
|
|
1214
1221
|
const protocolSummary = this.captureProtocolSummary();
|
|
1215
1222
|
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1216
1223
|
|
|
1217
1224
|
this._attachState = AttachState.Attaching;
|
|
1225
|
+
this.runtime.setAttachState(AttachState.Attaching);
|
|
1218
1226
|
this.emit("attaching");
|
|
1219
1227
|
if (this.offlineLoadEnabled) {
|
|
1220
1228
|
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
@@ -1231,6 +1239,7 @@ export class Container
|
|
|
1231
1239
|
}
|
|
1232
1240
|
|
|
1233
1241
|
this._attachState = AttachState.Attached;
|
|
1242
|
+
this.runtime.setAttachState(AttachState.Attached);
|
|
1234
1243
|
this.emit("attached");
|
|
1235
1244
|
|
|
1236
1245
|
if (!this.closed) {
|
|
@@ -1255,7 +1264,7 @@ export class Container
|
|
|
1255
1264
|
return PerformanceEvent.timedExecAsync(
|
|
1256
1265
|
this.mc.logger,
|
|
1257
1266
|
{ eventName: "Request" },
|
|
1258
|
-
async () => this.
|
|
1267
|
+
async () => this.runtime.request(path),
|
|
1259
1268
|
{ end: true, cancel: "error" },
|
|
1260
1269
|
);
|
|
1261
1270
|
}
|
|
@@ -1340,7 +1349,7 @@ export class Container
|
|
|
1340
1349
|
this.connectToDeltaStream(args);
|
|
1341
1350
|
}
|
|
1342
1351
|
|
|
1343
|
-
public async
|
|
1352
|
+
public readonly getAbsoluteUrl = async (relativeUrl: string): Promise<string | undefined> => {
|
|
1344
1353
|
if (this.resolvedUrl === undefined) {
|
|
1345
1354
|
return undefined;
|
|
1346
1355
|
}
|
|
@@ -1348,9 +1357,9 @@ export class Container
|
|
|
1348
1357
|
return this.urlResolver.getAbsoluteUrl(
|
|
1349
1358
|
this.resolvedUrl,
|
|
1350
1359
|
relativeUrl,
|
|
1351
|
-
getPackageName(this.
|
|
1360
|
+
getPackageName(this._loadedCodeDetails),
|
|
1352
1361
|
);
|
|
1353
|
-
}
|
|
1362
|
+
};
|
|
1354
1363
|
|
|
1355
1364
|
public async proposeCodeDetails(codeDetails: IFluidCodeDetails) {
|
|
1356
1365
|
if (!isFluidCodeDetails(codeDetails)) {
|
|
@@ -1381,7 +1390,7 @@ export class Container
|
|
|
1381
1390
|
this.deltaManager.inboundSignal.pause(),
|
|
1382
1391
|
]);
|
|
1383
1392
|
|
|
1384
|
-
if ((await this.
|
|
1393
|
+
if ((await this.satisfies(codeDetails)) === true) {
|
|
1385
1394
|
this.deltaManager.inbound.resume();
|
|
1386
1395
|
this.deltaManager.inboundSignal.resume();
|
|
1387
1396
|
return;
|
|
@@ -1392,6 +1401,47 @@ export class Container
|
|
|
1392
1401
|
this.close(error);
|
|
1393
1402
|
}
|
|
1394
1403
|
|
|
1404
|
+
/**
|
|
1405
|
+
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
1406
|
+
*/
|
|
1407
|
+
private async satisfies(constraintCodeDetails: IFluidCodeDetails) {
|
|
1408
|
+
// If we have no module, it can't satisfy anything.
|
|
1409
|
+
if (this._loadedModule === undefined) {
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
const comparers: IFluidCodeDetailsComparer[] = [];
|
|
1414
|
+
|
|
1415
|
+
const maybeCompareCodeLoader = this.codeLoader;
|
|
1416
|
+
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
1417
|
+
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
const maybeCompareExport: Partial<IProvideFluidCodeDetailsComparer> | undefined =
|
|
1421
|
+
this._loadedModule?.module.fluidExport;
|
|
1422
|
+
if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
|
|
1423
|
+
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
1427
|
+
// the incoming constraint, so we return false. Assuming it does not satisfy is safer, to force a reload
|
|
1428
|
+
// rather than potentially running with incompatible code.
|
|
1429
|
+
if (comparers.length === 0) {
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
for (const comparer of comparers) {
|
|
1434
|
+
const satisfies = await comparer.satisfies(
|
|
1435
|
+
this._loadedModule?.details,
|
|
1436
|
+
constraintCodeDetails,
|
|
1437
|
+
);
|
|
1438
|
+
if (satisfies === false) {
|
|
1439
|
+
return false;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return true;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1395
1445
|
private async getVersion(version: string | null): Promise<IVersion | undefined> {
|
|
1396
1446
|
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
1397
1447
|
return versions[0];
|
|
@@ -1469,7 +1519,10 @@ export class Container
|
|
|
1469
1519
|
if (this.offlineLoadEnabled) {
|
|
1470
1520
|
this.baseSnapshot = snapshot;
|
|
1471
1521
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
1472
|
-
this.baseSnapshotBlobs = await getBlobContentsFromTree(
|
|
1522
|
+
this.baseSnapshotBlobs = await getBlobContentsFromTree(
|
|
1523
|
+
snapshot,
|
|
1524
|
+
this.storageAdapter,
|
|
1525
|
+
);
|
|
1473
1526
|
}
|
|
1474
1527
|
}
|
|
1475
1528
|
|
|
@@ -1525,7 +1578,7 @@ export class Container
|
|
|
1525
1578
|
this.processRemoteMessage(message);
|
|
1526
1579
|
|
|
1527
1580
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
1528
|
-
await this.
|
|
1581
|
+
await this.runtime.notifyOpReplay?.(message);
|
|
1529
1582
|
}
|
|
1530
1583
|
pendingLocalState.savedOps = [];
|
|
1531
1584
|
|
|
@@ -1877,7 +1930,7 @@ export class Container
|
|
|
1877
1930
|
});
|
|
1878
1931
|
|
|
1879
1932
|
deltaManager.on("disconnect", (reason: string, error?: IAnyDriverError) => {
|
|
1880
|
-
this.
|
|
1933
|
+
this.noopHeuristic?.notifyDisconnect();
|
|
1881
1934
|
if (!this.closed) {
|
|
1882
1935
|
this.connectionStateHandler.receivedDisconnectEvent(reason, error);
|
|
1883
1936
|
}
|
|
@@ -1955,7 +2008,11 @@ export class Container
|
|
|
1955
2008
|
} else if (value === ConnectionState.CatchingUp) {
|
|
1956
2009
|
// This info is of most interesting while Catching Up.
|
|
1957
2010
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1958
|
-
|
|
2011
|
+
// Need to check that we have already loaded and fetched the snapshot.
|
|
2012
|
+
if (
|
|
2013
|
+
this.deltaManager.hasCheckpointSequenceNumber &&
|
|
2014
|
+
this._lifecycleState === "loaded"
|
|
2015
|
+
) {
|
|
1959
2016
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1960
2017
|
}
|
|
1961
2018
|
}
|
|
@@ -2100,7 +2157,7 @@ export class Container
|
|
|
2100
2157
|
}
|
|
2101
2158
|
|
|
2102
2159
|
this.messageCountAfterDisconnection += 1;
|
|
2103
|
-
this.
|
|
2160
|
+
this.noopHeuristic?.notifyMessageSent();
|
|
2104
2161
|
return this._deltaManager.submit(
|
|
2105
2162
|
type,
|
|
2106
2163
|
contents,
|
|
@@ -2117,39 +2174,67 @@ export class Container
|
|
|
2117
2174
|
}
|
|
2118
2175
|
const local = this.clientId === message.clientId;
|
|
2119
2176
|
|
|
2177
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
2178
|
+
// flagged should have left, or from a client that's not in the quorum but should be
|
|
2179
|
+
if (message.clientId != null) {
|
|
2180
|
+
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
2181
|
+
|
|
2182
|
+
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
2183
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
2184
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
2188
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
2189
|
+
// document we don't need to blow up aggressively.
|
|
2190
|
+
if (
|
|
2191
|
+
this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
2192
|
+
!canBeCoalescedByService(message)
|
|
2193
|
+
) {
|
|
2194
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
2195
|
+
throw new Error("Remote message's clientId already should have left");
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2120
2199
|
// Allow the protocol handler to process the message
|
|
2121
2200
|
const result = this.protocolHandler.processMessage(message, local);
|
|
2122
2201
|
|
|
2123
2202
|
// Forward messages to the loaded runtime for processing
|
|
2124
|
-
this.
|
|
2203
|
+
this.runtime.process(message, local);
|
|
2125
2204
|
|
|
2126
2205
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
2127
2206
|
if (this.activeConnection()) {
|
|
2128
|
-
if (this.
|
|
2207
|
+
if (this.noopHeuristic === undefined) {
|
|
2208
|
+
const serviceConfiguration = this.deltaManager.serviceConfiguration;
|
|
2129
2209
|
// Note that config from first connection will be used for this container's lifetime.
|
|
2130
2210
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
2131
2211
|
// clients.
|
|
2132
2212
|
// All existing will continue to use settings they got earlier.
|
|
2133
2213
|
assert(
|
|
2134
|
-
|
|
2214
|
+
serviceConfiguration !== undefined,
|
|
2135
2215
|
0x2e4 /* "there should be service config for active connection" */,
|
|
2136
2216
|
);
|
|
2137
|
-
this.
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
this.activeConnection(),
|
|
2141
|
-
0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */,
|
|
2142
|
-
);
|
|
2143
|
-
this.submitMessage(type);
|
|
2144
|
-
},
|
|
2145
|
-
this.serviceConfiguration.noopTimeFrequency,
|
|
2146
|
-
this.serviceConfiguration.noopCountFrequency,
|
|
2217
|
+
this.noopHeuristic = new NoopHeuristic(
|
|
2218
|
+
serviceConfiguration.noopTimeFrequency,
|
|
2219
|
+
serviceConfiguration.noopCountFrequency,
|
|
2147
2220
|
);
|
|
2221
|
+
this.noopHeuristic.on("wantsNoop", () => {
|
|
2222
|
+
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
2223
|
+
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
2224
|
+
// running the microtask that the heuristic queued in response.
|
|
2225
|
+
assert(
|
|
2226
|
+
this.activeConnection(),
|
|
2227
|
+
0x241 /* "Trying to send noop without active connection" */,
|
|
2228
|
+
);
|
|
2229
|
+
this.submitMessage(MessageType.NoOp);
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
this.noopHeuristic.notifyMessageProcessed(message);
|
|
2233
|
+
// The contract with the protocolHandler is that returning "immediateNoOp" is equivalent to "please immediately accept the proposal I just processed".
|
|
2234
|
+
if (result.immediateNoOp === true) {
|
|
2235
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
2236
|
+
this.submitMessage(MessageType2.Accept as unknown as MessageType);
|
|
2148
2237
|
}
|
|
2149
|
-
this.collabWindowTracker.scheduleSequenceNumberUpdate(
|
|
2150
|
-
message,
|
|
2151
|
-
result.immediateNoOp === true,
|
|
2152
|
-
);
|
|
2153
2238
|
}
|
|
2154
2239
|
|
|
2155
2240
|
this.emit("op", message);
|
|
@@ -2161,11 +2246,11 @@ export class Container
|
|
|
2161
2246
|
|
|
2162
2247
|
private processSignal(message: ISignalMessage) {
|
|
2163
2248
|
// No clientId indicates a system signal message.
|
|
2164
|
-
if (message
|
|
2249
|
+
if (protocolHandlerShouldProcessSignal(message)) {
|
|
2165
2250
|
this.protocolHandler.processSignal(message);
|
|
2166
2251
|
} else {
|
|
2167
2252
|
const local = this.clientId === message.clientId;
|
|
2168
|
-
this.
|
|
2253
|
+
this.runtime.processSignal(message, local);
|
|
2169
2254
|
}
|
|
2170
2255
|
}
|
|
2171
2256
|
|
|
@@ -2207,23 +2292,50 @@ export class Container
|
|
|
2207
2292
|
private async instantiateContext(
|
|
2208
2293
|
existing: boolean,
|
|
2209
2294
|
codeDetails: IFluidCodeDetails,
|
|
2210
|
-
snapshot
|
|
2295
|
+
snapshot: ISnapshotTree | undefined,
|
|
2211
2296
|
pendingLocalState?: unknown,
|
|
2212
2297
|
) {
|
|
2213
|
-
assert(this.
|
|
2298
|
+
assert(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
2214
2299
|
|
|
2215
2300
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
2216
2301
|
// are set. Global requests will still go directly to the loader
|
|
2217
2302
|
const maybeLoader: FluidObject<IHostLoader> = this.scope;
|
|
2218
2303
|
const loader = new RelativeLoader(this, maybeLoader.ILoader);
|
|
2219
|
-
|
|
2220
|
-
|
|
2304
|
+
|
|
2305
|
+
const loadCodeResult = await PerformanceEvent.timedExecAsync(
|
|
2306
|
+
this.subLogger,
|
|
2307
|
+
{ eventName: "CodeLoad" },
|
|
2308
|
+
async () => this.codeLoader.load(codeDetails),
|
|
2309
|
+
);
|
|
2310
|
+
|
|
2311
|
+
this._loadedModule = {
|
|
2312
|
+
module: loadCodeResult.module,
|
|
2313
|
+
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
2314
|
+
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
2315
|
+
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
2316
|
+
details: loadCodeResult.details ?? codeDetails,
|
|
2317
|
+
};
|
|
2318
|
+
|
|
2319
|
+
const fluidExport: FluidObject<IProvideRuntimeFactory> | undefined =
|
|
2320
|
+
this._loadedModule.module.fluidExport;
|
|
2321
|
+
const runtimeFactory = fluidExport?.IRuntimeFactory;
|
|
2322
|
+
if (runtimeFactory === undefined) {
|
|
2323
|
+
throw new Error(packageNotFactoryError);
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
const getSpecifiedCodeDetails = () =>
|
|
2327
|
+
(this.protocolHandler.quorum.get("code") ??
|
|
2328
|
+
this.protocolHandler.quorum.get("code2")) as IFluidCodeDetails | undefined;
|
|
2329
|
+
|
|
2330
|
+
const context = new ContainerContext(
|
|
2331
|
+
this.options,
|
|
2221
2332
|
this.scope,
|
|
2222
|
-
this.codeLoader,
|
|
2223
|
-
codeDetails,
|
|
2224
2333
|
snapshot,
|
|
2225
|
-
|
|
2226
|
-
|
|
2334
|
+
this._loadedFromVersion,
|
|
2335
|
+
this._deltaManager,
|
|
2336
|
+
this.storageAdapter,
|
|
2337
|
+
this.protocolHandler.quorum,
|
|
2338
|
+
this.protocolHandler.audience,
|
|
2227
2339
|
loader,
|
|
2228
2340
|
(type, contents, batch, metadata) =>
|
|
2229
2341
|
this.submitContainerMessage(type, contents, batch, metadata),
|
|
@@ -2234,22 +2346,42 @@ export class Container
|
|
|
2234
2346
|
(message) => this.submitSignal(message),
|
|
2235
2347
|
(error?: ICriticalContainerError) => this.dispose(error),
|
|
2236
2348
|
(error?: ICriticalContainerError) => this.close(error),
|
|
2237
|
-
|
|
2238
|
-
|
|
2349
|
+
this.updateDirtyContainerState,
|
|
2350
|
+
this.getAbsoluteUrl,
|
|
2351
|
+
() => this.resolvedUrl?.id,
|
|
2352
|
+
() => this.clientId,
|
|
2353
|
+
() => this._deltaManager.serviceConfiguration,
|
|
2354
|
+
() => this.attachState,
|
|
2355
|
+
() => this.connected,
|
|
2356
|
+
getSpecifiedCodeDetails,
|
|
2357
|
+
this._deltaManager.clientDetails,
|
|
2239
2358
|
existing,
|
|
2359
|
+
this.subLogger,
|
|
2240
2360
|
pendingLocalState,
|
|
2241
2361
|
);
|
|
2362
|
+
this._lifecycleEvents.once("disposed", () => {
|
|
2363
|
+
context.dispose();
|
|
2364
|
+
});
|
|
2365
|
+
|
|
2366
|
+
this._runtime = await PerformanceEvent.timedExecAsync(
|
|
2367
|
+
this.subLogger,
|
|
2368
|
+
{ eventName: "InstantiateRuntime" },
|
|
2369
|
+
async () => runtimeFactory.instantiateRuntime(context, existing),
|
|
2370
|
+
);
|
|
2371
|
+
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
2372
|
+
|
|
2373
|
+
this._loadedCodeDetails = codeDetails;
|
|
2242
2374
|
|
|
2243
2375
|
this.emit("contextChanged", codeDetails);
|
|
2244
2376
|
}
|
|
2245
2377
|
|
|
2246
|
-
private updateDirtyContainerState(dirty: boolean) {
|
|
2378
|
+
private readonly updateDirtyContainerState = (dirty: boolean) => {
|
|
2247
2379
|
if (this._dirtyContainer === dirty) {
|
|
2248
2380
|
return;
|
|
2249
2381
|
}
|
|
2250
2382
|
this._dirtyContainer = dirty;
|
|
2251
2383
|
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
2252
|
-
}
|
|
2384
|
+
};
|
|
2253
2385
|
|
|
2254
2386
|
/**
|
|
2255
2387
|
* Set the connected state of the ContainerContext
|
|
@@ -2258,21 +2390,21 @@ export class Container
|
|
|
2258
2390
|
* @param readonly - Is the container in readonly mode?
|
|
2259
2391
|
*/
|
|
2260
2392
|
private setContextConnectedState(state: boolean, readonly: boolean): void {
|
|
2261
|
-
if (this.
|
|
2393
|
+
if (this._runtime?.disposed === false) {
|
|
2262
2394
|
/**
|
|
2263
2395
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
2264
2396
|
* ops getting through to the DeltaManager.
|
|
2265
2397
|
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
2266
2398
|
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
2267
2399
|
*/
|
|
2268
|
-
this.
|
|
2400
|
+
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
2269
2401
|
}
|
|
2270
2402
|
}
|
|
2271
2403
|
}
|
|
2272
2404
|
|
|
2273
2405
|
/**
|
|
2274
2406
|
* IContainer interface that includes experimental features still under development.
|
|
2275
|
-
* @
|
|
2407
|
+
* @experimental
|
|
2276
2408
|
*/
|
|
2277
2409
|
export interface IContainerExperimental extends IContainer {
|
|
2278
2410
|
/**
|
|
@@ -2283,5 +2415,13 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2283
2415
|
* @experimental misuse of this API can result in duplicate op submission and potential document corruption
|
|
2284
2416
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2285
2417
|
*/
|
|
2286
|
-
getPendingLocalState(): string;
|
|
2418
|
+
getPendingLocalState?(): string;
|
|
2419
|
+
|
|
2420
|
+
/**
|
|
2421
|
+
* Closes the container and returns serialized local state intended to be
|
|
2422
|
+
* given to a newly loaded container.
|
|
2423
|
+
* @experimental
|
|
2424
|
+
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2425
|
+
*/
|
|
2426
|
+
closeAndGetPendingLocalState(): string;
|
|
2287
2427
|
}
|