@fluidframework/container-loader 2.0.0-internal.1.1.3 → 2.0.0-internal.1.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.
Files changed (92) hide show
  1. package/dist/catchUpMonitor.d.ts +6 -17
  2. package/dist/catchUpMonitor.d.ts.map +1 -1
  3. package/dist/catchUpMonitor.js +5 -36
  4. package/dist/catchUpMonitor.js.map +1 -1
  5. package/dist/collabWindowTracker.d.ts +1 -1
  6. package/dist/collabWindowTracker.d.ts.map +1 -1
  7. package/dist/collabWindowTracker.js +2 -1
  8. package/dist/collabWindowTracker.js.map +1 -1
  9. package/dist/connectionManager.d.ts +1 -1
  10. package/dist/connectionManager.d.ts.map +1 -1
  11. package/dist/connectionManager.js +8 -11
  12. package/dist/connectionManager.js.map +1 -1
  13. package/dist/connectionStateHandler.d.ts +80 -26
  14. package/dist/connectionStateHandler.d.ts.map +1 -1
  15. package/dist/connectionStateHandler.js +170 -89
  16. package/dist/connectionStateHandler.js.map +1 -1
  17. package/dist/container.d.ts +22 -11
  18. package/dist/container.d.ts.map +1 -1
  19. package/dist/container.js +107 -125
  20. package/dist/container.js.map +1 -1
  21. package/dist/containerContext.d.ts +18 -7
  22. package/dist/containerContext.d.ts.map +1 -1
  23. package/dist/containerContext.js +18 -8
  24. package/dist/containerContext.js.map +1 -1
  25. package/dist/containerStorageAdapter.d.ts +10 -24
  26. package/dist/containerStorageAdapter.d.ts.map +1 -1
  27. package/dist/containerStorageAdapter.js +50 -16
  28. package/dist/containerStorageAdapter.js.map +1 -1
  29. package/dist/deltaManager.d.ts +1 -1
  30. package/dist/deltaManager.d.ts.map +1 -1
  31. package/dist/deltaManager.js +18 -6
  32. package/dist/deltaManager.js.map +1 -1
  33. package/dist/loader.d.ts +1 -1
  34. package/dist/loader.js.map +1 -1
  35. package/dist/packageVersion.d.ts +1 -1
  36. package/dist/packageVersion.js +1 -1
  37. package/dist/packageVersion.js.map +1 -1
  38. package/dist/protocol.d.ts.map +1 -1
  39. package/dist/protocol.js +2 -1
  40. package/dist/protocol.js.map +1 -1
  41. package/lib/catchUpMonitor.d.ts +6 -17
  42. package/lib/catchUpMonitor.d.ts.map +1 -1
  43. package/lib/catchUpMonitor.js +5 -35
  44. package/lib/catchUpMonitor.js.map +1 -1
  45. package/lib/collabWindowTracker.d.ts +1 -1
  46. package/lib/collabWindowTracker.d.ts.map +1 -1
  47. package/lib/collabWindowTracker.js +3 -2
  48. package/lib/collabWindowTracker.js.map +1 -1
  49. package/lib/connectionManager.d.ts +1 -1
  50. package/lib/connectionManager.d.ts.map +1 -1
  51. package/lib/connectionManager.js +9 -14
  52. package/lib/connectionManager.js.map +1 -1
  53. package/lib/connectionStateHandler.d.ts +80 -26
  54. package/lib/connectionStateHandler.d.ts.map +1 -1
  55. package/lib/connectionStateHandler.js +170 -90
  56. package/lib/connectionStateHandler.js.map +1 -1
  57. package/lib/container.d.ts +22 -11
  58. package/lib/container.d.ts.map +1 -1
  59. package/lib/container.js +110 -128
  60. package/lib/container.js.map +1 -1
  61. package/lib/containerContext.d.ts +18 -7
  62. package/lib/containerContext.d.ts.map +1 -1
  63. package/lib/containerContext.js +19 -9
  64. package/lib/containerContext.js.map +1 -1
  65. package/lib/containerStorageAdapter.d.ts +10 -24
  66. package/lib/containerStorageAdapter.d.ts.map +1 -1
  67. package/lib/containerStorageAdapter.js +50 -15
  68. package/lib/containerStorageAdapter.js.map +1 -1
  69. package/lib/deltaManager.d.ts +1 -1
  70. package/lib/deltaManager.d.ts.map +1 -1
  71. package/lib/deltaManager.js +18 -6
  72. package/lib/deltaManager.js.map +1 -1
  73. package/lib/loader.d.ts +1 -1
  74. package/lib/loader.js.map +1 -1
  75. package/lib/packageVersion.d.ts +1 -1
  76. package/lib/packageVersion.js +1 -1
  77. package/lib/packageVersion.js.map +1 -1
  78. package/lib/protocol.d.ts.map +1 -1
  79. package/lib/protocol.js +2 -1
  80. package/lib/protocol.js.map +1 -1
  81. package/package.json +13 -13
  82. package/src/catchUpMonitor.ts +7 -47
  83. package/src/collabWindowTracker.ts +4 -3
  84. package/src/connectionManager.ts +9 -11
  85. package/src/connectionStateHandler.ts +231 -106
  86. package/src/container.ts +132 -147
  87. package/src/containerContext.ts +22 -8
  88. package/src/containerStorageAdapter.ts +64 -15
  89. package/src/deltaManager.ts +20 -7
  90. package/src/loader.ts +1 -1
  91. package/src/packageVersion.ts +1 -1
  92. package/src/protocol.ts +2 -1
