@fluidframework/container-loader 1.0.1 → 1.1.0-75972
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/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +3 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +44 -10
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +90 -35
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +2 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +48 -70
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +2 -2
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +4 -4
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +9 -1
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +8 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -4
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -2
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +4 -2
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +44 -10
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +90 -35
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +2 -2
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +49 -71
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +2 -2
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +4 -4
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +1 -1
- package/lib/contracts.js +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +9 -1
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +8 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +2 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +4 -4
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +3 -2
- package/lib/utils.js.map +1 -1
- package/package.json +14 -27
- package/src/connectionManager.ts +4 -6
- package/src/connectionStateHandler.ts +113 -54
- package/src/container.ts +66 -89
- package/src/containerStorageAdapter.ts +4 -4
- package/src/contracts.ts +1 -1
- package/src/deltaManager.ts +8 -2
- package/src/deltaQueue.ts +7 -3
- package/src/packageVersion.ts +1 -1
- package/src/retriableDocumentStorageService.ts +4 -4
- package/src/utils.ts +3 -2
package/lib/container.js
CHANGED
|
@@ -9,7 +9,7 @@ import { assert, performance, unreachableCase } from "@fluidframework/common-uti
|
|
|
9
9
|
import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
|
|
10
10
|
import { DataCorruptionError, extractSafePropertiesFromMessage, GenericError, UsageError, } from "@fluidframework/container-utils";
|
|
11
11
|
import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, } from "@fluidframework/driver-utils";
|
|
12
|
-
import { isSystemMessage,
|
|
12
|
+
import { isSystemMessage, ProtocolOpHandlerWithClientValidation, } from "@fluidframework/protocol-base";
|
|
13
13
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
14
14
|
import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
15
15
|
import { Audience } from "./audience";
|
|
@@ -110,7 +110,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
110
110
|
// Tells if container can reconnect on losing fist connection
|
|
111
111
|
// If false, container gets closed on loss of connection.
|
|
112
112
|
this._canReconnect = true;
|
|
113
|
-
this._lifecycleState = "
|
|
113
|
+
this._lifecycleState = "loading";
|
|
114
114
|
this._attachState = AttachState.Detached;
|
|
115
115
|
this.resumedOpProcessingAfterLoad = false;
|
|
116
116
|
this.firstConnection = true;
|
|
@@ -166,16 +166,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
166
166
|
logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
|
|
167
167
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
168
168
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
169
|
-
logConnectionIssue: (eventName) => {
|
|
169
|
+
logConnectionIssue: (eventName, details) => {
|
|
170
170
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
171
171
|
// its own join op. Attempt recovery option.
|
|
172
|
-
this._deltaManager.logConnectionIssue({
|
|
173
|
-
eventName,
|
|
174
|
-
duration: performance.now() - this.connectionTransitionTimes[ConnectionState.CatchingUp],
|
|
175
|
-
});
|
|
172
|
+
this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: performance.now() - this.connectionTransitionTimes[ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
|
|
176
173
|
},
|
|
177
174
|
connectionStateChanged: () => {
|
|
178
|
-
if
|
|
175
|
+
// Fire events only if container is fully loaded and not closed
|
|
176
|
+
if (this._lifecycleState === "loaded") {
|
|
179
177
|
this.propagateConnectionState();
|
|
180
178
|
}
|
|
181
179
|
},
|
|
@@ -261,7 +259,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
261
259
|
});
|
|
262
260
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
263
261
|
var _a, _b;
|
|
264
|
-
container._lifecycleState = "loading";
|
|
265
262
|
const version = loadOptions.version;
|
|
266
263
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
267
264
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
@@ -296,7 +293,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
296
293
|
static async createDetached(loader, codeDetails) {
|
|
297
294
|
const container = new Container(loader, {});
|
|
298
295
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
299
|
-
container._lifecycleState = "loading";
|
|
300
296
|
await container.createDetached(codeDetails);
|
|
301
297
|
return container;
|
|
302
298
|
}, { start: true, end: true, cancel: "generic" });
|
|
@@ -309,20 +305,16 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
309
305
|
const container = new Container(loader, {});
|
|
310
306
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
311
307
|
const deserializedSummary = JSON.parse(snapshot);
|
|
312
|
-
container._lifecycleState = "loading";
|
|
313
308
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
314
309
|
return container;
|
|
315
310
|
}, { start: true, end: true, cancel: "generic" });
|
|
316
311
|
}
|
|
317
|
-
|
|
318
|
-
return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
|
|
319
|
-
}
|
|
320
|
-
set loaded(t) {
|
|
321
|
-
assert(t, 0x27d /* "Setting loaded state to false is not supported" */);
|
|
322
|
-
assert(this._lifecycleState !== "created", 0x27e /* "Must go through loading state before loaded" */);
|
|
312
|
+
setLoaded() {
|
|
323
313
|
// It's conceivable the container could be closed when this is called
|
|
324
314
|
// Only transition states if currently loading
|
|
325
315
|
if (this._lifecycleState === "loading") {
|
|
316
|
+
// Propagate current connection state through the system.
|
|
317
|
+
this.propagateConnectionState();
|
|
326
318
|
this._lifecycleState = "loaded";
|
|
327
319
|
}
|
|
328
320
|
}
|
|
@@ -444,19 +436,30 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
444
436
|
return this.protocolHandler.quorum;
|
|
445
437
|
}
|
|
446
438
|
close(error) {
|
|
439
|
+
// 1. Ensure that close sequence is exactly the same no matter if it's initiated by host or by DeltaManager
|
|
440
|
+
// 2. We need to ensure that we deliver disconnect event to runtime properly. See connectionStateChanged
|
|
441
|
+
// handler. We only deliver events if container fully loaded. Transitioning from "loading" ->
|
|
442
|
+
// "closing" will lose that info (can also solve by tracking extra state).
|
|
443
|
+
this._deltaManager.close(error);
|
|
444
|
+
assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
445
|
+
assert(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
|
|
446
|
+
}
|
|
447
|
+
closeCore(error) {
|
|
447
448
|
var _a, _b, _c, _d;
|
|
448
|
-
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
449
|
+
assert(!this.closed, 0x315 /* re-entrancy */);
|
|
451
450
|
try {
|
|
452
|
-
this._lifecycleState = "closing";
|
|
453
451
|
// Ensure that we raise all key events even if one of these throws
|
|
454
452
|
try {
|
|
455
|
-
|
|
453
|
+
// Raise event first, to ensure we capture _lifecycleState before transition.
|
|
454
|
+
// This gives us a chance to know what errors happened on open vs. on fully loaded container.
|
|
455
|
+
this.mc.logger.sendTelemetryEvent({
|
|
456
|
+
eventName: "ContainerClose",
|
|
457
|
+
category: error === undefined ? "generic" : "error",
|
|
458
|
+
}, error);
|
|
459
|
+
this._lifecycleState = "closing";
|
|
456
460
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
457
461
|
this.connectionStateHandler.dispose();
|
|
458
462
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
459
|
-
assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
460
463
|
(_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
|
|
461
464
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
462
465
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
@@ -466,10 +469,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
466
469
|
catch (exception) {
|
|
467
470
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
468
471
|
}
|
|
469
|
-
this.mc.logger.sendTelemetryEvent({
|
|
470
|
-
eventName: "ContainerClose",
|
|
471
|
-
category: error === undefined ? "generic" : "error",
|
|
472
|
-
}, error);
|
|
473
472
|
this.emit("closed", error);
|
|
474
473
|
this.removeAllListeners();
|
|
475
474
|
if (this.visibilityEventHandler !== undefined) {
|
|
@@ -487,11 +486,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
487
486
|
assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
488
487
|
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
489
488
|
assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
489
|
+
assert(this._protocolHandler.attributes.term !== undefined, 0x30b /* Must have a valid protocol handler instance */);
|
|
490
490
|
const pendingState = {
|
|
491
491
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
492
492
|
url: this.resolvedUrl.url,
|
|
493
|
-
protocol: this.
|
|
494
|
-
term: this._protocolHandler.term,
|
|
493
|
+
protocol: this.protocolHandler.getProtocolState(),
|
|
494
|
+
term: this._protocolHandler.attributes.term,
|
|
495
495
|
clientId: this.clientId,
|
|
496
496
|
};
|
|
497
497
|
this.close();
|
|
@@ -790,10 +790,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
790
790
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
791
791
|
await this.instantiateContext(true, // existing
|
|
792
792
|
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
793
|
-
// Propagate current connection state through the system.
|
|
794
|
-
this.propagateConnectionState();
|
|
795
793
|
// Internal context is fully loaded at this point
|
|
796
|
-
this.
|
|
794
|
+
this.setLoaded();
|
|
797
795
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
798
796
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
799
797
|
if (!this.closed) {
|
|
@@ -849,8 +847,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
849
847
|
qValues);
|
|
850
848
|
// The load context - given we seeded the quorum - will be great
|
|
851
849
|
await this.instantiateContextDetached(false);
|
|
852
|
-
this.
|
|
853
|
-
this.loaded = true;
|
|
850
|
+
this.setLoaded();
|
|
854
851
|
}
|
|
855
852
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
856
853
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
@@ -871,8 +868,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
871
868
|
codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : []);
|
|
872
869
|
await this.instantiateContextDetached(true, // existing
|
|
873
870
|
snapshotTree);
|
|
874
|
-
this.
|
|
875
|
-
this.propagateConnectionState();
|
|
871
|
+
this.setLoaded();
|
|
876
872
|
}
|
|
877
873
|
async connectStorageService() {
|
|
878
874
|
var _a, _b;
|
|
@@ -926,7 +922,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
926
922
|
return protocolHandler;
|
|
927
923
|
}
|
|
928
924
|
async initializeProtocolState(attributes, members, proposals, values) {
|
|
929
|
-
const protocol = new
|
|
925
|
+
const protocol = new ProtocolOpHandlerWithClientValidation(attributes.minimumSequenceNumber, attributes.sequenceNumber, attributes.term, members, proposals, values, (key, value) => this.submitMessage(MessageType.Propose, { key, value }));
|
|
930
926
|
const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
931
927
|
protocol.quorum.on("error", (error) => {
|
|
932
928
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -954,17 +950,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
954
950
|
return protocol;
|
|
955
951
|
}
|
|
956
952
|
captureProtocolSummary() {
|
|
957
|
-
const quorumSnapshot = this.protocolHandler.
|
|
958
|
-
// Save attributes for the document
|
|
959
|
-
const documentAttributes = {
|
|
960
|
-
minimumSequenceNumber: this.protocolHandler.minimumSequenceNumber,
|
|
961
|
-
sequenceNumber: this.protocolHandler.sequenceNumber,
|
|
962
|
-
term: this.protocolHandler.term,
|
|
963
|
-
};
|
|
953
|
+
const quorumSnapshot = this.protocolHandler.snapshot();
|
|
964
954
|
const summary = {
|
|
965
955
|
tree: {
|
|
966
956
|
attributes: {
|
|
967
|
-
content: JSON.stringify(
|
|
957
|
+
content: JSON.stringify(this.protocolHandler.attributes),
|
|
968
958
|
type: SummaryType.Blob,
|
|
969
959
|
},
|
|
970
960
|
quorumMembers: {
|
|
@@ -1053,7 +1043,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1053
1043
|
this.emit("readonly", readonly);
|
|
1054
1044
|
});
|
|
1055
1045
|
deltaManager.on("closed", (error) => {
|
|
1056
|
-
this.
|
|
1046
|
+
this.closeCore(error);
|
|
1057
1047
|
});
|
|
1058
1048
|
return deltaManager;
|
|
1059
1049
|
}
|
|
@@ -1109,6 +1099,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1109
1099
|
}
|
|
1110
1100
|
}
|
|
1111
1101
|
propagateConnectionState() {
|
|
1102
|
+
var _a;
|
|
1112
1103
|
const logOpsOnReconnect = this.connectionState === ConnectionState.Connected &&
|
|
1113
1104
|
!this.firstConnection &&
|
|
1114
1105
|
this.connectionMode === "write";
|
|
@@ -1116,11 +1107,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1116
1107
|
this.messageCountAfterDisconnection = 0;
|
|
1117
1108
|
}
|
|
1118
1109
|
const state = this.connectionState === ConnectionState.Connected;
|
|
1119
|
-
if
|
|
1110
|
+
// Both protocol and context should not be undefined if we got so far.
|
|
1111
|
+
if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1120
1112
|
this.context.setConnectionState(state, this.clientId);
|
|
1121
1113
|
}
|
|
1122
|
-
|
|
1123
|
-
this.protocolHandler.quorum.setConnectionState(state, this.clientId);
|
|
1114
|
+
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1124
1115
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId);
|
|
1125
1116
|
if (logOpsOnReconnect) {
|
|
1126
1117
|
this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
|
|
@@ -1162,32 +1153,19 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1162
1153
|
return this._deltaManager.submit(type, contents, batch, metadata);
|
|
1163
1154
|
}
|
|
1164
1155
|
processRemoteMessage(message) {
|
|
1165
|
-
var _a, _b;
|
|
1166
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
1167
|
-
// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
|
|
1168
|
-
if (message.clientId != null) {
|
|
1169
|
-
let errorMsg;
|
|
1170
|
-
const client = this.getQuorum().getMember(message.clientId);
|
|
1171
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
1172
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
1173
|
-
errorMsg = "Remote message's clientId is missing from the quorum";
|
|
1174
|
-
}
|
|
1175
|
-
else if ((client === null || client === void 0 ? void 0 : client.shouldHaveLeft) === true && message.type !== MessageType.NoOp) {
|
|
1176
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
1177
|
-
errorMsg = "Remote message's clientId already should have left";
|
|
1178
|
-
}
|
|
1179
|
-
if (errorMsg !== undefined) {
|
|
1180
|
-
const error = new DataCorruptionError(errorMsg, extractSafePropertiesFromMessage(message));
|
|
1181
|
-
this.close(normalizeError(error));
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
1156
|
const local = this.clientId === message.clientId;
|
|
1157
|
+
// Allow the protocol handler to process the message
|
|
1158
|
+
let result = { immediateNoOp: false };
|
|
1159
|
+
try {
|
|
1160
|
+
result = this.protocolHandler.processMessage(message, local);
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
this.close(wrapError(error, (errorMessage) => new DataCorruptionError(errorMessage, extractSafePropertiesFromMessage(message))));
|
|
1164
|
+
}
|
|
1185
1165
|
// Forward non system messages to the loaded runtime for processing
|
|
1186
1166
|
if (!isSystemMessage(message)) {
|
|
1187
1167
|
this.context.process(message, local, undefined);
|
|
1188
1168
|
}
|
|
1189
|
-
// Allow the protocol handler to process the message
|
|
1190
|
-
const result = this.protocolHandler.processMessage(message, local);
|
|
1191
1169
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1192
1170
|
if (this.activeConnection()) {
|
|
1193
1171
|
if (this.collabWindowTracker === undefined) {
|
|
@@ -1199,7 +1177,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1199
1177
|
this.collabWindowTracker = new CollabWindowTracker((type, contents) => {
|
|
1200
1178
|
assert(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
|
|
1201
1179
|
this.submitMessage(type, contents);
|
|
1202
|
-
},
|
|
1180
|
+
}, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
|
|
1203
1181
|
}
|
|
1204
1182
|
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1205
1183
|
}
|