@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/dist/container.js
CHANGED
|
@@ -21,20 +21,20 @@ const audience_1 = require("./audience");
|
|
|
21
21
|
const containerContext_1 = require("./containerContext");
|
|
22
22
|
const contracts_1 = require("./contracts");
|
|
23
23
|
const deltaManager_1 = require("./deltaManager");
|
|
24
|
-
const deltaManagerProxy_1 = require("./deltaManagerProxy");
|
|
25
24
|
const loader_1 = require("./loader");
|
|
26
25
|
const packageVersion_1 = require("./packageVersion");
|
|
27
26
|
const containerStorageAdapter_1 = require("./containerStorageAdapter");
|
|
28
27
|
const connectionStateHandler_1 = require("./connectionStateHandler");
|
|
29
28
|
const utils_1 = require("./utils");
|
|
30
29
|
const quorum_1 = require("./quorum");
|
|
31
|
-
const
|
|
30
|
+
const noopHeuristic_1 = require("./noopHeuristic");
|
|
32
31
|
const connectionManager_1 = require("./connectionManager");
|
|
33
32
|
const connectionState_1 = require("./connectionState");
|
|
34
33
|
const protocol_1 = require("./protocol");
|
|
35
34
|
const detachedContainerRefSeqNumber = 0;
|
|
36
35
|
const dirtyContainerEvent = "dirty";
|
|
37
36
|
const savedContainerEvent = "saved";
|
|
37
|
+
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
38
38
|
/**
|
|
39
39
|
* Waits until container connects to delta storage and gets up-to-date.
|
|
40
40
|
*
|
|
@@ -127,26 +127,18 @@ async function ReportIfTooLong(logger, eventName, action) {
|
|
|
127
127
|
}
|
|
128
128
|
exports.ReportIfTooLong = ReportIfTooLong;
|
|
129
129
|
const summarizerClientType = "summarizer";
|
|
130
|
-
/**
|
|
131
|
-
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
132
|
-
*/
|
|
133
130
|
class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
134
131
|
/**
|
|
135
132
|
* @internal
|
|
136
133
|
*/
|
|
137
|
-
constructor(
|
|
138
|
-
var _a
|
|
134
|
+
constructor(createProps, loadProps) {
|
|
135
|
+
var _a;
|
|
139
136
|
super((name, error) => {
|
|
140
137
|
this.mc.logger.sendErrorEvent({
|
|
141
138
|
eventName: "ContainerEventHandlerException",
|
|
142
139
|
name: typeof name === "string" ? name : undefined,
|
|
143
140
|
}, error);
|
|
144
141
|
});
|
|
145
|
-
this.loader = loader;
|
|
146
|
-
this.protocolHandlerBuilder = protocolHandlerBuilder;
|
|
147
|
-
// Tells if container can reconnect on losing fist connection
|
|
148
|
-
// If false, container gets closed on loss of connection.
|
|
149
|
-
this._canReconnect = true;
|
|
150
142
|
/**
|
|
151
143
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
152
144
|
*
|
|
@@ -172,28 +164,58 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
172
164
|
this.attachStarted = false;
|
|
173
165
|
this._dirtyContainer = false;
|
|
174
166
|
this.savedOps = [];
|
|
167
|
+
this.clientsWhoShouldHaveLeft = new Set();
|
|
175
168
|
this.setAutoReconnectTime = common_utils_1.performance.now();
|
|
169
|
+
this._lifecycleEvents = new common_utils_1.TypedEventEmitter();
|
|
176
170
|
this._disposed = false;
|
|
177
|
-
this.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
171
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
172
|
+
if (this.resolvedUrl === undefined) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)(this._loadedCodeDetails));
|
|
176
|
+
};
|
|
177
|
+
this.updateDirtyContainerState = (dirty) => {
|
|
178
|
+
if (this._dirtyContainer === dirty) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this._dirtyContainer = dirty;
|
|
182
|
+
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
183
|
+
};
|
|
184
|
+
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
185
|
+
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
186
|
+
const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
|
|
187
|
+
this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
|
|
188
|
+
this.clientDetailsOverride = clientDetailsOverride;
|
|
189
|
+
this.urlResolver = urlResolver;
|
|
190
|
+
this.serviceFactory = documentServiceFactory;
|
|
191
|
+
this.codeLoader = codeLoader;
|
|
192
|
+
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
193
|
+
// all clients that were loaded from the same loader (including summarizer clients).
|
|
194
|
+
// Tracking alternative ways to handle this in AB#4129.
|
|
195
|
+
this.options = Object.assign({}, options);
|
|
196
|
+
this.scope = scope;
|
|
197
|
+
this.detachedBlobStorage = detachedBlobStorage;
|
|
198
|
+
this.protocolHandlerBuilder =
|
|
199
|
+
protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
|
|
200
|
+
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
201
|
+
this.clone = async (_loadProps, createParamOverrides) => {
|
|
202
|
+
return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
|
|
203
|
+
};
|
|
182
204
|
// Create logger for data stores to use
|
|
183
205
|
const type = this.client.details.type;
|
|
184
206
|
const interactive = this.client.details.capabilities.interactive;
|
|
185
207
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
|
|
186
208
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
187
209
|
// We assign the id later so property getter is used.
|
|
188
|
-
this.subLogger = telemetry_utils_1.ChildLogger.create(
|
|
210
|
+
this.subLogger = telemetry_utils_1.ChildLogger.create(subLogger, undefined, {
|
|
189
211
|
all: {
|
|
190
212
|
clientType,
|
|
191
213
|
containerId: (0, uuid_1.v4)(),
|
|
192
|
-
docId: () => { var _a
|
|
214
|
+
docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
|
|
193
215
|
containerAttachState: () => this._attachState,
|
|
194
216
|
containerLifecycleState: () => this._lifecycleState,
|
|
195
217
|
containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
|
|
196
|
-
serializedContainer:
|
|
218
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
197
219
|
},
|
|
198
220
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
199
221
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -203,23 +225,24 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
203
225
|
dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
|
|
204
226
|
dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
|
|
205
227
|
dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
|
|
206
|
-
containerLoadedFromVersionId: () => { var _a; return (_a = this.
|
|
207
|
-
containerLoadedFromVersionDate: () => { var _a; return (_a = this.
|
|
228
|
+
containerLoadedFromVersionId: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
|
|
229
|
+
containerLoadedFromVersionDate: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
|
|
208
230
|
// message information to associate errors with the specific execution state
|
|
209
231
|
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
210
232
|
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; },
|
|
211
233
|
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; },
|
|
212
|
-
dmLastMsqSeqClientId: () => {
|
|
234
|
+
dmLastMsqSeqClientId: () => {
|
|
235
|
+
var _a, _b, _c, _d;
|
|
236
|
+
return ((_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId) === null
|
|
237
|
+
? "null"
|
|
238
|
+
: (_d = (_c = this.deltaManager) === null || _c === void 0 ? void 0 : _c.lastMessage) === null || _d === void 0 ? void 0 : _d.clientId;
|
|
239
|
+
},
|
|
213
240
|
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; },
|
|
214
241
|
connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
215
242
|
},
|
|
216
243
|
});
|
|
217
244
|
// Prefix all events in this file with container-loader
|
|
218
245
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
|
|
219
|
-
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
220
|
-
// all clients that were loaded from the same loader (including summarizer clients).
|
|
221
|
-
// Tracking alternative ways to handle this in AB#4129.
|
|
222
|
-
this.options = Object.assign({}, this.loader.services.options);
|
|
223
246
|
this._deltaManager = this.createDeltaManager();
|
|
224
247
|
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
225
248
|
logger: this.mc.logger,
|
|
@@ -235,7 +258,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
235
258
|
}
|
|
236
259
|
},
|
|
237
260
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
238
|
-
maxClientLeaveWaitTime:
|
|
261
|
+
maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
|
|
239
262
|
logConnectionIssue: (eventName, category, details) => {
|
|
240
263
|
const mode = this.connectionMode;
|
|
241
264
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
@@ -259,7 +282,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
259
282
|
this.connect();
|
|
260
283
|
}
|
|
261
284
|
},
|
|
262
|
-
|
|
285
|
+
clientShouldHaveLeft: (clientId) => {
|
|
286
|
+
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
287
|
+
},
|
|
288
|
+
}, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
|
|
263
289
|
this.on(savedContainerEvent, () => {
|
|
264
290
|
this.connectionStateHandler.containerSaved();
|
|
265
291
|
});
|
|
@@ -271,8 +297,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
271
297
|
: (0, driver_utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
|
|
272
298
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
273
299
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
274
|
-
const forceEnableSummarizeProtocolTree = (
|
|
275
|
-
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(
|
|
300
|
+
const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
|
|
301
|
+
this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
276
302
|
const isDomAvailable = typeof document === "object" &&
|
|
277
303
|
document !== null &&
|
|
278
304
|
typeof document.addEventListener === "function" &&
|
|
@@ -298,33 +324,28 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
298
324
|
* Load an existing container.
|
|
299
325
|
* @internal
|
|
300
326
|
*/
|
|
301
|
-
static async load(
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
canReconnect: loadOptions.canReconnect,
|
|
306
|
-
serializedContainerState: pendingLocalState,
|
|
307
|
-
}, protocolHandlerBuilder);
|
|
327
|
+
static async load(loadProps, createProps) {
|
|
328
|
+
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
329
|
+
const container = new Container(createProps, loadProps);
|
|
330
|
+
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
308
331
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
309
|
-
var _a, _b;
|
|
310
|
-
const version = loadOptions.version;
|
|
311
332
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
312
333
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
313
334
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
314
335
|
const mode = pendingLocalState
|
|
315
|
-
? Object.assign(Object.assign({}, (
|
|
336
|
+
? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
|
|
316
337
|
const onClosed = (err) => {
|
|
317
338
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
318
339
|
reject(err !== null && err !== void 0 ? err : new container_utils_1.GenericError("Container closed without error during load"));
|
|
319
340
|
};
|
|
320
341
|
container.on("closed", onClosed);
|
|
321
342
|
container
|
|
322
|
-
.load(version, mode, pendingLocalState)
|
|
343
|
+
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
323
344
|
.finally(() => {
|
|
324
345
|
container.removeListener("closed", onClosed);
|
|
325
346
|
})
|
|
326
347
|
.then((props) => {
|
|
327
|
-
event.end(Object.assign(Object.assign({}, props),
|
|
348
|
+
event.end(Object.assign(Object.assign({}, props), loadMode));
|
|
328
349
|
resolve(container);
|
|
329
350
|
}, (error) => {
|
|
330
351
|
const err = (0, telemetry_utils_1.normalizeError)(error);
|
|
@@ -334,13 +355,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
334
355
|
container.close(err);
|
|
335
356
|
onClosed(err);
|
|
336
357
|
});
|
|
337
|
-
}), { start: true, end: true, cancel: "generic" });
|
|
358
|
+
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
338
359
|
}
|
|
339
360
|
/**
|
|
340
361
|
* Create a new container in a detached state.
|
|
341
362
|
*/
|
|
342
|
-
static async createDetached(
|
|
343
|
-
const container = new Container(
|
|
363
|
+
static async createDetached(createProps, codeDetails) {
|
|
364
|
+
const container = new Container(createProps);
|
|
344
365
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
345
366
|
await container.createDetached(codeDetails);
|
|
346
367
|
return container;
|
|
@@ -350,8 +371,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
350
371
|
* Create a new container in a detached state that is initialized with a
|
|
351
372
|
* snapshot from a previous detached container.
|
|
352
373
|
*/
|
|
353
|
-
static async rehydrateDetachedFromSnapshot(
|
|
354
|
-
const container = new Container(
|
|
374
|
+
static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
|
|
375
|
+
const container = new Container(createProps);
|
|
355
376
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
356
377
|
const deserializedSummary = JSON.parse(snapshot);
|
|
357
378
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
@@ -373,14 +394,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
373
394
|
this._lifecycleState === "disposing" ||
|
|
374
395
|
this._lifecycleState === "disposed");
|
|
375
396
|
}
|
|
376
|
-
get
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
get context() {
|
|
380
|
-
if (this._context === undefined) {
|
|
381
|
-
throw new container_utils_1.GenericError("Attempted to access context before it was defined");
|
|
397
|
+
get runtime() {
|
|
398
|
+
if (this._runtime === undefined) {
|
|
399
|
+
throw new Error("Attempted to access runtime before it was defined");
|
|
382
400
|
}
|
|
383
|
-
return this.
|
|
401
|
+
return this._runtime;
|
|
384
402
|
}
|
|
385
403
|
get protocolHandler() {
|
|
386
404
|
if (this._protocolHandler === undefined) {
|
|
@@ -395,17 +413,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
395
413
|
return this;
|
|
396
414
|
}
|
|
397
415
|
get resolvedUrl() {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
416
|
+
var _a;
|
|
417
|
+
/**
|
|
418
|
+
* All attached containers will have a document service,
|
|
419
|
+
* this is required, as attached containers are attached to
|
|
420
|
+
* a service. Detached containers will neither have a document
|
|
421
|
+
* service or a resolved url as they only exist locally.
|
|
422
|
+
* in order to create a document service a resolved url must
|
|
423
|
+
* first be obtained, this is how the container is identified.
|
|
424
|
+
* Because of this, the document service's resolved url
|
|
425
|
+
* is always the same as the containers, as we had to
|
|
426
|
+
* obtain the resolved url, and then create the service from it.
|
|
427
|
+
*/
|
|
428
|
+
return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
|
|
402
429
|
}
|
|
403
430
|
get readOnlyInfo() {
|
|
404
431
|
return this._deltaManager.readOnlyInfo;
|
|
405
432
|
}
|
|
406
|
-
get closeSignal() {
|
|
407
|
-
return this._deltaManager.closeAbortController.signal;
|
|
408
|
-
}
|
|
409
433
|
/**
|
|
410
434
|
* Tracks host requiring read-only mode.
|
|
411
435
|
*/
|
|
@@ -421,13 +445,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
421
445
|
get connected() {
|
|
422
446
|
return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
|
|
423
447
|
}
|
|
424
|
-
/**
|
|
425
|
-
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
426
|
-
* configuration details returned as part of the initial connection.
|
|
427
|
-
*/
|
|
428
|
-
get serviceConfiguration() {
|
|
429
|
-
return this._deltaManager.serviceConfiguration;
|
|
430
|
-
}
|
|
431
448
|
/**
|
|
432
449
|
* The server provided id of the client.
|
|
433
450
|
* Set once this.connected is true, otherwise undefined
|
|
@@ -435,21 +452,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
435
452
|
get clientId() {
|
|
436
453
|
return this._clientId;
|
|
437
454
|
}
|
|
438
|
-
/**
|
|
439
|
-
* The server provided claims of the client.
|
|
440
|
-
* Set once this.connected is true, otherwise undefined
|
|
441
|
-
*/
|
|
442
|
-
get scopes() {
|
|
443
|
-
return this._deltaManager.connectionManager.scopes;
|
|
444
|
-
}
|
|
445
|
-
get clientDetails() {
|
|
446
|
-
return this._deltaManager.clientDetails;
|
|
447
|
-
}
|
|
448
455
|
get offlineLoadEnabled() {
|
|
449
456
|
var _a, _b;
|
|
450
457
|
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;
|
|
451
458
|
// summarizer will not have any pending state we want to save
|
|
452
|
-
return enabled && this.clientDetails.capabilities.interactive;
|
|
459
|
+
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
453
460
|
}
|
|
454
461
|
/**
|
|
455
462
|
* Get the code details that are currently specified for the container.
|
|
@@ -464,8 +471,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
464
471
|
* loaded.
|
|
465
472
|
*/
|
|
466
473
|
getLoadedCodeDetails() {
|
|
467
|
-
|
|
468
|
-
return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
|
|
474
|
+
return this._loadedCodeDetails;
|
|
469
475
|
}
|
|
470
476
|
/**
|
|
471
477
|
* Retrieves the audience associated with the document
|
|
@@ -481,49 +487,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
481
487
|
get isDirty() {
|
|
482
488
|
return this._dirtyContainer;
|
|
483
489
|
}
|
|
484
|
-
get serviceFactory() {
|
|
485
|
-
return this.loader.services.documentServiceFactory;
|
|
486
|
-
}
|
|
487
|
-
get urlResolver() {
|
|
488
|
-
return this.loader.services.urlResolver;
|
|
489
|
-
}
|
|
490
|
-
get scope() {
|
|
491
|
-
return this.loader.services.scope;
|
|
492
|
-
}
|
|
493
|
-
get codeLoader() {
|
|
494
|
-
return this.loader.services.codeLoader;
|
|
495
|
-
}
|
|
496
490
|
/**
|
|
497
491
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
498
492
|
*/
|
|
499
493
|
async getEntryPoint() {
|
|
500
494
|
var _a, _b;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (this.
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
while (this._context === undefined) {
|
|
508
|
-
await new Promise((resolve, reject) => {
|
|
509
|
-
const contextChangedHandler = () => {
|
|
510
|
-
resolve();
|
|
511
|
-
this.off("disposed", disposedHandler);
|
|
512
|
-
};
|
|
513
|
-
const disposedHandler = (error) => {
|
|
514
|
-
reject(error !== null && error !== void 0 ? error : "The Container is disposed");
|
|
515
|
-
this.off("contextChanged", contextChangedHandler);
|
|
516
|
-
};
|
|
517
|
-
this.once("contextChanged", contextChangedHandler);
|
|
518
|
-
this.once("disposed", disposedHandler);
|
|
519
|
-
});
|
|
520
|
-
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
521
|
-
// should have set this._context; making sure.
|
|
522
|
-
(0, common_utils_1.assert)(this._context !== undefined, 0x5a2 /* Context still not defined after contextChanged event */);
|
|
495
|
+
if (this._disposed) {
|
|
496
|
+
throw new container_utils_1.UsageError("The context is already disposed");
|
|
497
|
+
}
|
|
498
|
+
if (this._runtime !== undefined) {
|
|
499
|
+
return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
523
500
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
501
|
+
return new Promise((resolve, reject) => {
|
|
502
|
+
const runtimeInstantiatedHandler = () => {
|
|
503
|
+
var _a, _b;
|
|
504
|
+
(0, common_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
505
|
+
resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
506
|
+
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
507
|
+
};
|
|
508
|
+
const disposedHandler = () => {
|
|
509
|
+
reject(new Error("ContainerContext was disposed"));
|
|
510
|
+
this._lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
511
|
+
};
|
|
512
|
+
this._lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
513
|
+
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
514
|
+
});
|
|
527
515
|
}
|
|
528
516
|
/**
|
|
529
517
|
* Retrieves the quorum associated with the document
|
|
@@ -532,7 +520,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
532
520
|
return this.protocolHandler.quorum;
|
|
533
521
|
}
|
|
534
522
|
dispose(error) {
|
|
535
|
-
this._deltaManager.
|
|
523
|
+
this._deltaManager.dispose(error);
|
|
536
524
|
this.verifyClosed();
|
|
537
525
|
}
|
|
538
526
|
close(error) {
|
|
@@ -548,7 +536,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
548
536
|
(0, common_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
549
537
|
}
|
|
550
538
|
closeCore(error) {
|
|
551
|
-
var _a
|
|
539
|
+
var _a;
|
|
552
540
|
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
553
541
|
try {
|
|
554
542
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -566,12 +554,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
566
554
|
this._lifecycleState = "closing";
|
|
567
555
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
568
556
|
this.connectionStateHandler.dispose();
|
|
569
|
-
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
570
|
-
this.storageAdapter.dispose();
|
|
571
|
-
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
572
|
-
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
573
|
-
// Driver need to ensure all caches are cleared on critical errors
|
|
574
|
-
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
575
557
|
}
|
|
576
558
|
catch (exception) {
|
|
577
559
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
@@ -583,6 +565,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
583
565
|
}
|
|
584
566
|
finally {
|
|
585
567
|
this._lifecycleState = "closed";
|
|
568
|
+
// There is no user for summarizer, so we need to ensure dispose is called
|
|
569
|
+
if (this.client.details.type === summarizerClientType) {
|
|
570
|
+
this.dispose(error);
|
|
571
|
+
}
|
|
586
572
|
}
|
|
587
573
|
}
|
|
588
574
|
disposeCore(error) {
|
|
@@ -596,7 +582,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
596
582
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
597
583
|
this.mc.logger.sendTelemetryEvent({
|
|
598
584
|
eventName: "ContainerDispose",
|
|
599
|
-
|
|
585
|
+
// Only log error if container isn't closed
|
|
586
|
+
category: !this.closed && error !== undefined ? "error" : "generic",
|
|
600
587
|
}, error);
|
|
601
588
|
// ! Progressing from "closed" to "disposing" is not allowed
|
|
602
589
|
if (this._lifecycleState !== "closed") {
|
|
@@ -604,7 +591,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
604
591
|
}
|
|
605
592
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
606
593
|
this.connectionStateHandler.dispose();
|
|
607
|
-
|
|
594
|
+
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
595
|
+
(_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
|
|
608
596
|
this.storageAdapter.dispose();
|
|
609
597
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
610
598
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -622,6 +610,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
622
610
|
}
|
|
623
611
|
finally {
|
|
624
612
|
this._lifecycleState = "disposed";
|
|
613
|
+
this._lifecycleEvents.emit("disposed");
|
|
625
614
|
}
|
|
626
615
|
}
|
|
627
616
|
closeAndGetPendingLocalState() {
|
|
@@ -636,12 +625,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
636
625
|
if (!this.offlineLoadEnabled) {
|
|
637
626
|
throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
|
|
638
627
|
}
|
|
628
|
+
if (this.closed || this._disposed) {
|
|
629
|
+
throw new container_utils_1.UsageError("Pending state cannot be retried if the container is closed or disposed");
|
|
630
|
+
}
|
|
639
631
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
640
632
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
641
633
|
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
642
634
|
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
643
635
|
const pendingState = {
|
|
644
|
-
pendingRuntimeState: this.
|
|
636
|
+
pendingRuntimeState: this.runtime.getPendingLocalState(),
|
|
645
637
|
baseSnapshot: this.baseSnapshot,
|
|
646
638
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
647
639
|
savedOps: this.savedOps,
|
|
@@ -657,11 +649,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
657
649
|
}
|
|
658
650
|
serialize() {
|
|
659
651
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
660
|
-
const appSummary = this.
|
|
652
|
+
const appSummary = this.runtime.createSummary();
|
|
661
653
|
const protocolSummary = this.captureProtocolSummary();
|
|
662
654
|
const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
663
|
-
if (this.
|
|
664
|
-
this.loader.services.detachedBlobStorage.size > 0) {
|
|
655
|
+
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
665
656
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
666
657
|
type: protocol_definitions_1.SummaryType.Blob,
|
|
667
658
|
content: "true",
|
|
@@ -680,15 +671,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
680
671
|
(0, common_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
681
672
|
this.attachStarted = true;
|
|
682
673
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
683
|
-
const hasAttachmentBlobs = this.
|
|
684
|
-
this.loader.services.detachedBlobStorage.size > 0;
|
|
674
|
+
const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
685
675
|
try {
|
|
686
676
|
(0, common_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
687
677
|
let summary;
|
|
688
678
|
if (!hasAttachmentBlobs) {
|
|
689
679
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
690
680
|
// semantics around what the attach means as far as async code goes.
|
|
691
|
-
const appSummary = this.
|
|
681
|
+
const appSummary = this.runtime.createSummary();
|
|
692
682
|
const protocolSummary = this.captureProtocolSummary();
|
|
693
683
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
694
684
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
@@ -696,6 +686,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
696
686
|
// starting to attach the container to storage.
|
|
697
687
|
// Also, this should only be fired in detached container.
|
|
698
688
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
689
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
699
690
|
this.emit("attaching");
|
|
700
691
|
if (this.offlineLoadEnabled) {
|
|
701
692
|
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
@@ -705,40 +696,38 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
705
696
|
}
|
|
706
697
|
}
|
|
707
698
|
// Actually go and create the resolved document
|
|
708
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
709
|
-
(0, driver_utils_1.ensureFluidResolvedUrl)(createNewResolvedUrl);
|
|
710
699
|
if (this.service === undefined) {
|
|
711
|
-
|
|
700
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
701
|
+
(0, common_utils_1.assert)(this.client.details.type !== summarizerClientType &&
|
|
702
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
712
703
|
this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
713
|
-
cancel: this.
|
|
704
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
714
705
|
});
|
|
715
706
|
}
|
|
716
|
-
const resolvedUrl = this.service.resolvedUrl;
|
|
717
|
-
(0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
|
|
718
|
-
this._resolvedUrl = resolvedUrl;
|
|
719
707
|
await this.storageAdapter.connectToService(this.service);
|
|
720
708
|
if (hasAttachmentBlobs) {
|
|
721
709
|
// upload blobs to storage
|
|
722
|
-
(0, common_utils_1.assert)(!!this.
|
|
710
|
+
(0, common_utils_1.assert)(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
723
711
|
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
724
712
|
// support blob handles that only know about the local IDs
|
|
725
713
|
const redirectTable = new Map();
|
|
726
714
|
// if new blobs are added while uploading, upload them too
|
|
727
|
-
while (redirectTable.size < this.
|
|
728
|
-
const newIds = this.
|
|
715
|
+
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
716
|
+
const newIds = this.detachedBlobStorage
|
|
729
717
|
.getBlobIds()
|
|
730
718
|
.filter((id) => !redirectTable.has(id));
|
|
731
719
|
for (const id of newIds) {
|
|
732
|
-
const blob = await this.
|
|
720
|
+
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
733
721
|
const response = await this.storageAdapter.createBlob(blob);
|
|
734
722
|
redirectTable.set(id, response.id);
|
|
735
723
|
}
|
|
736
724
|
}
|
|
737
725
|
// take summary and upload
|
|
738
|
-
const appSummary = this.
|
|
726
|
+
const appSummary = this.runtime.createSummary(redirectTable);
|
|
739
727
|
const protocolSummary = this.captureProtocolSummary();
|
|
740
728
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
741
729
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
730
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
742
731
|
this.emit("attaching");
|
|
743
732
|
if (this.offlineLoadEnabled) {
|
|
744
733
|
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
@@ -753,6 +742,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
753
742
|
});
|
|
754
743
|
}
|
|
755
744
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
745
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
|
|
756
746
|
this.emit("attached");
|
|
757
747
|
if (!this.closed) {
|
|
758
748
|
this.resumeInternal({
|
|
@@ -764,18 +754,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
764
754
|
catch (error) {
|
|
765
755
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
766
756
|
const newError = (0, telemetry_utils_1.normalizeError)(error);
|
|
767
|
-
|
|
768
|
-
if ((0, driver_utils_1.isFluidResolvedUrl)(resolvedUrl)) {
|
|
769
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
770
|
-
}
|
|
757
|
+
newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
|
|
771
758
|
this.close(newError);
|
|
772
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
773
759
|
throw newError;
|
|
774
760
|
}
|
|
775
761
|
}, { start: true, end: true, cancel: "generic" });
|
|
776
762
|
}
|
|
777
763
|
async request(path) {
|
|
778
|
-
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.
|
|
764
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
779
765
|
}
|
|
780
766
|
setAutoReconnectInternal(mode) {
|
|
781
767
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
@@ -841,13 +827,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
841
827
|
// Ensure connection to web socket
|
|
842
828
|
this.connectToDeltaStream(args);
|
|
843
829
|
}
|
|
844
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
845
|
-
var _a;
|
|
846
|
-
if (this.resolvedUrl === undefined) {
|
|
847
|
-
return undefined;
|
|
848
|
-
}
|
|
849
|
-
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
|
|
850
|
-
}
|
|
851
830
|
async proposeCodeDetails(codeDetails) {
|
|
852
831
|
if (!(0, container_definitions_1.isFluidCodeDetails)(codeDetails)) {
|
|
853
832
|
throw new Error("Provided codeDetails are not IFluidCodeDetails");
|
|
@@ -864,13 +843,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
864
843
|
.catch(() => false);
|
|
865
844
|
}
|
|
866
845
|
async processCodeProposal() {
|
|
867
|
-
var _a;
|
|
868
846
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
869
847
|
await Promise.all([
|
|
870
848
|
this.deltaManager.inbound.pause(),
|
|
871
849
|
this.deltaManager.inboundSignal.pause(),
|
|
872
850
|
]);
|
|
873
|
-
if ((await this.
|
|
851
|
+
if ((await this.satisfies(codeDetails)) === true) {
|
|
874
852
|
this.deltaManager.inbound.resume();
|
|
875
853
|
this.deltaManager.inboundSignal.resume();
|
|
876
854
|
return;
|
|
@@ -878,19 +856,44 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
878
856
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
879
857
|
const error = new container_utils_1.GenericError("Existing context does not satisfy incoming proposal");
|
|
880
858
|
this.close(error);
|
|
881
|
-
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
862
|
+
*/
|
|
863
|
+
async satisfies(constraintCodeDetails) {
|
|
864
|
+
var _a, _b;
|
|
865
|
+
// If we have no module, it can't satisfy anything.
|
|
866
|
+
if (this._loadedModule === undefined) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
const comparers = [];
|
|
870
|
+
const maybeCompareCodeLoader = this.codeLoader;
|
|
871
|
+
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
872
|
+
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
873
|
+
}
|
|
874
|
+
const maybeCompareExport = (_a = this._loadedModule) === null || _a === void 0 ? void 0 : _a.module.fluidExport;
|
|
875
|
+
if ((maybeCompareExport === null || maybeCompareExport === void 0 ? void 0 : maybeCompareExport.IFluidCodeDetailsComparer) !== undefined) {
|
|
876
|
+
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
877
|
+
}
|
|
878
|
+
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
879
|
+
// the incoming constraint, so we return false. Assuming it does not satisfy is safer, to force a reload
|
|
880
|
+
// rather than potentially running with incompatible code.
|
|
881
|
+
if (comparers.length === 0) {
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
for (const comparer of comparers) {
|
|
885
|
+
const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
|
|
886
|
+
if (satisfies === false) {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return true;
|
|
882
891
|
}
|
|
883
892
|
async getVersion(version) {
|
|
884
893
|
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
885
894
|
return versions[0];
|
|
886
895
|
}
|
|
887
|
-
recordConnectStartTime() {
|
|
888
|
-
if (this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] === undefined) {
|
|
889
|
-
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
896
|
connectToDeltaStream(args) {
|
|
893
|
-
this.recordConnectStartTime();
|
|
894
897
|
// All agents need "write" access, including summarizer.
|
|
895
898
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
896
899
|
args.mode = "write";
|
|
@@ -902,12 +905,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
902
905
|
*
|
|
903
906
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
904
907
|
*/
|
|
905
|
-
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
906
|
-
var _a;
|
|
907
|
-
|
|
908
|
-
throw new Error("Attempting to load without a resolved url");
|
|
909
|
-
}
|
|
910
|
-
this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
908
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
909
|
+
var _a, _b, _c;
|
|
910
|
+
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
911
911
|
// Ideally we always connect as "read" by default.
|
|
912
912
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
913
913
|
// We should not rely on it by (one of them will address the issue, but we need to address both)
|
|
@@ -933,9 +933,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
933
933
|
else {
|
|
934
934
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
935
935
|
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
936
|
-
var _a;
|
|
937
936
|
this.close(error);
|
|
938
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
939
937
|
});
|
|
940
938
|
}
|
|
941
939
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
@@ -952,7 +950,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
952
950
|
if (this.offlineLoadEnabled) {
|
|
953
951
|
this.baseSnapshot = snapshot;
|
|
954
952
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
955
|
-
this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.
|
|
953
|
+
this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storageAdapter);
|
|
956
954
|
}
|
|
957
955
|
}
|
|
958
956
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
@@ -988,7 +986,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
988
986
|
for (const message of pendingLocalState.savedOps) {
|
|
989
987
|
this.processRemoteMessage(message);
|
|
990
988
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
991
|
-
await this.
|
|
989
|
+
await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
|
|
992
990
|
}
|
|
993
991
|
pendingLocalState.savedOps = [];
|
|
994
992
|
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
@@ -1061,8 +1059,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1061
1059
|
}
|
|
1062
1060
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
1063
1061
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1064
|
-
(0, common_utils_1.assert)(!!this.
|
|
1065
|
-
this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1062
|
+
(0, common_utils_1.assert)(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1066
1063
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1067
1064
|
}
|
|
1068
1065
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
@@ -1115,9 +1112,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1115
1112
|
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
1116
1113
|
}
|
|
1117
1114
|
initializeProtocolState(attributes, quorumSnapshot) {
|
|
1118
|
-
|
|
1119
|
-
const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
|
|
1120
|
-
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
|
|
1115
|
+
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
|
|
1121
1116
|
const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
1122
1117
|
protocol.quorum.on("error", (error) => {
|
|
1123
1118
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -1137,10 +1132,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1137
1132
|
});
|
|
1138
1133
|
}
|
|
1139
1134
|
this.processCodeProposal().catch((error) => {
|
|
1140
|
-
var _a;
|
|
1141
1135
|
const normalizedError = (0, telemetry_utils_1.normalizeError)(error);
|
|
1142
1136
|
this.close(normalizedError);
|
|
1143
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
|
|
1144
1137
|
throw error;
|
|
1145
1138
|
});
|
|
1146
1139
|
}
|
|
@@ -1224,9 +1217,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1224
1217
|
(0, common_utils_1.assert)(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1225
1218
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1226
1219
|
});
|
|
1220
|
+
deltaManager.on("establishingConnection", (reason) => {
|
|
1221
|
+
this.connectionStateHandler.establishingConnection(reason);
|
|
1222
|
+
});
|
|
1223
|
+
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1224
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1225
|
+
});
|
|
1227
1226
|
deltaManager.on("disconnect", (reason, error) => {
|
|
1228
1227
|
var _a;
|
|
1229
|
-
(_a = this.
|
|
1228
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1230
1229
|
if (!this.closed) {
|
|
1231
1230
|
this.connectionStateHandler.receivedDisconnectEvent(reason, error);
|
|
1232
1231
|
}
|
|
@@ -1280,10 +1279,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1280
1279
|
time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
|
|
1281
1280
|
durationFromDisconnected = telemetry_utils_1.TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1282
1281
|
}
|
|
1283
|
-
else {
|
|
1284
|
-
// This info is of most
|
|
1282
|
+
else if (value === connectionState_1.ConnectionState.CatchingUp) {
|
|
1283
|
+
// This info is of most interesting while Catching Up.
|
|
1285
1284
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1286
|
-
|
|
1285
|
+
// Need to check that we have already loaded and fetched the snapshot.
|
|
1286
|
+
if (this.deltaManager.hasCheckpointSequenceNumber &&
|
|
1287
|
+
this._lifecycleState === "loaded") {
|
|
1287
1288
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1288
1289
|
}
|
|
1289
1290
|
}
|
|
@@ -1330,7 +1331,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1330
1331
|
}
|
|
1331
1332
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1332
1333
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1333
|
-
var _a;
|
|
1334
1334
|
switch (type) {
|
|
1335
1335
|
case protocol_definitions_1.MessageType.Operation:
|
|
1336
1336
|
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
@@ -1339,7 +1339,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1339
1339
|
default: {
|
|
1340
1340
|
const newError = new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1341
1341
|
this.close(newError);
|
|
1342
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
1343
1342
|
return -1;
|
|
1344
1343
|
}
|
|
1345
1344
|
}
|
|
@@ -1372,7 +1371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1372
1371
|
return -1;
|
|
1373
1372
|
}
|
|
1374
1373
|
this.messageCountAfterDisconnection += 1;
|
|
1375
|
-
(_a = this.
|
|
1374
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
|
|
1376
1375
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1377
1376
|
}
|
|
1378
1377
|
processRemoteMessage(message) {
|
|
@@ -1380,24 +1379,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1380
1379
|
this.savedOps.push(message);
|
|
1381
1380
|
}
|
|
1382
1381
|
const local = this.clientId === message.clientId;
|
|
1382
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
1383
|
+
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1384
|
+
if (message.clientId != null) {
|
|
1385
|
+
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1386
|
+
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
1387
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1388
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1389
|
+
}
|
|
1390
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1391
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1392
|
+
// document we don't need to blow up aggressively.
|
|
1393
|
+
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1394
|
+
!(0, driver_utils_1.canBeCoalescedByService)(message)) {
|
|
1395
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1396
|
+
throw new Error("Remote message's clientId already should have left");
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1383
1399
|
// Allow the protocol handler to process the message
|
|
1384
1400
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1385
1401
|
// Forward messages to the loaded runtime for processing
|
|
1386
|
-
this.
|
|
1402
|
+
this.runtime.process(message, local);
|
|
1387
1403
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1388
1404
|
if (this.activeConnection()) {
|
|
1389
|
-
if (this.
|
|
1405
|
+
if (this.noopHeuristic === undefined) {
|
|
1406
|
+
const serviceConfiguration = this.deltaManager.serviceConfiguration;
|
|
1390
1407
|
// Note that config from first connection will be used for this container's lifetime.
|
|
1391
1408
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1392
1409
|
// clients.
|
|
1393
1410
|
// All existing will continue to use settings they got earlier.
|
|
1394
|
-
(0, common_utils_1.assert)(
|
|
1395
|
-
this.
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1411
|
+
(0, common_utils_1.assert)(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1412
|
+
this.noopHeuristic = new noopHeuristic_1.NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
|
|
1413
|
+
this.noopHeuristic.on("wantsNoop", () => {
|
|
1414
|
+
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
1415
|
+
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
1416
|
+
// running the microtask that the heuristic queued in response.
|
|
1417
|
+
(0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
|
|
1418
|
+
this.submitMessage(protocol_definitions_1.MessageType.NoOp);
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
this.noopHeuristic.notifyMessageProcessed(message);
|
|
1422
|
+
// The contract with the protocolHandler is that returning "immediateNoOp" is equivalent to "please immediately accept the proposal I just processed".
|
|
1423
|
+
if (result.immediateNoOp === true) {
|
|
1424
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
1425
|
+
this.submitMessage(driver_utils_1.MessageType2.Accept);
|
|
1399
1426
|
}
|
|
1400
|
-
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1401
1427
|
}
|
|
1402
1428
|
this.emit("op", message);
|
|
1403
1429
|
}
|
|
@@ -1406,12 +1432,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1406
1432
|
}
|
|
1407
1433
|
processSignal(message) {
|
|
1408
1434
|
// No clientId indicates a system signal message.
|
|
1409
|
-
if (
|
|
1435
|
+
if ((0, protocol_1.protocolHandlerShouldProcessSignal)(message)) {
|
|
1410
1436
|
this.protocolHandler.processSignal(message);
|
|
1411
1437
|
}
|
|
1412
1438
|
else {
|
|
1413
1439
|
const local = this.clientId === message.clientId;
|
|
1414
|
-
this.
|
|
1440
|
+
this.runtime.processSignal(message, local);
|
|
1415
1441
|
}
|
|
1416
1442
|
}
|
|
1417
1443
|
/**
|
|
@@ -1444,20 +1470,37 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1444
1470
|
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1445
1471
|
}
|
|
1446
1472
|
async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
|
|
1447
|
-
var _a;
|
|
1448
|
-
(0, common_utils_1.assert)(((_a = this.
|
|
1473
|
+
var _a, _b;
|
|
1474
|
+
(0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1449
1475
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1450
1476
|
// are set. Global requests will still go directly to the loader
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
this.
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1477
|
+
const maybeLoader = this.scope;
|
|
1478
|
+
const loader = new loader_1.RelativeLoader(this, maybeLoader.ILoader);
|
|
1479
|
+
const loadCodeResult = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "CodeLoad" }, async () => this.codeLoader.load(codeDetails));
|
|
1480
|
+
this._loadedModule = {
|
|
1481
|
+
module: loadCodeResult.module,
|
|
1482
|
+
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1483
|
+
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1484
|
+
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1485
|
+
details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
|
|
1486
|
+
};
|
|
1487
|
+
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1488
|
+
const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
|
|
1489
|
+
if (runtimeFactory === undefined) {
|
|
1490
|
+
throw new Error(packageNotFactoryError);
|
|
1458
1491
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1492
|
+
const getSpecifiedCodeDetails = () => {
|
|
1493
|
+
var _a;
|
|
1494
|
+
return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
|
|
1495
|
+
};
|
|
1496
|
+
const context = new containerContext_1.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);
|
|
1497
|
+
this._lifecycleEvents.once("disposed", () => {
|
|
1498
|
+
context.dispose();
|
|
1499
|
+
});
|
|
1500
|
+
this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1501
|
+
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1502
|
+
this._loadedCodeDetails = codeDetails;
|
|
1503
|
+
this.emit("contextChanged", codeDetails);
|
|
1461
1504
|
}
|
|
1462
1505
|
/**
|
|
1463
1506
|
* Set the connected state of the ContainerContext
|
|
@@ -1467,17 +1510,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1467
1510
|
*/
|
|
1468
1511
|
setContextConnectedState(state, readonly) {
|
|
1469
1512
|
var _a;
|
|
1470
|
-
if (((_a = this.
|
|
1513
|
+
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1471
1514
|
/**
|
|
1472
1515
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1473
1516
|
* ops getting through to the DeltaManager.
|
|
1474
1517
|
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1475
1518
|
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1476
1519
|
*/
|
|
1477
|
-
this.
|
|
1520
|
+
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
1478
1521
|
}
|
|
1479
1522
|
}
|
|
1480
1523
|
}
|
|
1481
1524
|
exports.Container = Container;
|
|
1482
|
-
Container.version = "^0.1.0";
|
|
1483
1525
|
//# sourceMappingURL=container.js.map
|