package/dist/container.js CHANGED
@@ -24,10 +24,8 @@ const deltaManager_1 = require("./deltaManager");
24
24
  const deltaManagerProxy_1 = require("./deltaManagerProxy");
25
25
  const loader_1 = require("./loader");
26
26
  const packageVersion_1 = require("./packageVersion");
27
- const connectionStateHandler_1 = require("./connectionStateHandler");
28
- const retriableDocumentStorageService_1 = require("./retriableDocumentStorageService");
29
- const protocolTreeDocumentStorageService_1 = require("./protocolTreeDocumentStorageService");
30
27
  const containerStorageAdapter_1 = require("./containerStorageAdapter");
28
+ const connectionStateHandler_1 = require("./connectionStateHandler");
31
29
  const utils_1 = require("./utils");
32
30
  const quorum_1 = require("./quorum");
33
31
  const collabWindowTracker_1 = require("./collabWindowTracker");
@@ -38,14 +36,19 @@ const detachedContainerRefSeqNumber = 0;
38
36
  const dirtyContainerEvent = "dirty";
39
37
  const savedContainerEvent = "saved";
40
38
  /**
41
- * Waits until container connects to delta storage and gets up-to-date
39
+ * Waits until container connects to delta storage and gets up-to-date.
40
+ *
42
41
  * Useful when resolving URIs and hitting 404, due to container being loaded from (stale) snapshot and not being
43
42
  * up to date. Host may chose to wait in such case and retry resolving URI.
43
+ *
44
44
  * Warning: Will wait infinitely for connection to establish if there is no connection.
45
45
  * May result in deadlock if Container.disconnect() is called and never followed by a call to Container.connect().
46
- * @returns true: container is up to date, it processed all the ops that were know at the time of first connection
47
- * false: storage does not provide indication of how far the client is. Container processed
48
- * all the ops known to it, but it maybe still behind.
46
+ *
47
+ * @returns `true`: container is up to date, it processed all the ops that were know at the time of first connection.
48
+ *
49
+ * `false`: storage does not provide indication of how far the client is. Container processed all the ops known to it,
50
+ * but it maybe still behind.
51
+ *
49
52
  * @throws an error beginning with `"Container closed"` if the container is closed before it catches up.
50
53
  */
