@fluidframework/container-loader 2.0.0-internal.5.1.0 → 2.0.0-internal.5.2.0
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 +16 -0
- package/dist/catchUpMonitor.d.ts +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +3 -0
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +10 -8
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -27
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +164 -104
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +22 -58
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +27 -200
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js.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 +1 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +0 -18
- 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/retriableDocumentStorageService.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +3 -0
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +10 -8
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -27
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +166 -106
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +22 -58
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +27 -200
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js.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 +1 -12
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +0 -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/retriableDocumentStorageService.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/package.json +11 -11
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +2 -1
- package/src/connectionStateHandler.ts +14 -9
- package/src/container.ts +247 -126
- package/src/containerContext.ts +32 -318
- package/src/containerStorageAdapter.ts +1 -1
- package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -39
- package/src/protocolTreeDocumentStorageService.ts +1 -1
- package/src/retriableDocumentStorageService.ts +2 -1
- package/dist/collabWindowTracker.d.ts +0 -19
- package/dist/collabWindowTracker.d.ts.map +0 -1
- package/dist/collabWindowTracker.js.map +0 -1
- package/lib/collabWindowTracker.d.ts +0 -19
- package/lib/collabWindowTracker.d.ts.map +0 -1
- package/lib/collabWindowTracker.js.map +0 -1
package/dist/container.js
CHANGED
|
@@ -28,13 +28,14 @@ const containerStorageAdapter_1 = require("./containerStorageAdapter");
|
|
|
28
28
|
const connectionStateHandler_1 = require("./connectionStateHandler");
|
|
29
29
|
const utils_1 = require("./utils");
|
|
30
30
|
const quorum_1 = require("./quorum");
|
|
31
|
-
const
|
|
31
|
+
const noopHeuristic_1 = require("./noopHeuristic");
|
|
32
32
|
const connectionManager_1 = require("./connectionManager");
|
|
33
33
|
const connectionState_1 = require("./connectionState");
|
|
34
34
|
const protocol_1 = require("./protocol");
|
|
35
35
|
const detachedContainerRefSeqNumber = 0;
|
|
36
36
|
const dirtyContainerEvent = "dirty";
|
|
37
37
|
const savedContainerEvent = "saved";
|
|
38
|
+
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
38
39
|
/**
|
|
39
40
|
* Waits until container connects to delta storage and gets up-to-date.
|
|
40
41
|
*
|
|
@@ -164,8 +165,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
164
165
|
this.attachStarted = false;
|
|
165
166
|
this._dirtyContainer = false;
|
|
166
167
|
this.savedOps = [];
|
|
168
|
+
this.clientsWhoShouldHaveLeft = new Set();
|
|
167
169
|
this.setAutoReconnectTime = common_utils_1.performance.now();
|
|
170
|
+
this._lifecycleEvents = new common_utils_1.TypedEventEmitter();
|
|
168
171
|
this._disposed = false;
|
|
172
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
173
|
+
if (this.resolvedUrl === undefined) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)(this._loadedCodeDetails));
|
|
177
|
+
};
|
|
178
|
+
this.updateDirtyContainerState = (dirty) => {
|
|
179
|
+
if (this._dirtyContainer === dirty) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this._dirtyContainer = dirty;
|
|
183
|
+
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
184
|
+
};
|
|
169
185
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
170
186
|
this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
|
|
171
187
|
const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
|
|
@@ -210,8 +226,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
210
226
|
dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
|
|
211
227
|
dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
|
|
212
228
|
dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
|
|
213
|
-
containerLoadedFromVersionId: () => { var _a; return (_a = this.
|
|
214
|
-
containerLoadedFromVersionDate: () => { var _a; return (_a = this.
|
|
229
|
+
containerLoadedFromVersionId: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
|
|
230
|
+
containerLoadedFromVersionDate: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
|
|
215
231
|
// message information to associate errors with the specific execution state
|
|
216
232
|
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
217
233
|
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; },
|
|
@@ -262,6 +278,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
262
278
|
this.connect();
|
|
263
279
|
}
|
|
264
280
|
},
|
|
281
|
+
clientShouldHaveLeft: (clientId) => {
|
|
282
|
+
this.clientsWhoShouldHaveLeft.add(clientId);
|
|
283
|
+
},
|
|
265
284
|
}, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
|
|
266
285
|
this.on(savedContainerEvent, () => {
|
|
267
286
|
this.connectionStateHandler.containerSaved();
|
|
@@ -304,6 +323,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
304
323
|
static async load(loadProps, createProps) {
|
|
305
324
|
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
306
325
|
const container = new Container(createProps, loadProps);
|
|
326
|
+
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
307
327
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
308
328
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
309
329
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
@@ -331,7 +351,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
331
351
|
container.close(err);
|
|
332
352
|
onClosed(err);
|
|
333
353
|
});
|
|
334
|
-
}), { start: true, end: true, cancel: "generic" });
|
|
354
|
+
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
335
355
|
}
|
|
336
356
|
/**
|
|
337
357
|
* Create a new container in a detached state.
|
|
@@ -370,14 +390,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
370
390
|
this._lifecycleState === "disposing" ||
|
|
371
391
|
this._lifecycleState === "disposed");
|
|
372
392
|
}
|
|
373
|
-
get
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
get context() {
|
|
377
|
-
if (this._context === undefined) {
|
|
378
|
-
throw new container_utils_1.GenericError("Attempted to access context before it was defined");
|
|
393
|
+
get runtime() {
|
|
394
|
+
if (this._runtime === undefined) {
|
|
395
|
+
throw new Error("Attempted to access runtime before it was defined");
|
|
379
396
|
}
|
|
380
|
-
return this.
|
|
397
|
+
return this._runtime;
|
|
381
398
|
}
|
|
382
399
|
get protocolHandler() {
|
|
383
400
|
if (this._protocolHandler === undefined) {
|
|
@@ -406,15 +423,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
406
423
|
*/
|
|
407
424
|
return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
|
|
408
425
|
}
|
|
409
|
-
get loadedFromVersion() {
|
|
410
|
-
return this._loadedFromVersion;
|
|
411
|
-
}
|
|
412
426
|
get readOnlyInfo() {
|
|
413
427
|
return this._deltaManager.readOnlyInfo;
|
|
414
428
|
}
|
|
415
|
-
get closeSignal() {
|
|
416
|
-
return this._deltaManager.closeAbortController.signal;
|
|
417
|
-
}
|
|
418
429
|
/**
|
|
419
430
|
* Tracks host requiring read-only mode.
|
|
420
431
|
*/
|
|
@@ -430,13 +441,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
430
441
|
get connected() {
|
|
431
442
|
return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
|
|
432
443
|
}
|
|
433
|
-
/**
|
|
434
|
-
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
435
|
-
* configuration details returned as part of the initial connection.
|
|
436
|
-
*/
|
|
437
|
-
get serviceConfiguration() {
|
|
438
|
-
return this._deltaManager.serviceConfiguration;
|
|
439
|
-
}
|
|
440
444
|
/**
|
|
441
445
|
* The server provided id of the client.
|
|
442
446
|
* Set once this.connected is true, otherwise undefined
|
|
@@ -444,21 +448,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
444
448
|
get clientId() {
|
|
445
449
|
return this._clientId;
|
|
446
450
|
}
|
|
447
|
-
/**
|
|
448
|
-
* The server provided claims of the client.
|
|
449
|
-
* Set once this.connected is true, otherwise undefined
|
|
450
|
-
*/
|
|
451
|
-
get scopes() {
|
|
452
|
-
return this._deltaManager.connectionManager.scopes;
|
|
453
|
-
}
|
|
454
|
-
get clientDetails() {
|
|
455
|
-
return this._deltaManager.clientDetails;
|
|
456
|
-
}
|
|
457
451
|
get offlineLoadEnabled() {
|
|
458
452
|
var _a, _b;
|
|
459
453
|
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;
|
|
460
454
|
// summarizer will not have any pending state we want to save
|
|
461
|
-
return enabled && this.clientDetails.capabilities.interactive;
|
|
455
|
+
return enabled && this.deltaManager.clientDetails.capabilities.interactive;
|
|
462
456
|
}
|
|
463
457
|
/**
|
|
464
458
|
* Get the code details that are currently specified for the container.
|
|
@@ -473,8 +467,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
473
467
|
* loaded.
|
|
474
468
|
*/
|
|
475
469
|
getLoadedCodeDetails() {
|
|
476
|
-
|
|
477
|
-
return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
|
|
470
|
+
return this._loadedCodeDetails;
|
|
478
471
|
}
|
|
479
472
|
/**
|
|
480
473
|
* Retrieves the audience associated with the document
|
|
@@ -495,32 +488,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
495
488
|
*/
|
|
496
489
|
async getEntryPoint() {
|
|
497
490
|
var _a, _b;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
|
|
501
|
-
if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
|
|
502
|
-
throw new container_utils_1.UsageError("The container is disposing or disposed");
|
|
491
|
+
if (this._disposed) {
|
|
492
|
+
throw new container_utils_1.UsageError("The context is already disposed");
|
|
503
493
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const contextChangedHandler = () => {
|
|
507
|
-
resolve();
|
|
508
|
-
this.off("disposed", disposedHandler);
|
|
509
|
-
};
|
|
510
|
-
const disposedHandler = (error) => {
|
|
511
|
-
reject(error !== null && error !== void 0 ? error : "The Container is disposed");
|
|
512
|
-
this.off("contextChanged", contextChangedHandler);
|
|
513
|
-
};
|
|
514
|
-
this.once("contextChanged", contextChangedHandler);
|
|
515
|
-
this.once("disposed", disposedHandler);
|
|
516
|
-
});
|
|
517
|
-
// The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
|
|
518
|
-
// should have set this._context; making sure.
|
|
519
|
-
(0, common_utils_1.assert)(this._context !== undefined, 0x5a2 /* Context still not defined after contextChanged event */);
|
|
494
|
+
if (this._runtime !== undefined) {
|
|
495
|
+
return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
520
496
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
497
|
+
return new Promise((resolve, reject) => {
|
|
498
|
+
const runtimeInstantiatedHandler = () => {
|
|
499
|
+
var _a, _b;
|
|
500
|
+
(0, common_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
|
|
501
|
+
resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
502
|
+
this._lifecycleEvents.off("disposed", disposedHandler);
|
|
503
|
+
};
|
|
504
|
+
const disposedHandler = () => {
|
|
505
|
+
reject(new Error("ContainerContext was disposed"));
|
|
506
|
+
this._lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
507
|
+
};
|
|
508
|
+
this._lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
509
|
+
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
510
|
+
});
|
|
524
511
|
}
|
|
525
512
|
/**
|
|
526
513
|
* Retrieves the quorum associated with the document
|
|
@@ -587,7 +574,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
587
574
|
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
588
575
|
this.mc.logger.sendTelemetryEvent({
|
|
589
576
|
eventName: "ContainerDispose",
|
|
590
|
-
|
|
577
|
+
// Only log error if container isn't closed
|
|
578
|
+
category: !this.closed && error !== undefined ? "error" : "generic",
|
|
591
579
|
}, error);
|
|
592
580
|
// ! Progressing from "closed" to "disposing" is not allowed
|
|
593
581
|
if (this._lifecycleState !== "closed") {
|
|
@@ -595,7 +583,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
595
583
|
}
|
|
596
584
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
597
585
|
this.connectionStateHandler.dispose();
|
|
598
|
-
|
|
586
|
+
const maybeError = error !== undefined ? new Error(error.message) : undefined;
|
|
587
|
+
(_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
|
|
599
588
|
this.storageAdapter.dispose();
|
|
600
589
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
601
590
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -613,6 +602,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
613
602
|
}
|
|
614
603
|
finally {
|
|
615
604
|
this._lifecycleState = "disposed";
|
|
605
|
+
this._lifecycleEvents.emit("disposed");
|
|
616
606
|
}
|
|
617
607
|
}
|
|
618
608
|
closeAndGetPendingLocalState() {
|
|
@@ -632,7 +622,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
632
622
|
(0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
633
623
|
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
634
624
|
const pendingState = {
|
|
635
|
-
pendingRuntimeState: this.
|
|
625
|
+
pendingRuntimeState: this.runtime.getPendingLocalState(),
|
|
636
626
|
baseSnapshot: this.baseSnapshot,
|
|
637
627
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
638
628
|
savedOps: this.savedOps,
|
|
@@ -648,7 +638,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
648
638
|
}
|
|
649
639
|
serialize() {
|
|
650
640
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
|
|
651
|
-
const appSummary = this.
|
|
641
|
+
const appSummary = this.runtime.createSummary();
|
|
652
642
|
const protocolSummary = this.captureProtocolSummary();
|
|
653
643
|
const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
654
644
|
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
@@ -677,7 +667,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
677
667
|
if (!hasAttachmentBlobs) {
|
|
678
668
|
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
679
669
|
// semantics around what the attach means as far as async code goes.
|
|
680
|
-
const appSummary = this.
|
|
670
|
+
const appSummary = this.runtime.createSummary();
|
|
681
671
|
const protocolSummary = this.captureProtocolSummary();
|
|
682
672
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
683
673
|
// Set the state as attaching as we are starting the process of attaching container.
|
|
@@ -685,6 +675,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
685
675
|
// starting to attach the container to storage.
|
|
686
676
|
// Also, this should only be fired in detached container.
|
|
687
677
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
678
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
688
679
|
this.emit("attaching");
|
|
689
680
|
if (this.offlineLoadEnabled) {
|
|
690
681
|
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
@@ -699,7 +690,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
699
690
|
(0, common_utils_1.assert)(this.client.details.type !== summarizerClientType &&
|
|
700
691
|
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
701
692
|
this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
702
|
-
cancel: this.
|
|
693
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
703
694
|
});
|
|
704
695
|
}
|
|
705
696
|
await this.storageAdapter.connectToService(this.service);
|
|
@@ -721,10 +712,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
721
712
|
}
|
|
722
713
|
}
|
|
723
714
|
// take summary and upload
|
|
724
|
-
const appSummary = this.
|
|
715
|
+
const appSummary = this.runtime.createSummary(redirectTable);
|
|
725
716
|
const protocolSummary = this.captureProtocolSummary();
|
|
726
717
|
summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
|
|
727
718
|
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
719
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
|
|
728
720
|
this.emit("attaching");
|
|
729
721
|
if (this.offlineLoadEnabled) {
|
|
730
722
|
const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
|
|
@@ -739,6 +731,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
739
731
|
});
|
|
740
732
|
}
|
|
741
733
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
734
|
+
this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
|
|
742
735
|
this.emit("attached");
|
|
743
736
|
if (!this.closed) {
|
|
744
737
|
this.resumeInternal({
|
|
@@ -757,7 +750,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
757
750
|
}, { start: true, end: true, cancel: "generic" });
|
|
758
751
|
}
|
|
759
752
|
async request(path) {
|
|
760
|
-
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.
|
|
753
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
|
|
761
754
|
}
|
|
762
755
|
setAutoReconnectInternal(mode) {
|
|
763
756
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
@@ -823,13 +816,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
823
816
|
// Ensure connection to web socket
|
|
824
817
|
this.connectToDeltaStream(args);
|
|
825
818
|
}
|
|
826
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
827
|
-
var _a;
|
|
828
|
-
if (this.resolvedUrl === undefined) {
|
|
829
|
-
return undefined;
|
|
830
|
-
}
|
|
831
|
-
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
|
|
832
|
-
}
|
|
833
819
|
async proposeCodeDetails(codeDetails) {
|
|
834
820
|
if (!(0, container_definitions_1.isFluidCodeDetails)(codeDetails)) {
|
|
835
821
|
throw new Error("Provided codeDetails are not IFluidCodeDetails");
|
|
@@ -851,7 +837,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
851
837
|
this.deltaManager.inbound.pause(),
|
|
852
838
|
this.deltaManager.inboundSignal.pause(),
|
|
853
839
|
]);
|
|
854
|
-
if ((await this.
|
|
840
|
+
if ((await this.satisfies(codeDetails)) === true) {
|
|
855
841
|
this.deltaManager.inbound.resume();
|
|
856
842
|
this.deltaManager.inboundSignal.resume();
|
|
857
843
|
return;
|
|
@@ -860,6 +846,38 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
860
846
|
const error = new container_utils_1.GenericError("Existing context does not satisfy incoming proposal");
|
|
861
847
|
this.close(error);
|
|
862
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* Determines if the currently loaded module satisfies the incoming constraint code details
|
|
851
|
+
*/
|
|
852
|
+
async satisfies(constraintCodeDetails) {
|
|
853
|
+
var _a, _b;
|
|
854
|
+
// If we have no module, it can't satisfy anything.
|
|
855
|
+
if (this._loadedModule === undefined) {
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
const comparers = [];
|
|
859
|
+
const maybeCompareCodeLoader = this.codeLoader;
|
|
860
|
+
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
861
|
+
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
862
|
+
}
|
|
863
|
+
const maybeCompareExport = (_a = this._loadedModule) === null || _a === void 0 ? void 0 : _a.module.fluidExport;
|
|
864
|
+
if ((maybeCompareExport === null || maybeCompareExport === void 0 ? void 0 : maybeCompareExport.IFluidCodeDetailsComparer) !== undefined) {
|
|
865
|
+
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
866
|
+
}
|
|
867
|
+
// If there are no comparers, then it's impossible to know if the currently loaded package satisfies
|
|
868
|
+
// the incoming constraint, so we return false. Assuming it does not satisfy is safer, to force a reload
|
|
869
|
+
// rather than potentially running with incompatible code.
|
|
870
|
+
if (comparers.length === 0) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
for (const comparer of comparers) {
|
|
874
|
+
const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
|
|
875
|
+
if (satisfies === false) {
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
863
881
|
async getVersion(version) {
|
|
864
882
|
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
865
883
|
return versions[0];
|
|
@@ -877,7 +895,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
877
895
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
878
896
|
*/
|
|
879
897
|
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
880
|
-
var _a;
|
|
898
|
+
var _a, _b, _c;
|
|
881
899
|
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
882
900
|
// Ideally we always connect as "read" by default.
|
|
883
901
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
@@ -921,7 +939,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
921
939
|
if (this.offlineLoadEnabled) {
|
|
922
940
|
this.baseSnapshot = snapshot;
|
|
923
941
|
// Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
|
|
924
|
-
this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.
|
|
942
|
+
this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storageAdapter);
|
|
925
943
|
}
|
|
926
944
|
}
|
|
927
945
|
const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
|
|
@@ -957,7 +975,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
957
975
|
for (const message of pendingLocalState.savedOps) {
|
|
958
976
|
this.processRemoteMessage(message);
|
|
959
977
|
// allow runtime to apply stashed ops at this op's sequence number
|
|
960
|
-
await this.
|
|
978
|
+
await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
|
|
961
979
|
}
|
|
962
980
|
pendingLocalState.savedOps = [];
|
|
963
981
|
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
@@ -1196,7 +1214,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1196
1214
|
});
|
|
1197
1215
|
deltaManager.on("disconnect", (reason, error) => {
|
|
1198
1216
|
var _a;
|
|
1199
|
-
(_a = this.
|
|
1217
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
|
|
1200
1218
|
if (!this.closed) {
|
|
1201
1219
|
this.connectionStateHandler.receivedDisconnectEvent(reason, error);
|
|
1202
1220
|
}
|
|
@@ -1340,7 +1358,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1340
1358
|
return -1;
|
|
1341
1359
|
}
|
|
1342
1360
|
this.messageCountAfterDisconnection += 1;
|
|
1343
|
-
(_a = this.
|
|
1361
|
+
(_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
|
|
1344
1362
|
return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
|
|
1345
1363
|
}
|
|
1346
1364
|
processRemoteMessage(message) {
|
|
@@ -1348,24 +1366,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1348
1366
|
this.savedOps.push(message);
|
|
1349
1367
|
}
|
|
1350
1368
|
const local = this.clientId === message.clientId;
|
|
1369
|
+
// Check and report if we're getting messages from a clientId that we previously
|
|
1370
|
+
// flagged should have left, or from a client that's not in the quorum but should be
|
|
1371
|
+
if (message.clientId != null) {
|
|
1372
|
+
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
1373
|
+
if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
|
|
1374
|
+
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1375
|
+
throw new Error("Remote message's clientId is missing from the quorum");
|
|
1376
|
+
}
|
|
1377
|
+
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
1378
|
+
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
1379
|
+
// document we don't need to blow up aggressively.
|
|
1380
|
+
if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
1381
|
+
!(0, driver_utils_1.canBeCoalescedByService)(message)) {
|
|
1382
|
+
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1383
|
+
throw new Error("Remote message's clientId already should have left");
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1351
1386
|
// Allow the protocol handler to process the message
|
|
1352
1387
|
const result = this.protocolHandler.processMessage(message, local);
|
|
1353
1388
|
// Forward messages to the loaded runtime for processing
|
|
1354
|
-
this.
|
|
1389
|
+
this.runtime.process(message, local);
|
|
1355
1390
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1356
1391
|
if (this.activeConnection()) {
|
|
1357
|
-
if (this.
|
|
1392
|
+
if (this.noopHeuristic === undefined) {
|
|
1393
|
+
const serviceConfiguration = this.deltaManager.serviceConfiguration;
|
|
1358
1394
|
// Note that config from first connection will be used for this container's lifetime.
|
|
1359
1395
|
// That means that if relay service changes settings, such changes will impact only newly booted
|
|
1360
1396
|
// clients.
|
|
1361
1397
|
// All existing will continue to use settings they got earlier.
|
|
1362
|
-
(0, common_utils_1.assert)(
|
|
1363
|
-
this.
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1398
|
+
(0, common_utils_1.assert)(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1399
|
+
this.noopHeuristic = new noopHeuristic_1.NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
|
|
1400
|
+
this.noopHeuristic.on("wantsNoop", () => {
|
|
1401
|
+
// On disconnect we notify the heuristic which should prevent it from wanting a noop.
|
|
1402
|
+
// Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
|
|
1403
|
+
// running the microtask that the heuristic queued in response.
|
|
1404
|
+
(0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
|
|
1405
|
+
this.submitMessage(protocol_definitions_1.MessageType.NoOp);
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
this.noopHeuristic.notifyMessageProcessed(message);
|
|
1409
|
+
// The contract with the protocolHandler is that returning "immediateNoOp" is equivalent to "please immediately accept the proposal I just processed".
|
|
1410
|
+
if (result.immediateNoOp === true) {
|
|
1411
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
1412
|
+
this.submitMessage(driver_utils_1.MessageType2.Accept);
|
|
1367
1413
|
}
|
|
1368
|
-
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1369
1414
|
}
|
|
1370
1415
|
this.emit("op", message);
|
|
1371
1416
|
}
|
|
@@ -1379,7 +1424,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1379
1424
|
}
|
|
1380
1425
|
else {
|
|
1381
1426
|
const local = this.clientId === message.clientId;
|
|
1382
|
-
this.
|
|
1427
|
+
this.runtime.processSignal(message, local);
|
|
1383
1428
|
}
|
|
1384
1429
|
}
|
|
1385
1430
|
/**
|
|
@@ -1412,21 +1457,37 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1412
1457
|
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
1413
1458
|
}
|
|
1414
1459
|
async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
|
|
1415
|
-
var _a;
|
|
1416
|
-
(0, common_utils_1.assert)(((_a = this.
|
|
1460
|
+
var _a, _b;
|
|
1461
|
+
(0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
|
|
1417
1462
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1418
1463
|
// are set. Global requests will still go directly to the loader
|
|
1419
1464
|
const maybeLoader = this.scope;
|
|
1420
1465
|
const loader = new loader_1.RelativeLoader(this, maybeLoader.ILoader);
|
|
1421
|
-
|
|
1422
|
-
this.
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1466
|
+
const loadCodeResult = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "CodeLoad" }, async () => this.codeLoader.load(codeDetails));
|
|
1467
|
+
this._loadedModule = {
|
|
1468
|
+
module: loadCodeResult.module,
|
|
1469
|
+
// An older interface ICodeLoader could return an IFluidModule which didn't have details.
|
|
1470
|
+
// If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
|
|
1471
|
+
// TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
|
|
1472
|
+
details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
|
|
1473
|
+
};
|
|
1474
|
+
const fluidExport = this._loadedModule.module.fluidExport;
|
|
1475
|
+
const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
|
|
1476
|
+
if (runtimeFactory === undefined) {
|
|
1477
|
+
throw new Error(packageNotFactoryError);
|
|
1427
1478
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1479
|
+
const deltaManagerProxy = new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager);
|
|
1480
|
+
const quorumProxy = new quorum_1.QuorumProxy(this.protocolHandler.quorum);
|
|
1481
|
+
const context = new containerContext_1.ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, deltaManagerProxy, this.storageAdapter, quorumProxy, 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, () => deltaManagerProxy.serviceConfiguration, () => this.attachState, () => this.connected, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
|
|
1482
|
+
this._lifecycleEvents.once("disposed", () => {
|
|
1483
|
+
context.dispose();
|
|
1484
|
+
quorumProxy.dispose();
|
|
1485
|
+
deltaManagerProxy.dispose();
|
|
1486
|
+
});
|
|
1487
|
+
this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
|
|
1488
|
+
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1489
|
+
this._loadedCodeDetails = codeDetails;
|
|
1490
|
+
this.emit("contextChanged", codeDetails);
|
|
1430
1491
|
}
|
|
1431
1492
|
/**
|
|
1432
1493
|
* Set the connected state of the ContainerContext
|
|
@@ -1436,17 +1497,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1436
1497
|
*/
|
|
1437
1498
|
setContextConnectedState(state, readonly) {
|
|
1438
1499
|
var _a;
|
|
1439
|
-
if (((_a = this.
|
|
1500
|
+
if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1440
1501
|
/**
|
|
1441
1502
|
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1442
1503
|
* ops getting through to the DeltaManager.
|
|
1443
1504
|
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1444
1505
|
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1445
1506
|
*/
|
|
1446
|
-
this.
|
|
1507
|
+
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
1447
1508
|
}
|
|
1448
1509
|
}
|
|
1449
1510
|
}
|
|
1450
1511
|
exports.Container = Container;
|
|
1451
|
-
Container.version = "^0.1.0";
|
|
1452
1512
|
//# sourceMappingURL=container.js.map
|