@fluidframework/container-loader 2.0.0-dev.4.4.0.162574 → 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 +59 -0
- package/README.md +27 -3
- 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 +3 -2
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +32 -13
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +18 -3
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +34 -9
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +99 -70
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +260 -218
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +24 -67
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +28 -217
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +3 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +29 -6
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +9 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +22 -9
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +42 -31
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +2 -3
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +2 -3
- package/dist/deltaQueue.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/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +9 -8
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +47 -61
- 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 +3 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -1
- package/dist/utils.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 +3 -2
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +33 -14
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +18 -3
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +35 -10
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +99 -70
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +264 -222
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +24 -67
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +28 -217
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +3 -3
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +29 -6
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +9 -3
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +22 -9
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +44 -33
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +2 -3
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -3
- package/lib/deltaQueue.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/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +9 -8
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +47 -61
- 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 +3 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +7 -1
- package/lib/utils.js.map +1 -1
- package/package.json +22 -20
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +40 -22
- package/src/connectionStateHandler.ts +66 -17
- package/src/container.ts +464 -292
- package/src/containerContext.ts +33 -341
- package/src/containerStorageAdapter.ts +40 -10
- package/src/contracts.ts +11 -3
- package/src/deltaManager.ts +74 -45
- package/src/deltaQueue.ts +2 -3
- package/src/disposal.ts +25 -0
- package/src/index.ts +1 -8
- package/src/loader.ts +85 -83
- 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 +4 -2
- package/src/utils.ts +15 -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/lib/container.js
CHANGED
|
@@ -5,30 +5,30 @@
|
|
|
5
5
|
// eslint-disable-next-line import/no-internal-modules
|
|
6
6
|
import merge from "lodash/merge";
|
|
7
7
|
import { v4 as uuid } from "uuid";
|
|
8
|
-
import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
|
|
8
|
+
import { TypedEventEmitter, assert, performance, unreachableCase, } from "@fluidframework/common-utils";
|
|
9
9
|
import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
|
|
10
10
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
11
|
-
import { readAndParse, OnlineStatus, isOnline,
|
|
11
|
+
import { readAndParse, OnlineStatus, isOnline, combineAppAndProtocolSummary, runWithRetry, isCombinedAppAndProtocolSummary, MessageType2, canBeCoalescedByService, } from "@fluidframework/driver-utils";
|
|
12
12
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
13
13
|
import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
14
14
|
import { Audience } from "./audience";
|
|
15
15
|
import { ContainerContext } from "./containerContext";
|
|
16
16
|
import { ReconnectMode, getPackageName } from "./contracts";
|
|
17
17
|
import { DeltaManager } from "./deltaManager";
|
|
18
|
-
import { DeltaManagerProxy } from "./deltaManagerProxy";
|
|
19
18
|
import { RelativeLoader } from "./loader";
|
|
20
19
|
import { pkgVersion } from "./packageVersion";
|
|
21
20
|
import { ContainerStorageAdapter, getBlobContentsFromTree, getBlobContentsFromTreeWithBlobContents, } from "./containerStorageAdapter";
|
|
22
21
|
import { createConnectionStateHandler } from "./connectionStateHandler";
|
|
23
22
|
import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
|
|
24
|
-
import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues
|
|
25
|
-
import {
|
|
23
|
+
import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues } from "./quorum";
|
|
24
|
+
import { NoopHeuristic } from "./noopHeuristic";
|
|
26
25
|
import { ConnectionManager } from "./connectionManager";
|
|
27
26
|
import { ConnectionState } from "./connectionState";
|
|
28
|
-
import { OnlyValidTermValue, ProtocolHandler, } from "./protocol";
|
|
27
|
+
import { OnlyValidTermValue, ProtocolHandler, protocolHandlerShouldProcessSignal, } from "./protocol";
|
|
29
28
|
const detachedContainerRefSeqNumber = 0;
|
|
30
29
|
const dirtyContainerEvent = "dirty";
|
|
31
30
|
const savedContainerEvent = "saved";
|
|
31
|
+
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
32
32
|
/**
|
|
33
33
|
* Waits until container connects to delta storage and gets up-to-date.
|
|
34
34
|
*
|
|
@@ -119,26 +119,18 @@ export async function ReportIfTooLong(logger, eventName, action) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
const summarizerClientType = "summarizer";
|
|
122
|
-
/**
|
|
123
|
-
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
124
|
-
*/
|
|
125
122
|
export class Container extends EventEmitterWithErrorHandling {
|
|
126
123
|
/**
|
|
127
124
|
* @internal
|
|
128
125
|
*/
|
|
129
|
-
constructor(
|
|
130
|
-
var _a
|
|
126
|
+
constructor(createProps, loadProps) {
|
|
127
|
+
var _a;
|
|
131
128
|
super((name, error) => {
|
|
132
129
|
this.mc.logger.sendErrorEvent({
|
|
133
130
|
eventName: "ContainerEventHandlerException",
|
|
134
131
|
name: typeof name === "string" ? name : undefined,
|
|
135
132
|
}, error);
|
|
136
133
|
});
|
|
137
|
-
this.loader = loader;
|
|
138
|
-
this.protocolHandlerBuilder = protocolHandlerBuilder;
|
|
139
|
-
// Tells if container can reconnect on losing fist connection
|
|
140
|
-
// If false, container gets closed on loss of connection.
|
|
141
|
-
this._canReconnect = true;
|
|
142
134
|
/**
|
|
143
135
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
144
136
|
*
|
|
@@ -164,28 +156,58 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
164
156
|
this.attachStarted = false;
|
|
165
157
|
this._dirtyContainer = false;
|
|
166
158
|
this.savedOps = [];
|
|
159
|
+
this.clientsWhoShouldHaveLeft = new Set();
|
|
167
160
|
this.setAutoReconnectTime = performance.now();
|
|
161
|
+
this._lifecycleEvents = new TypedEventEmitter();
|
|
168
162
|
this._disposed = false;
|
|
169
|
-
this.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
164
|
+
if (this.resolvedUrl === undefined) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
|
|
168
|
+
};
|
|
169
|
+
this.updateDirtyContainerState = (dirty) => {
|
|
170
|
+
if (this._dirtyContainer === dirty) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this._dirtyContainer = dirty;
|
|
174
|
+
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
175
|
+
};
|
|
176
|
+
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
177
|
+
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
178
|
+
const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
|
|
179
|
+
this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
|
|
180
|
+
this.clientDetailsOverride = clientDetailsOverride;
|
|
181
|
+
this.urlResolver = urlResolver;
|
|
182
|
+
this.serviceFactory = documentServiceFactory;
|
|
183
|
+
this.codeLoader = codeLoader;
|
|
184
|
+
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
185
|
+
// all clients that were loaded from the same loader (including summarizer clients).
|
|
186
|
+
// Tracking alternative ways to handle this in AB#4129.
|
|
187
|
+
this.options = Object.assign({}, options);
|
|
188
|
+
this.scope = scope;
|
|
189
|
+
this.detachedBlobStorage = detachedBlobStorage;
|
|
190
|
+
this.protocolHandlerBuilder =
|
|
191
|
+
protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
192
|
+
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
193
|
+
this.clone = async (_loadProps, createParamOverrides) => {
|
|
194
|
+
return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
|
|
195
|
+
};
|
|
174
196
|
// Create logger for data stores to use
|
|
175
197
|
const type = this.client.details.type;
|
|
176
198
|
const interactive = this.client.details.capabilities.interactive;
|
|
177
199
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
|
|
178
200
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
179
201
|
// We assign the id later so property getter is used.
|
|
180
|
-
this.subLogger = ChildLogger.create(
|
|
202
|
+
this.subLogger = ChildLogger.create(subLogger, undefined, {
|
|
181
203
|
all: {
|
|
182
204
|
clientType,
|
|
183
205
|
containerId: uuid(),
|
|
184
|
-
docId: () => { var _a
|
|
206
|
+
docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
|
|
185
207
|
containerAttachState: () => this._attachState,
|
|
186
208
|
containerLifecycleState: () => this._lifecycleState,
|
|
187
209
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
188
|
-
serializedContainer:
|
|
210
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
189
211
|
},
|
|
190
212
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
191
213
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -195,23 +217,24 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
195
217
|
dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
|
|
196
218
|
dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
|
|
197
219
|
dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
|
|
198
|
-
containerLoadedFromVersionId: () => { var _a; return (_a = this.
|
|
199
|
-
containerLoadedFromVersionDate: () => { var _a; return (_a = this.
|
|
220
|
+
containerLoadedFromVersionId: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
|
|
221
|
+
containerLoadedFromVersionDate: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
|
|
200
222
|
// message information to associate errors with the specific execution state
|
|
201
223
|
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
202
224
|
dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
|
|
203
225
|
dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
|
|
204
|
-
dmLastMsqSeqClientId: () => {
|
|
226
|
+
dmLastMsqSeqClientId: () => {
|
|
227
|
+
var _a, _b, _c, _d;
|
|
228
|
+
return ((_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId) === null
|
|
229
|
+
? "null"
|
|
230
|
+
: (_d = (_c = this.deltaManager) === null || _c === void 0 ? void 0 : _c.lastMessage) === null || _d === void 0 ? void 0 : _d.clientId;
|
|
231
|
+
},
|
|
205
232
|
dmLastMsgClientSeq: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientSequenceNumber; },
|
|
206
233
|
connectionStateDuration: () => performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
207
234
|
},
|
|
208
235
|
});
|
|
209
236
|
// Prefix all events in this file with container-loader
|
|
210
237
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
211
|
-
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
212
|
-
// all clients that were loaded from the same loader (including summarizer clients).
|
|
213
|
-
// Tracking alternative ways to handle this in AB#4129.
|
|
214
|
-
this.options = Object.assign({}, this.loader.services.options);
|
|
215
238
|
this._deltaManager = this.createDeltaManager();
|
|
216
239
|
this.connectionStateHandler = createConnectionStateHandler({
|
|
217
240
|
logger: this.mc.logger,
|
|
@@ -227,7 +250,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
227
250
|
}
|
|
228
251
|
},
|
|
229
252
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
230
|
-
maxClientLeaveWaitTime:
|
|
253
|
+
maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
|
|
231
254
|
logConnectionIssue: (eventName, category, details) => {
|
|
232
255
|
const mode = this.connectionMode;
|
|
233
256
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
@@ -251,7 +274,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
251
274
|
this.connect();
|
|
252
275
|
}
|
|
253
276
|
},
|
|
254
|
-
|
|
277
|
+
clientShouldHaveLeft: (clientId) => {
|
|
278
|
+
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
279
|
+
},
|
|
280
|
+
}, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
|
|
255
281
|
this.on(savedContainerEvent, () => {
|
|
256
282
|
this.connectionStateHandler.containerSaved();
|
|
257
283
|
});
|
|
@@ -263,8 +289,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
263
289
|
: combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
|
|
264
290
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
265
291
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
266
|
-
const forceEnableSummarizeProtocolTree = (
|
|
267
|
-
this.storageAdapter = new ContainerStorageAdapter(
|
|
292
|
+
const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
|
|
293
|
+
this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
268
294
|
const isDomAvailable = typeof document === "object" &&
|
|
269
295
|
document !== null &&
|
|
270
296
|
typeof document.addEventListener === "function" &&
|
|
@@ -290,33 +316,28 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
290
316
|
* Load an existing container.
|
|
291
317
|
* @internal
|
|
292
318
|
*/
|
|
293
|
-
static async load(
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
canReconnect: loadOptions.canReconnect,
|
|
298
|
-
serializedContainerState: pendingLocalState,
|
|
299
|
-
}, protocolHandlerBuilder);
|
|
319
|
+
static async load(loadProps, createProps) {
|
|
320
|
+
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
321
|
+
const container = new Container(createProps, loadProps);
|
|
322
|
+
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
300
323
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
301
|
-
var _a, _b;
|
|
302
|
-
const version = loadOptions.version;
|
|
303
324
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
304
325
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
305
326
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
306
327
|
const mode = pendingLocalState
|
|
307
|
-
? Object.assign(Object.assign({}, (
|
|
328
|
+
? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
|
|
308
329
|
const onClosed = (err) => {
|
|
309
330
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
310
331
|
reject(err !== null && err !== void 0 ? err : new GenericError("Container closed without error during load"));
|
|
311
332
|
};
|
|
312
333
|
container.on("closed", onClosed);
|
|
313
334
|
container
|
|
314
|
-
.load(version, mode, pendingLocalState)
|
|
335
|
+
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
315
336
|
.finally(() => {
|
|
316
337
|
container.removeListener("closed", onClosed);
|
|
317
338
|
})
|
|
318
339
|
.then((props) => {
|
|
319
|
-
event.end(Object.assign(Object.assign({}, props),
|
|
340
|
+
event.end(Object.assign(Object.assign({}, props), loadMode));
|
|
320
341
|
resolve(container);
|
|
321
342
|
}, (error) => {
|
|
322
343
|
const err = normalizeError(error);
|
|
@@ -326,13 +347,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
326
347
|
container.close(err);
|
|
327
348
|
onClosed(err);
|
|
328
349
|
});
|
|
329
|
-
}), { start: true, end: true, cancel: "generic" });
|
|
350
|
+
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
330
351
|
}
|
|
331
352
|
/**
|
|
332
353
|
* Create a new container in a detached state.
|
|
333
354
|
*/
|
|
334
|
-
static async createDetached(
|
|
335
|
-
const container = new Container(
|
|
355
|
+
static async createDetached(createProps, codeDetails) {
|
|
356
|
+
const container = new Container(createProps);
|
|
336
357
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
337
358
|
await container.createDetached(codeDetails);
|
|
338
359
|
return container;
|
|
@@ -342,8 +363,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
342
363
|
* Create a new container in a detached state that is initialized with a
|
|
343
364
|
* snapshot from a previous detached container.
|
|
344
365
|
*/
|
|
345
|
-
static async rehydrateDetachedFromSnapshot(
|
|
346
|
-
const container = new Container(
|
|
366
|
+
static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
|
|
367
|
+
const container = new Container(createProps);
|
|
347
368
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
348
369
|
const deserializedSummary = JSON.parse(snapshot);
|
|
349
370
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
@@ -365,14 +386,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
365
386
|
this._lifecycleState === "disposing" ||
|
|
366
387
|
this._lifecycleState === "disposed");
|
|
367
388
|
}
|
|
368
|
-
get
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
get context() {
|
|
372
|
-
if (this._context === undefined) {
|
|
373
|
-
throw new GenericError("Attempted to access context before it was defined");
|
|
389
|
+
get runtime() {
|
|
390
|
+
if (this._runtime === undefined) {
|
|
391
|
+
throw new Error("Attempted to access runtime before it was defined");
|
|
374
392
|
}
|
|
375
|
-
return this.
|
|
393
|
+
return this._runtime;
|
|
376
394
|
}
|
|
377
395
|
get protocolHandler() {
|
|
378
396
|
if (this._protocolHandler === undefined) {
|
|
@@ -387,17 +405,23 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
387
405
|
return this;
|
|
388
406
|
}
|
|
389
407
|
get resolvedUrl() {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
408
|
+
var _a;
|
|
409
|
+
/**
|
|
410
|
+
* All attached containers will have a document service,
|
|
411
|
+
* this is required, as attached containers are attached to
|
|
412
|
+
* a service. Detached containers will neither have a document
|
|
413
|
+
* service or a resolved url as they only exist locally.
|
|
414
|
+
* in order to create a document service a resolved url must
|
|
415
|
+
* first be obtained, this is how the container is identified.
|
|
416
|
+
* Because of this, the document service's resolved url
|
|
417
|
+
* is always the same as the containers, as we had to
|
|
418
|
+
* obtain the resolved url, and then create the service from it.
|
|
419
|
+
*/
|
|
420
|
+
return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
|
|
394
421
|
}
|
|
395
422
|
get readOnlyInfo() {
|
|
396
423
|
return this._deltaManager.readOnlyInfo;
|
|
397
424
|
}
|
|
398
|
-
get closeSignal() {
|
|
399
|
-
return this._deltaManager.closeAbortController.signal;
|
|
400
|
-
}
|
|
401
425
|
/**
|
|
402
426
|
* Tracks host requiring read-only mode.
|
|
403
427
|
*/
|
|
@@ -413,13 +437,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
413
437
|
get connected() {
|
|
414
438
|
return this.connectionStateHandler.connectionState === ConnectionState.Connected;
|
|
415
439
|
}
|
|
416
|
-
/**
|
|
417
|
-
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
418
|
-
* configuration details returned as part of the initial connection.
|
|
419
|
-
*/
|
|
420
|
-
get serviceConfiguration() {
|
|
421
|
-
return this._deltaManager.serviceConfiguration;
|
|
422
|
-
}
|
|
423
440
|
/**
|
|
424
441
|
* The server provided id of the client.
|
|
425
442
|
* Set once this.connected is true, otherwise undefined
|
|
@@ -427,21 +444,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
427
444
|
get clientId() {
|
|
428
445
|
return this._clientId;
|
|
429
446
|
}
|
|
430
|
-
/**
|
|
431
|
-
* The server provided claims of the client.
|
|
432
|
-
* Set once this.connected is true, otherwise undefined
|
|
433
|
-
*/
|
|
434
|
-
get scopes() {
|
|
435
|
-
return this._deltaManager.connectionManager.scopes;
|
|
436
|
-
}
|
|
437
|
-
get clientDetails() {
|
|
438
|
-
return this._deltaManager.clientDetails;
|
|
439
|
-
}
|
|
440
447
|
get offlineLoadEnabled() {
|
|
441
448
|
var _a, _b;
|
|
442
449
|
const enabled = (_a = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) !== null && _a !== void 0 ? _a : ((_b = this.options) === null || _b === void 0 ? void 0 : _b.enableOfflineLoad) === true;
|
|
443
450
|
// summarizer will not have any pending state we want to save
|
|
444
|
-
return enabled && this.clientDetails.capabilities.interactive;
|
|
451
|
+
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
445
452
|
}
|
|
446
453
|
/**
|
|
447
454
|
* Get the code details that are currently specified for the container.
|
|
@@ -456,8 +463,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
456
463
|
* loaded.
|
|
457
464
|
*/
|
|
458
465
|
getLoadedCodeDetails() {
|
|
459
|
-
|
|
460
|
-
return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
|
|
466
|
+
return this._loadedCodeDetails;
|
|
461
467
|
}
|
|
462
468
|
/**
|
|
463
469
|
* Retrieves the audience associated with the document
|
|
@@ -473,49 +479,31 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
473
479
|
get isDirty() {
|
|
474
480
|
return this._dirtyContainer;
|
|
475
481
|
}
|
|
476
|
-
get serviceFactory() {
|
|
477
|
-
return this.loader.services.documentServiceFactory;
|
|
478
|
-
}
|
|
479
|
-
get urlResolver() {
|
|
480
|
-
return this.loader.services.urlResolver;
|
|
481
|
-
}
|
|
482
|
-
get scope() {
|
|
483
|
-
return this.loader.services.scope;
|
|
484
|
-
}
|
|
485
|
-
get codeLoader() {
|
|
486
|
-
return this.loader.services.codeLoader;
|
|
487
|
-
}
|
|
488
482
|
/**
|
|
489
483
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
490
484
|
*/
|
|
491
485
|
async getEntryPoint() {
|
|
492
486
|
var _a, _b;
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (this.
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
while (this._context === undefined) {
|
|
500
|
-
await new Promise((resolve, reject) => {
|
|
501
|
-
const contextChangedHandler = () => {
|
|
502
|
-
resolve();
|
|
503
|
-
this.off("disposed", disposedHandler);
|
|
504
|
-
};
|
|
505
|
-
const disposedHandler = (error) => {
|
|
506
|
-
reject(error !== null && error !== void 0 ? error : "The Container is disposed");
|
|
507
|
-
this.off("contextChanged", contextChangedHandler);
|
|
508
|
-
};
|
|
509
|
-
this.once("contextChanged", contextChangedHandler);
|
|
510
|
-
this.once("disposed", disposedHandler);
|
|
511
|
-
});
|
|
512
|
-
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
513
|
-
// should have set this._context; making sure.
|
|
514
|
-
assert(this._context !== undefined, 0x5a2 /* Context still not defined after contextChanged event */);
|
|
487
|
+
if (this._disposed) {
|
|
488
|
+
throw new UsageError("The context is already disposed");
|
|
489
|
+
}
|
|
490
|
+
if (this._runtime !== undefined) {
|
|
491
|
+
return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
515
492
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
493
|
+
return new Promise((resolve, reject) => {
|
|
494
|
+
const runtimeInstantiatedHandler = () => {
|
|
495
|
+
var _a, _b;
|
|
496
|
+
assert(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
497
|
+
resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
498
|
+
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
499
|
+
};
|
|
500
|
+
const disposedHandler = () => {
|
|
501
|
+
reject(new Error("ContainerContext was disposed"));
|
|
502
|
+
this._lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
503
|
+
};
|
|
504
|
+
this._lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
505
|
+
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
506
|
+
});
|
|
519
507
|
}
|
|
520
508
|
/**
|
|
521
509
|
* Retrieves the quorum associated with the document
|
|
@@ -524,7 +512,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
524
512
|
return this.protocolHandler.quorum;
|
|
525
513
|
}
|
|
526
514
|
dispose(error) {
|
|
527
|
-
this._deltaManager.
|
|
515
|
+
this._deltaManager.dispose(error);
|
|
528
516
|
this.verifyClosed();
|
|
529
517
|
}
|
|
530
518
|
close(error) {
|
|
@@ -540,7 +528,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
540
528
|
assert(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
541
529
|
}
|
|
542
530
|
closeCore(error) {
|
|
543
|
-
var _a
|
|
531
|
+
var _a;
|
|
544
532
|
assert(!this.closed, 0x315 /* re-entrancy */);
|
|
545
533
|
try {
|
|
546
534
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -558,12 +546,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
558
546
|
this._lifecycleState = "closing";
|
|
559
547
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
560
548
|
this.connectionStateHandler.dispose();
|
|
561
|
-
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
562
|
-
this.storageAdapter.dispose();
|
|
563
|
-
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
564
|
-
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
565
|
-
// Driver need to ensure all caches are cleared on critical errors
|
|
566
|
-
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
567
549
|
}
|
|
568
550
|
catch (exception) {
|
|
569
551
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
@@ -575,6 +557,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
575
557
|
}
|
|
576
558
|
finally {
|
|
577
559
|
this._lifecycleState = "closed";
|
|
560
|
+
// There is no user for summarizer, so we need to ensure dispose is called
|
|
561
|
+
if (this.client.details.type === summarizerClientType) {
|
|
562
|
+
this.dispose(error);
|
|
563
|
+
}
|
|
578
564
|
}
|
|
579
565
|
}
|
|
580
566
|
disposeCore(error) {
|
|
@@ -588,7 +574,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
588
574
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
589
575
|
this.mc.logger.sendTelemetryEvent({
|
|
590
576
|
eventName: "ContainerDispose",
|
|
591
|
-
|
|
577
|
+
// Only log error if container isn't closed
|
|
578
|
+
category: !this.closed && error !== undefined ? "error" : "generic",
|
|
592
579
|
}, error);
|
|
593
580
|
// ! Progressing from "closed" to "disposing" is not allowed
|
|
594
581
|
if (this._lifecycleState !== "closed") {
|
|
@@ -596,7 +583,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
596
583
|
}
|
|
597
584
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
598
585
|
this.connectionStateHandler.dispose();
|
|
599
|
-
|
|
586
|
+
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
587
|
+
(_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
|
|
600
588
|
this.storageAdapter.dispose();
|
|
601
589
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
602
590
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -614,6 +602,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
614
602
|
}
|
|
615
603
|
finally {
|
|
616
604
|
this._lifecycleState = "disposed";
|
|
605
|
+
this._lifecycleEvents.emit("disposed");
|
|
617
606
|
}
|
|
618
607
|
}
|
|
619
608
|
closeAndGetPendingLocalState() {
|
|
@@ -628,12 +617,15 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
628
617
|
if (!this.offlineLoadEnabled) {
|
|
629
618
|
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
630
619
|
}
|
|
620
|
+
if (this.closed || this._disposed) {
|
|
621
|
+
throw new UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
622
|
+
}
|
|
631
623
|
assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
632
624
|
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
633
625
|
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
634
626
|
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
635
627
|
const pendingState = {
|
|
636
|
-
pendingRuntimeState: this.
|
|
628
|
+
pendingRuntimeState: this.runtime.getPendingLocalState(),
|
|
637
629
|
baseSnapshot: this.baseSnapshot,
|
|
638
630
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
639
631
|
savedOps: this.savedOps,
|
|
@@ -649,11 +641,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
649
641
|
}
|
|
650
642
|
serialize() {
|
|
651
643
|
assert(this.attachState === AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
652
|
-
const appSummary = this.
|
|
644
|
+
const appSummary = this.runtime.createSummary();
|
|
653
645
|
const protocolSummary = this.captureProtocolSummary();
|
|
654
646
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
655
|
-
if (this.
|
|
656
|
-
this.loader.services.detachedBlobStorage.size > 0) {
|
|
647
|
+
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
657
648
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
658
649
|
type: SummaryType.Blob,
|
|
659
650
|
content: "true",
|
|
@@ -672,15 +663,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
672
663
|
assert(this._attachState === AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
673
664
|
this.attachStarted = true;
|
|
674
665
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
675
|
-
const hasAttachmentBlobs = this.
|
|
676
|
-
this.loader.services.detachedBlobStorage.size > 0;
|
|
666
|
+
const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
677
667
|
try {
|
|
678
668
|
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
679
669
|
let summary;
|
|
680
670
|
if (!hasAttachmentBlobs) {
|
|
681
671
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
682
672
|
// semantics around what the attach means as far as async code goes.
|
|
683
|
-
const appSummary = this.
|
|
673
|
+
const appSummary = this.runtime.createSummary();
|
|
684
674
|
const protocolSummary = this.captureProtocolSummary();
|
|
685
675
|
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
686
676
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
@@ -688,6 +678,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
688
678
|
// starting to attach the container to storage.
|
|
689
679
|
// Also, this should only be fired in detached container.
|
|
690
680
|
this._attachState = AttachState.Attaching;
|
|
681
|
+
this.runtime.setAttachState(AttachState.Attaching);
|
|
691
682
|
this.emit("attaching");
|
|
692
683
|
if (this.offlineLoadEnabled) {
|
|
693
684
|
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
@@ -697,40 +688,38 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
697
688
|
}
|
|
698
689
|
}
|
|
699
690
|
// Actually go and create the resolved document
|
|
700
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
701
|
-
ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
702
691
|
if (this.service === undefined) {
|
|
703
|
-
|
|
692
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
693
|
+
assert(this.client.details.type !== summarizerClientType &&
|
|
694
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
704
695
|
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
705
|
-
cancel: this.
|
|
696
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
706
697
|
});
|
|
707
698
|
}
|
|
708
|
-
const resolvedUrl = this.service.resolvedUrl;
|
|
709
|
-
ensureFluidResolvedUrl(resolvedUrl);
|
|
710
|
-
this._resolvedUrl = resolvedUrl;
|
|
711
699
|
await this.storageAdapter.connectToService(this.service);
|
|
712
700
|
if (hasAttachmentBlobs) {
|
|
713
701
|
// upload blobs to storage
|
|
714
|
-
assert(!!this.
|
|
702
|
+
assert(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
715
703
|
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
716
704
|
// support blob handles that only know about the local IDs
|
|
717
705
|
const redirectTable = new Map();
|
|
718
706
|
// if new blobs are added while uploading, upload them too
|
|
719
|
-
while (redirectTable.size < this.
|
|
720
|
-
const newIds = this.
|
|
707
|
+
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
708
|
+
const newIds = this.detachedBlobStorage
|
|
721
709
|
.getBlobIds()
|
|
722
710
|
.filter((id) => !redirectTable.has(id));
|
|
723
711
|
for (const id of newIds) {
|
|
724
|
-
const blob = await this.
|
|
712
|
+
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
725
713
|
const response = await this.storageAdapter.createBlob(blob);
|
|
726
714
|
redirectTable.set(id, response.id);
|
|
727
715
|
}
|
|
728
716
|
}
|
|
729
717
|
// take summary and upload
|
|
730
|
-
const appSummary = this.
|
|
718
|
+
const appSummary = this.runtime.createSummary(redirectTable);
|
|
731
719
|
const protocolSummary = this.captureProtocolSummary();
|
|
732
720
|
summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
733
721
|
this._attachState = AttachState.Attaching;
|
|
722
|
+
this.runtime.setAttachState(AttachState.Attaching);
|
|
734
723
|
this.emit("attaching");
|
|
735
724
|
if (this.offlineLoadEnabled) {
|
|
736
725
|
const snapshot = getSnapshotTreeFromSerializedContainer(summary);
|
|
@@ -745,6 +734,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
745
734
|
});
|
|
746
735
|
}
|
|
747
736
|
this._attachState = AttachState.Attached;
|
|
737
|
+
this.runtime.setAttachState(AttachState.Attached);
|
|
748
738
|
this.emit("attached");
|
|
749
739
|
if (!this.closed) {
|
|
750
740
|
this.resumeInternal({
|
|
@@ -756,18 +746,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
756
746
|
catch (error) {
|
|
757
747
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
758
748
|
const newError = normalizeError(error);
|
|
759
|
-
|
|
760
|
-
if (isFluidResolvedUrl(resolvedUrl)) {
|
|
761
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
762
|
-
}
|
|
749
|
+
newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
|
|
763
750
|
this.close(newError);
|
|
764
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
765
751
|
throw newError;
|
|
766
752
|
}
|
|
767
753
|
}, { start: true, end: true, cancel: "generic" });
|
|
768
754
|
}
|
|
769
755
|
async request(path) {
|
|
770
|
-
return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.
|
|
756
|
+
return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
771
757
|
}
|
|
772
758
|
setAutoReconnectInternal(mode) {
|
|
773
759
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
@@ -833,13 +819,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
833
819
|
// Ensure connection to web socket
|
|
834
820
|
this.connectToDeltaStream(args);
|
|
835
821
|
}
|
|
836
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
837
|
-
var _a;
|
|
838
|
-
if (this.resolvedUrl === undefined) {
|
|
839
|
-
return undefined;
|
|
840
|
-
}
|
|
841
|
-
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
|
|
842
|
-
}
|
|
843
822
|
async proposeCodeDetails(codeDetails) {
|
|
844
823
|
if (!isFluidCodeDetails(codeDetails)) {
|
|
845
824
|
throw new Error("Provided codeDetails are not IFluidCodeDetails");
|
|
@@ -856,13 +835,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
856
835
|
.catch(() => false);
|
|
857
836
|
}
|
|
858
837
|
async processCodeProposal() {
|
|
859
|
-
var _a;
|
|
860
838
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
861
839
|
await Promise.all([
|
|
862
840
|
this.deltaManager.inbound.pause(),
|
|
863
841
|
this.deltaManager.inboundSignal.pause(),
|
|
864
842
|
]);
|
|
865
|
-
if ((await this.
|
|
843
|
+
if ((await this.satisfies(codeDetails)) === true) {
|
|
866
844
|
this.deltaManager.inbound.resume();
|
|
867
845
|
this.deltaManager.inboundSignal.resume();
|
|
868
846
|
return;
|
|
@@ -870,19 +848,44 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
870
848
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
871
849
|
const error = new GenericError("Existing context does not satisfy incoming proposal");
|
|
872
850
|
this.close(error);
|
|
873
|
-
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
854
|
+
*/
|
|
855
|
+
async satisfies(constraintCodeDetails) {
|
|
856
|
+
var _a, _b;
|
|
857
|
+
// If we have no module, it can't satisfy anything.
|
|
858
|
+
if (this._loadedModule === undefined) {
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
const comparers = [];
|
|
862
|
+
const maybeCompareCodeLoader = this.codeLoader;
|
|
863
|
+
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
864
|
+
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
865
|
+
}
|
|
866
|
+
const maybeCompareExport = (_a = this._loadedModule) === null || _a === void 0 ? void 0 : _a.module.fluidExport;
|
|
867
|
+
if ((maybeCompareExport === null || maybeCompareExport === void 0 ? void 0 : maybeCompareExport.IFluidCodeDetailsComparer) !== undefined) {
|
|
868
|
+
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
869
|
+
}
|
|
870
|
+
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
871
|
+
// the incoming constraint, so we return false. Assuming it does not satisfy is safer, to force a reload
|
|
872
|
+
// rather than potentially running with incompatible code.
|
|
873
|
+
if (comparers.length === 0) {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
for (const comparer of comparers) {
|
|
877
|
+
const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
|
|
878
|
+
if (satisfies === false) {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return true;
|
|
874
883
|
}
|
|
875
884
|
async getVersion(version) {
|
|
876
885
|
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
877
886
|
return versions[0];
|
|
878
887
|
}
|
|
879
|
-
recordConnectStartTime() {
|
|
880
|
-
if (this.connectionTransitionTimes[ConnectionState.Disconnected] === undefined) {
|
|
881
|
-
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
888
|
connectToDeltaStream(args) {
|
|
885
|
-
this.recordConnectStartTime();
|
|
886
889
|
// All agents need "write" access, including summarizer.
|
|
887
890
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
888
891
|
args.mode = "write";
|
|
@@ -894,12 +897,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
894
897
|
*
|
|
895
898
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
896
899
|
*/
|
|
897
|
-
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
898
|
-
var _a;
|
|
899
|
-
|
|
900
|
-
throw new Error("Attempting to load without a resolved url");
|
|
901
|
-
}
|
|
902
|
-
this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
900
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
901
|
+
var _a, _b, _c;
|
|
902
|
+
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
903
903
|
// Ideally we always connect as "read" by default.
|
|
904
904
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
905
905
|
// We should not rely on it by (one of them will address the issue, but we need to address both)
|
|
@@ -925,9 +925,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
925
925
|
else {
|
|
926
926
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
927
927
|
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
928
|
-
var _a;
|
|
929
928
|
this.close(error);
|
|
930
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
931
929
|
});
|
|
932
930
|
}
|
|
933
931
|
this._attachState = AttachState.Attached;
|
|
@@ -944,7 +942,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
944
942
|
if (this.offlineLoadEnabled) {
|
|
945
943
|
this.baseSnapshot = snapshot;
|
|
946
944
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
947
|
-
this.baseSnapshotBlobs = await getBlobContentsFromTree(snapshot, this.
|
|
945
|
+
this.baseSnapshotBlobs = await getBlobContentsFromTree(snapshot, this.storageAdapter);
|
|
948
946
|
}
|
|
949
947
|
}
|
|
950
948
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
@@ -980,7 +978,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
980
978
|
for (const message of pendingLocalState.savedOps) {
|
|
981
979
|
this.processRemoteMessage(message);
|
|
982
980
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
983
|
-
await this.
|
|
981
|
+
await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
|
|
984
982
|
}
|
|
985
983
|
pendingLocalState.savedOps = [];
|
|
986
984
|
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
@@ -1053,8 +1051,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1053
1051
|
}
|
|
1054
1052
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
1055
1053
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1056
|
-
assert(!!this.
|
|
1057
|
-
this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1054
|
+
assert(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1058
1055
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1059
1056
|
}
|
|
1060
1057
|
const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
|
|
@@ -1107,9 +1104,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1107
1104
|
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
1108
1105
|
}
|
|
1109
1106
|
initializeProtocolState(attributes, quorumSnapshot) {
|
|
1110
|
-
|
|
1111
|
-
const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
1112
|
-
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
|
|
1107
|
+
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
|
|
1113
1108
|
const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
1114
1109
|
protocol.quorum.on("error", (error) => {
|
|
1115
1110
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -1129,10 +1124,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1129
1124
|
});
|
|
1130
1125
|
}
|
|
1131
1126
|
this.processCodeProposal().catch((error) => {
|
|
1132
|
-
var _a;
|
|
1133
1127
|
const normalizedError = normalizeError(error);
|
|
1134
1128
|
this.close(normalizedError);
|
|
1135
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
|
|
1136
1129
|
throw error;
|
|
1137
1130
|
});
|
|
1138
1131
|
}
|
|
@@ -1216,9 +1209,15 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1216
1209
|
assert(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1217
1210
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1218
1211
|
});
|
|
1212
|
+
deltaManager.on("establishingConnection", (reason) => {
|
|
1213
|
+
this.connectionStateHandler.establishingConnection(reason);
|
|
1214
|
+
});
|
|
1215
|
+
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1216
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1217
|
+
});
|
|
1219
1218
|
deltaManager.on("disconnect", (reason, error) => {
|
|
1220
1219
|
var _a;
|
|
1221
|
-
(_a = this.
|
|
1220
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1222
1221
|
if (!this.closed) {
|
|
1223
1222
|
this.connectionStateHandler.receivedDisconnectEvent(reason, error);
|
|
1224
1223
|
}
|
|
@@ -1272,10 +1271,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1272
1271
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
1273
1272
|
durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1274
1273
|
}
|
|
1275
|
-
else {
|
|
1276
|
-
// This info is of most
|
|
1274
|
+
else if (value === ConnectionState.CatchingUp) {
|
|
1275
|
+
// This info is of most interesting while Catching Up.
|
|
1277
1276
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1278
|
-
|
|
1277
|
+
// Need to check that we have already loaded and fetched the snapshot.
|
|
1278
|
+
if (this.deltaManager.hasCheckpointSequenceNumber &&
|
|
1279
|
+
this._lifecycleState === "loaded") {
|
|
1279
1280
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1280
1281
|
}
|
|
1281
1282
|
}
|
|
@@ -1322,7 +1323,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1322
1323
|
}
|
|
1323
1324
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1324
1325
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1325
|
-
var _a;
|
|
1326
1326
|
switch (type) {
|
|
1327
1327
|
case MessageType.Operation:
|
|
1328
1328
|
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
@@ -1331,7 +1331,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1331
1331
|
default: {
|
|
1332
1332
|
const newError = new GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1333
1333
|
this.close(newError);
|
|
1334
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
1335
1334
|
return -1;
|
|
1336
1335
|
}
|
|
1337
1336
|
}
|
|
@@ -1364,7 +1363,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1364
1363
|
return -1;
|
|
1365
1364
|
}
|
|
1366
1365
|
this.messageCountAfterDisconnection += 1;
|
|
1367
|
-
(_a = this.
|
|
1366
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
|
|
1368
1367
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1369
1368
|
}
|
|
1370
1369
|
processRemoteMessage(message) {
|
|
@@ -1372,24 +1371,51 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1372
1371
|
this.savedOps.push(message);
|
|
1373
1372
|
}
|
|
1374
1373
|
const local = this.clientId === message.clientId;
|
|
1374
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
1375
|
+
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1376
|
+
if (message.clientId != null) {
|
|
1377
|
+
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1378
|
+
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
1379
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1380
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1381
|
+
}
|
|
1382
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1383
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1384
|
+
// document we don't need to blow up aggressively.
|
|
1385
|
+
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1386
|
+
!canBeCoalescedByService(message)) {
|
|
1387
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1388
|
+
throw new Error("Remote message's clientId already should have left");
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1375
1391
|
// Allow the protocol handler to process the message
|
|
1376
1392
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1377
1393
|
// Forward messages to the loaded runtime for processing
|
|
1378
|
-
this.
|
|
1394
|
+
this.runtime.process(message, local);
|
|
1379
1395
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1380
1396
|
if (this.activeConnection()) {
|
|
1381
|
-
if (this.
|
|
1397
|
+
if (this.noopHeuristic === undefined) {
|
|
1398
|
+
const serviceConfiguration = this.deltaManager.serviceConfiguration;
|
|
1382
1399
|
// Note that config from first connection will be used for this container's lifetime.
|
|
1383
1400
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1384
1401
|
// clients.
|
|
1385
1402
|
// All existing will continue to use settings they got earlier.
|
|
1386
|
-
assert(
|
|
1387
|
-
this.
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1403
|
+
assert(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1404
|
+
this.noopHeuristic = new NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
|
|
1405
|
+
this.noopHeuristic.on("wantsNoop", () => {
|
|
1406
|
+
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
1407
|
+
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
1408
|
+
// running the microtask that the heuristic queued in response.
|
|
1409
|
+
assert(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
|
|
1410
|
+
this.submitMessage(MessageType.NoOp);
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
this.noopHeuristic.notifyMessageProcessed(message);
|
|
1414
|
+
// The contract with the protocolHandler is that returning "immediateNoOp" is equivalent to "please immediately accept the proposal I just processed".
|
|
1415
|
+
if (result.immediateNoOp === true) {
|
|
1416
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
1417
|
+
this.submitMessage(MessageType2.Accept);
|
|
1391
1418
|
}
|
|
1392
|
-
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1393
1419
|
}
|
|
1394
1420
|
this.emit("op", message);
|
|
1395
1421
|
}
|
|
@@ -1398,12 +1424,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1398
1424
|
}
|
|
1399
1425
|
processSignal(message) {
|
|
1400
1426
|
// No clientId indicates a system signal message.
|
|
1401
|
-
if (message
|
|
1427
|
+
if (protocolHandlerShouldProcessSignal(message)) {
|
|
1402
1428
|
this.protocolHandler.processSignal(message);
|
|
1403
1429
|
}
|
|
1404
1430
|
else {
|
|
1405
1431
|
const local = this.clientId === message.clientId;
|
|
1406
|
-
this.
|
|
1432
|
+
this.runtime.processSignal(message, local);
|
|
1407
1433
|
}
|
|
1408
1434
|
}
|
|
1409
1435
|
/**
|
|
@@ -1436,20 +1462,37 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1436
1462
|
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1437
1463
|
}
|
|
1438
1464
|
async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
|
|
1439
|
-
var _a;
|
|
1440
|
-
assert(((_a = this.
|
|
1465
|
+
var _a, _b;
|
|
1466
|
+
assert(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1441
1467
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1442
1468
|
// are set. Global requests will still go directly to the loader
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
this.
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1469
|
+
const maybeLoader = this.scope;
|
|
1470
|
+
const loader = new RelativeLoader(this, maybeLoader.ILoader);
|
|
1471
|
+
const loadCodeResult = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "CodeLoad" }, async () => this.codeLoader.load(codeDetails));
|
|
1472
|
+
this._loadedModule = {
|
|
1473
|
+
module: loadCodeResult.module,
|
|
1474
|
+
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1475
|
+
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1476
|
+
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1477
|
+
details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
|
|
1478
|
+
};
|
|
1479
|
+
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1480
|
+
const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
|
|
1481
|
+
if (runtimeFactory === undefined) {
|
|
1482
|
+
throw new Error(packageNotFactoryError);
|
|
1450
1483
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1484
|
+
const getSpecifiedCodeDetails = () => {
|
|
1485
|
+
var _a;
|
|
1486
|
+
return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
|
|
1487
|
+
};
|
|
1488
|
+
const context = new ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; }, () => this.clientId, () => this._deltaManager.serviceConfiguration, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1489
|
+
this._lifecycleEvents.once("disposed", () => {
|
|
1490
|
+
context.dispose();
|
|
1491
|
+
});
|
|
1492
|
+
this._runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1493
|
+
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1494
|
+
this._loadedCodeDetails = codeDetails;
|
|
1495
|
+
this.emit("contextChanged", codeDetails);
|
|
1453
1496
|
}
|
|
1454
1497
|
/**
|
|
1455
1498
|
* Set the connected state of the ContainerContext
|
|
@@ -1459,16 +1502,15 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1459
1502
|
*/
|
|
1460
1503
|
setContextConnectedState(state, readonly) {
|
|
1461
1504
|
var _a;
|
|
1462
|
-
if (((_a = this.
|
|
1505
|
+
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1463
1506
|
/**
|
|
1464
1507
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1465
1508
|
* ops getting through to the DeltaManager.
|
|
1466
1509
|
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1467
1510
|
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1468
1511
|
*/
|
|
1469
|
-
this.
|
|
1512
|
+
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
1470
1513
|
}
|
|
1471
1514
|
}
|
|
1472
1515
|
}
|
|
1473
|
-
Container.version = "^0.1.0";
|
|
1474
1516
|
//# sourceMappingURL=container.js.map
|