51
54
  async function waitContainerToCatchUp(container) {
@@ -190,9 +193,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
190
193
  this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
191
194
  const summarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree")) !== null && _a !== void 0 ? _a : this.loader.services.options.summarizeProtocolTree;
192
195
  this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
193
- this.connectionStateHandler = new connectionStateHandler_1.ConnectionStateHandler({
194
- quorumClients: () => { var _a; return (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.quorum; },
195
- logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
196
+ this._deltaManager = this.createDeltaManager();
197
+ this._clientId = (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId;
198
+ this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
199
+ logger: this.mc.logger,
200
+ connectionStateChanged: (value, oldState, reason) => {
201
+ if (value === connectionState_1.ConnectionState.Connected) {
202
+ this._clientId = this.connectionStateHandler.pendingClientId;
203
+ }
204
+ this.logConnectionStateChangeTelemetry(value, oldState, reason);
205
+ if (this._lifecycleState === "loaded") {
206
+ this.propagateConnectionState(false /* initial transition */);
207
+ }
208
+ },
196
209
  shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
197
210
  maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
198
211
  logConnectionIssue: (eventName, details) => {
@@ -200,29 +213,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
200
213
  // its own join op. Attempt recovery option.
201
214
  this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: common_utils_1.performance.now() - this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
202
215
  },
203
- connectionStateChanged: () => {
204
- // Fire events only if container is fully loaded and not closed
205
- if (this._lifecycleState === "loaded") {
206
- this.propagateConnectionState();
207
- }
208
- },
209
- }, this.mc.logger, (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId);
216
+ }, this.deltaManager, this._clientId);
210
217
  this.on(savedContainerEvent, () => {
211
218
  this.connectionStateHandler.containerSaved();
212
219
  });
213
- this._deltaManager = this.createDeltaManager();
214
- this._storage = new containerStorageAdapter_1.ContainerStorageAdapter(() => {
215
- if (this.attachState !== container_definitions_1.AttachState.Attached) {
216
- if (this.loader.services.detachedBlobStorage !== undefined) {
217
- return new containerStorageAdapter_1.BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.mc.logger);
218
- }
219
- this.mc.logger.sendErrorEvent({
220
- eventName: "NoRealStorageInDetachedContainer",
221
- });
222
- throw new Error("Real storage calls not allowed in Unattached container");
223
- }
224
- return this.storageService;
225
- });
220
+ this.storageService = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, this.options.summarizeProtocolTree === true
221
+ ? () => this.captureProtocolSummary()
222
+ : undefined);
226
223
  const isDomAvailable = typeof document === "object" &&
227
224
  document !== null &&
228
225
  typeof document.addEventListener === "function" &&
@@ -343,7 +340,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
343
340
  // Only transition states if currently loading
344
341
  if (this._lifecycleState === "loading") {
345
342
  // Propagate current connection state through the system.
346
- this.propagateConnectionState();
343
+ this.propagateConnectionState(true /* initial transition */);
347
344
  this._lifecycleState = "loaded";
348
345
  }
349
346
  }
@@ -351,13 +348,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
351
348
  return (this._lifecycleState === "closing" || this._lifecycleState === "closed");
352
349
  }
353
350
  get storage() {
354
- return this._storage;
355
- }
356
- get storageService() {
357
- if (this._storageService === undefined) {
358
- throw new Error("Attempted to access storageService before it was defined");
359
- }
360
- return this._storageService;
351
+ return this.storageService;
361
352
  }
362
353
  get context() {
363
354
  if (this._context === undefined) {
@@ -398,7 +389,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
398
389
  return this.connectionStateHandler.connectionState;
399
390
  }
400
391
  get connected() {
401
- return this.connectionStateHandler.connected;
392
+ return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
402
393
  }
403
394
  /**
404
395
  * Service configuration details. If running in offline mode will be undefined otherwise will contain service
@@ -412,7 +403,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
412
403
  * Set once this.connected is true, otherwise undefined
413
404
  */
414
405
  get clientId() {
415
- return this.connectionStateHandler.clientId;
406
+ return this._clientId;
416
407
  }
417
408
  /**
418
409
  * The server provided claims of the client.
@@ -474,7 +465,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
474
465
  (0, common_utils_1.assert)(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
475
466
  }
476
467
  closeCore(error) {
477
- var _a, _b, _c, _d;
468
+ var _a, _b, _c;
478
469
  (0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
479
470
  try {
480
471
  // Ensure that we raise all key events even if one of these throws
@@ -489,11 +480,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
489
480
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
490
481
  this.connectionStateHandler.dispose();
491
482
  (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
492
- (_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
483
+ this.storageService.dispose();
493
484
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
494
485
  // about file, like file being overwritten in storage, but client having stale local cache.
495
486
  // Driver need to ensure all caches are cleared on critical errors
496
- (_d = this.service) === null || _d === void 0 ? void 0 : _d.dispose(error);
487
+ (_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
497
488
  }
498
489
  catch (exception) {
499
490
  this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
@@ -580,7 +571,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
580
571
  const resolvedUrl = this.service.resolvedUrl;
581
572
  (0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
582
573
  this._resolvedUrl = resolvedUrl;
583
- await this.connectStorageService();
574
+ await this.storageService.connectToService(this.service);
584
575
  if (hasAttachmentBlobs) {
585
576
  // upload blobs to storage
586
577
  (0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
@@ -610,8 +601,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
610
601
  }
611
602
  this._attachState = container_definitions_1.AttachState.Attached;
612
603
  this.emit("attached");
613
- // Propagate current connection state through the system.
614
- this.propagateConnectionState();
615
604
  if (!this.closed) {
616
605
  this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
617
606
  }
@@ -750,9 +739,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
750
739
  /**
751
740
  * Load container.
752
741
  *
753
- * @param specifiedVersion - one of the following
754
- * - undefined - fetch latest snapshot
755
- * - otherwise, version sha to load snapshot
742
+ * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
756
743
  */
757
744
  async load(specifiedVersion, loadMode, pendingLocalState) {
758
745
  if (this._resolvedUrl === undefined) {
@@ -775,11 +762,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
775
762
  this.connectToDeltaStream(connectionArgs);
776
763
  }
777
764
  if (!pendingLocalState) {
778
- await this.connectStorageService();
765
+ await this.storageService.connectToService(this.service);
779
766
  }
780
767
  else {
781
768
  // if we have pendingLocalState we can load without storage; don't wait for connection
782
- this.connectStorageService().catch((error) => this.close(error));
769
+ this.storageService.connectToService(this.service).catch((error) => this.close(error));
783
770
  }
784
771
  this._attachState = container_definitions_1.AttachState.Attached;
785
772
  // Fetch specified snapshot.
@@ -892,12 +879,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
892
879
  delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
893
880
  }
894
881
  const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
895
- this._storage.loadSnapshotForRehydratingContainer(snapshotTree);
896
- const attributes = await this.getDocumentAttributes(this._storage, snapshotTree);
882
+ this.storageService.loadSnapshotForRehydratingContainer(snapshotTree);
883
+ const attributes = await this.getDocumentAttributes(this.storageService, snapshotTree);
897
884
  await this.attachDeltaManagerOpHandler(attributes);
898
885
  // Initialize the protocol handler
899
886
  const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
900
- const qValues = await (0, driver_utils_1.readAndParse)(this._storage, baseTree.blobs.quorumValues);
887
+ const qValues = await (0, driver_utils_1.readAndParse)(this.storageService, baseTree.blobs.quorumValues);
901
888
  const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
902
889
  this.initializeProtocolState(attributes, {
903
890
  members: [],
@@ -908,23 +895,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
908
895
  snapshotTree);
909
896
  this.setLoaded();
910
897
  }
911
- async connectStorageService() {
912
- var _a, _b;
913
- if (this._storageService !== undefined) {
914
- return;
915
- }
916
- (0, common_utils_1.assert)(this.service !== undefined, 0x1ef /* "services must be defined" */);
917
- const storageService = await this.service.connectToStorage();
918
- this._storageService =
919
- new retriableDocumentStorageService_1.RetriableDocumentStorageService(storageService, this.mc.logger);
920
- if (this.options.summarizeProtocolTree === true) {
921
- this.mc.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
922
- this._storageService =
923
- new protocolTreeDocumentStorageService_1.ProtocolTreeStorageService(this._storageService, () => this.captureProtocolSummary());
924
- }
925
- // ensure we did not lose that policy in the process of wrapping
926
- (0, common_utils_1.assert)(((_a = storageService.policies) === null || _a === void 0 ? void 0 : _a.minBlobSize) === ((_b = this.storageService.policies) === null || _b === void 0 ? void 0 : _b.minBlobSize), 0x0e0 /* "lost minBlobSize policy" */);
927
- }
928
898
  async getDocumentAttributes(storage, tree) {
929
899
  if (tree === undefined) {
930
900
  return {
@@ -963,7 +933,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
963
933
  initializeProtocolState(attributes, quorumSnapshot) {
964
934
  var _a, _b;
965
935
  const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
966
- const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, { key, value }), (_b = this._initialClients) !== null && _b !== void 0 ? _b : []);
936
+ const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })), (_b = this._initialClients) !== null && _b !== void 0 ? _b : []);
967
937
  this._initialClients = undefined;
968
938
  const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
969
939
  protocol.quorum.on("error", (error) => {
@@ -1077,10 +1047,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1077
1047
  this._protocolHandler.audience.addMember(priorClient.clientId, priorClient.client);
1078
1048
  }
1079
1049
  }
1080
- const deltaManagerForCatchingUp = this.mc.config.getBoolean("Fluid.Container.CatchUpBeforeDeclaringConnected") === true ?
1081
- this.deltaManager
1082
- : undefined;
1083
- this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details, deltaManagerForCatchingUp);
1050
+ this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
1084
1051
  });
1085
1052
  deltaManager.on("disconnect", (reason) => {
1086
1053
  var _a;
@@ -1097,6 +1064,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1097
1064
  this.emit("warning", warn);
1098
1065
  });
1099
1066
  deltaManager.on("readonly", (readonly) => {
1067
+ this.setContextConnectedState(this.connectionState === connectionState_1.ConnectionState.Connected, readonly);
1100
1068
  this.emit("readonly", readonly);
1101
1069
  });
1102
1070
  deltaManager.on("closed", (error) => {
@@ -1139,12 +1107,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1139
1107
  opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
1140
1108
  }
1141
1109
  }
1142
- if (this.firstConnection) {
1143
- connectionInitiationReason = "InitialConnect";
1144
- }
1145
- else {
1146
- connectionInitiationReason = "AutoReconnect";
1147
- }
1110
+ connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
1148
1111
  }
1149
1112
  this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`, from: connectionState_1.ConnectionState[oldState], duration,
1150
1113
  durationFromDisconnected,
@@ -1155,49 +1118,64 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1155
1118
  this.firstConnection = false;
1156
1119
  }
1157
1120
  }
1158
- propagateConnectionState() {
1121
+ propagateConnectionState(initialTransition) {
1159
1122
  var _a;
1123
+ // When container loaded, we want to propagate initial connection state.
1124
+ // After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
1125
+ // This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
1126
+ if (!initialTransition &&
1127
+ this.connectionState !== connectionState_1.ConnectionState.Connected &&
1128
+ this.connectionState !== connectionState_1.ConnectionState.Disconnected) {
1129
+ return;
1130
+ }
1131
+ const state = this.connectionState === connectionState_1.ConnectionState.Connected;
1160
1132
  const logOpsOnReconnect = this.connectionState === connectionState_1.ConnectionState.Connected &&
1161
1133
  !this.firstConnection &&
1162
1134
  this.connectionMode === "write";
1163
1135
  if (logOpsOnReconnect) {
1164
1136
  this.messageCountAfterDisconnection = 0;
1165
1137
  }
1166
- const state = this.connectionState === connectionState_1.ConnectionState.Connected;
1167
1138
  // Both protocol and context should not be undefined if we got so far.
1168
- if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1169
- this.context.setConnectionState(state, this.clientId);
1170
- }
1139
+ this.setContextConnectedState(state, (_a = this._deltaManager.connectionManager.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
1171
1140
  this.protocolHandler.setConnectionState(state, this.clientId);
1172
1141
  (0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId);
1173
1142
  if (logOpsOnReconnect) {
1174
1143
  this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
1175
1144
  }
1176
1145
  }
1146
+ // back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
1177
1147
  submitContainerMessage(type, contents, batch, metadata) {
1178
- const outboundMessageType = type;
1179
- switch (outboundMessageType) {
1148
+ switch (type) {
1180
1149
  case protocol_definitions_1.MessageType.Operation:
1181
- case protocol_definitions_1.MessageType.RemoteHelp:
1182
- break;
1183
- case protocol_definitions_1.MessageType.Summarize: {
1184
- // github #6451: this is only needed for staging so the server
1185
- // know when the protocol tree is included
1186
- // this can be removed once all clients send
1187
- // protocol tree by default
1188
- const summary = contents;
1189
- if (summary.details === undefined) {
1190
- summary.details = {};
1191
- }
1192
- summary.details.includesProtocolTree =
1193
- this.options.summarizeProtocolTree === true;
1194
- break;
1195
- }
1150
+ return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
1151
+ case protocol_definitions_1.MessageType.Summarize:
1152
+ return this.submitSummaryMessage(contents);
1196
1153
  default:
1197
1154
  this.close(new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type }));
1198
1155
  return -1;
1199
1156
  }
1200
- return this.submitMessage(type, contents, batch, metadata);
1157
+ }
1158
+ /** @returns clientSequenceNumber of last message in a batch */
1159
+ submitBatch(batch) {
1160
+ let clientSequenceNumber = -1;
1161
+ for (const message of batch) {
1162
+ clientSequenceNumber = this.submitMessage(protocol_definitions_1.MessageType.Operation, message.contents, true, // batch
1163
+ message.metadata);
1164
+ }
1165
+ this._deltaManager.flush();
1166
+ return clientSequenceNumber;
1167
+ }
1168
+ submitSummaryMessage(summary) {
1169
+ // github #6451: this is only needed for staging so the server
1170
+ // know when the protocol tree is included
1171
+ // this can be removed once all clients send
1172
+ // protocol tree by default
1173
+ if (summary.details === undefined) {
1174
+ summary.details = {};
1175
+ }
1176
+ summary.details.includesProtocolTree =
1177
+ this.options.summarizeProtocolTree === true;
1178
+ return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */);
1201
1179
  }
1202
1180
  submitMessage(type, contents, batch, metadata) {
1203
1181
  var _a;
@@ -1212,22 +1190,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1212
1190
  processRemoteMessage(message) {
1213
1191
  const local = this.clientId === message.clientId;
1214
1192
  // Allow the protocol handler to process the message
1215
- let result = { immediateNoOp: false };
1216
- try {
1217
- result = this.protocolHandler.processMessage(message, local);
1218
- }
1219
- catch (error) {
1220
- this.close((0, telemetry_utils_1.wrapError)(error, (errorMessage) => new container_utils_1.DataCorruptionError(errorMessage, (0, container_utils_1.extractSafePropertiesFromMessage)(message))));
1221
- }
1222
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
1223
- if ((0, driver_utils_1.isUnpackedRuntimeMessage)(message) && !(0, driver_utils_1.isRuntimeMessage)(message)) {
1224
- this.mc.logger.sendTelemetryEvent({ eventName: "UnpackedRuntimeMessage", type: message.type });
1225
- }
1226
- // Forward non system messages to the loaded runtime for processing
1227
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
1228
- if ((0, driver_utils_1.isRuntimeMessage)(message) || (0, driver_utils_1.isUnpackedRuntimeMessage)(message)) {
1229
- this.context.process(message, local, undefined);
1230
- }
1193
+ const result = this.protocolHandler.processMessage(message, local);
1194
+ // Forward messages to the loaded runtime for processing
1195
+ this.context.process(message, local, undefined);
1231
1196
  // Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
1232
1197
  if (this.activeConnection()) {
1233
1198
  if (this.collabWindowTracker === undefined) {
@@ -1236,15 +1201,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1236
1201
  // clients.
1237
1202
  // All existing will continue to use settings they got earlier.
1238
1203
  (0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
1239
- this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type, contents) => {
1204
+ this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type) => {
1240
1205
  (0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
1241
- this.submitMessage(type, contents);
1206
+ this.submitMessage(type);
1242
1207
  }, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
1243
1208
  }
1244
1209
  this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
1245
1210
  }
1246
1211
  this.emit("op", message);
1247
- return result;
1248
1212
  }
1249
1213
  submitSignal(message) {
1250
1214
  this._deltaManager.submitSignal(JSON.stringify(message));
@@ -1291,7 +1255,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1291
1255
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1292
1256
  // are set. Global requests will still go directly to the loader
1293
1257
  const loader = new loader_1.RelativeLoader(this, this.loader);
1294
- this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1258
+ this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp) => this.submitSummaryMessage(summaryOp), (batch) => this.submitBatch(batch), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1295
1259
  this.emit("contextChanged", codeDetails);
1296
1260
  }
1297
1261
  updateDirtyContainerState(dirty) {
@@ -1304,6 +1268,24 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1304
1268
  logContainerError(warning) {
1305
1269
  this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
1306
1270
  }
1271
+ /**
1272
+ * Set the connected state of the ContainerContext
1273
+ * This controls the "connected" state of the ContainerRuntime as well
1274
+ * @param state - Is the container currently connected?
1275
+ * @param readonly - Is the container in readonly mode?
1276
+ */
1277
+ setContextConnectedState(state, readonly) {
1278
+ var _a;
1279
+ if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1280
+ /**
1281
+ * We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
1282
+ * ops getting through to the DeltaManager.
1283
+ * The ContainerRuntime's "connected" state simply means it is ok to send ops
1284
+ * See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
1285
+ */
1286
+ this.context.setConnectionState(state && !readonly, this.clientId);
1287
+ }
1288
+ }
1307
1289
  }
1308
1290
  exports.Container = Container;
1309
1291
  Container.version = "^0.1.0";