@fluidframework/container-loader 0.52.0 → 0.54.0-47413

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 (77) hide show
  1. package/dist/connectionManager.d.ts +153 -0
  2. package/dist/connectionManager.d.ts.map +1 -0
  3. package/dist/connectionManager.js +664 -0
  4. package/dist/connectionManager.js.map +1 -0
  5. package/dist/connectionStateHandler.d.ts +1 -0
  6. package/dist/connectionStateHandler.d.ts.map +1 -1
  7. package/dist/connectionStateHandler.js +6 -0
  8. package/dist/connectionStateHandler.js.map +1 -1
  9. package/dist/container.d.ts +2 -22
  10. package/dist/container.d.ts.map +1 -1
  11. package/dist/container.js +121 -151
  12. package/dist/container.js.map +1 -1
  13. package/dist/containerContext.d.ts +1 -0
  14. package/dist/containerContext.d.ts.map +1 -1
  15. package/dist/containerContext.js +4 -0
  16. package/dist/containerContext.js.map +1 -1
  17. package/dist/contracts.d.ts +112 -0
  18. package/dist/contracts.d.ts.map +1 -0
  19. package/dist/contracts.js +14 -0
  20. package/dist/contracts.js.map +1 -0
  21. package/dist/deltaManager.d.ts +26 -142
  22. package/dist/deltaManager.d.ts.map +1 -1
  23. package/dist/deltaManager.js +143 -770
  24. package/dist/deltaManager.js.map +1 -1
  25. package/dist/loader.d.ts +14 -4
  26. package/dist/loader.d.ts.map +1 -1
  27. package/dist/loader.js +10 -4
  28. package/dist/loader.js.map +1 -1
  29. package/dist/packageVersion.d.ts +1 -1
  30. package/dist/packageVersion.d.ts.map +1 -1
  31. package/dist/packageVersion.js +1 -1
  32. package/dist/packageVersion.js.map +1 -1
  33. package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
  34. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  35. package/lib/connectionManager.d.ts +153 -0
  36. package/lib/connectionManager.d.ts.map +1 -0
  37. package/lib/connectionManager.js +660 -0
  38. package/lib/connectionManager.js.map +1 -0
  39. package/lib/connectionStateHandler.d.ts +1 -0
  40. package/lib/connectionStateHandler.d.ts.map +1 -1
  41. package/lib/connectionStateHandler.js +6 -0
  42. package/lib/connectionStateHandler.js.map +1 -1
  43. package/lib/container.d.ts +2 -22
  44. package/lib/container.d.ts.map +1 -1
  45. package/lib/container.js +122 -152
  46. package/lib/container.js.map +1 -1
  47. package/lib/containerContext.d.ts +1 -0
  48. package/lib/containerContext.d.ts.map +1 -1
  49. package/lib/containerContext.js +4 -0
  50. package/lib/containerContext.js.map +1 -1
  51. package/lib/contracts.d.ts +112 -0
  52. package/lib/contracts.d.ts.map +1 -0
  53. package/lib/contracts.js +11 -0
  54. package/lib/contracts.js.map +1 -0
  55. package/lib/deltaManager.d.ts +26 -142
  56. package/lib/deltaManager.d.ts.map +1 -1
  57. package/lib/deltaManager.js +147 -774
  58. package/lib/deltaManager.js.map +1 -1
  59. package/lib/loader.d.ts +14 -4
  60. package/lib/loader.d.ts.map +1 -1
  61. package/lib/loader.js +11 -5
  62. package/lib/loader.js.map +1 -1
  63. package/lib/packageVersion.d.ts +1 -1
  64. package/lib/packageVersion.d.ts.map +1 -1
  65. package/lib/packageVersion.js +1 -1
  66. package/lib/packageVersion.js.map +1 -1
  67. package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
  68. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  69. package/package.json +9 -9
  70. package/src/connectionManager.ts +892 -0
  71. package/src/connectionStateHandler.ts +8 -0
  72. package/src/container.ts +165 -187
  73. package/src/containerContext.ts +4 -0
  74. package/src/contracts.ts +156 -0
  75. package/src/deltaManager.ts +181 -978
  76. package/src/loader.ts +59 -27
  77. package/src/packageVersion.ts +1 -1
package/dist/container.js CHANGED
@@ -21,6 +21,7 @@ const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
21
21
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
22
22
  const audience_1 = require("./audience");
23
23
  const containerContext_1 = require("./containerContext");
24
+ const contracts_1 = require("./contracts");
24
25
  const deltaManager_1 = require("./deltaManager");
25
26
  const deltaManagerProxy_1 = require("./deltaManagerProxy");
26
27
  const loader_1 = require("./loader");
@@ -32,6 +33,7 @@ const containerStorageAdapter_1 = require("./containerStorageAdapter");
32
33
  const utils_1 = require("./utils");
33
34
  const quorum_1 = require("./quorum");
34
35
  const collabWindowTracker_1 = require("./collabWindowTracker");
36
+ const connectionManager_1 = require("./connectionManager");
35
37
  const detachedContainerRefSeqNumber = 0;
36
38
  const dirtyContainerEvent = "dirty";
37
39
  const savedContainerEvent = "saved";
@@ -98,6 +100,8 @@ async function waitContainerToCatchUp(container) {
98
100
  waitForOps();
99
101
  };
100
102
  container.on(telemetry_utils_1.connectedEventName, callback);
103
+ // TODO: Remove null check after next release #8523
104
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
101
105
  container.resume();
102
106
  });
103
107
  }
@@ -144,7 +148,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
144
148
  this.subLogger = telemetry_utils_1.ChildLogger.create(loader.services.subLogger, undefined, {
145
149
  all: {
146
150
  clientType,
147
- loaderVersion: packageVersion_1.pkgVersion,
148
151
  containerId: uuid_1.v4(),
149
152
  docId: () => this.id,
150
153
  containerAttachState: () => this._attachState,
@@ -173,7 +176,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
173
176
  this.connectionStateHandler = new connectionStateHandler_1.ConnectionStateHandler({
174
177
  protocolHandler: () => this._protocolHandler,
175
178
  logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
176
- shouldClientJoinWrite: () => this._deltaManager.shouldJoinWrite(),
179
+ shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
177
180
  maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
178
181
  logConnectionIssue: (eventName) => {
179
182
  // We get here when socket does not receive any ops on "write" connection, including
@@ -229,22 +232,22 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
229
232
  switch (event) {
230
233
  case dirtyContainerEvent:
231
234
  if (this._dirtyContainer) {
232
- listener(dirtyContainerEvent);
235
+ listener();
233
236
  }
234
237
  break;
235
238
  case savedContainerEvent:
236
239
  if (!this._dirtyContainer) {
237
- listener(savedContainerEvent);
240
+ listener();
238
241
  }
239
242
  break;
240
243
  case telemetry_utils_1.connectedEventName:
241
244
  if (this.connected) {
242
- listener(event, this.clientId);
245
+ listener(this.clientId);
243
246
  }
244
247
  break;
245
248
  case telemetry_utils_1.disconnectedEventName:
246
249
  if (!this.connected) {
247
- listener(event);
250
+ listener();
248
251
  }
249
252
  break;
250
253
  default:
@@ -291,16 +294,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
291
294
  container.close(err);
292
295
  onClosed(err);
293
296
  });
294
- }), { start: true, end: true, cancel: "error" });
297
+ }), { start: true, end: true, cancel: "generic" });
295
298
  }
296
299
  /**
297
300
  * Create a new container in a detached state.
298
301
  */
299
302
  static async createDetached(loader, codeDetails) {
300
303
  const container = new Container(loader, {});
301
- container._lifecycleState = "loading";
302
- await container.createDetached(codeDetails);
303
- return container;
304
+ return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "CreateDetached" }, async (_event) => {
305
+ container._lifecycleState = "loading";
306
+ await container.createDetached(codeDetails);
307
+ return container;
308
+ }, { start: true, end: true, cancel: "generic" });
304
309
  }
305
310
  /**
306
311
  * Create a new container in a detached state that is initialized with a
@@ -308,10 +313,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
308
313
  */
309
314
  static async rehydrateDetachedFromSnapshot(loader, snapshot) {
310
315
  const container = new Container(loader, {});
311
- const deserializedSummary = JSON.parse(snapshot);
312
- container._lifecycleState = "loading";
313
- await container.rehydrateDetachedFromSnapshot(deserializedSummary);
314
- return container;
316
+ return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
317
+ const deserializedSummary = JSON.parse(snapshot);
318
+ container._lifecycleState = "loading";
319
+ await container.rehydrateDetachedFromSnapshot(deserializedSummary);
320
+ return container;
321
+ }, { start: true, end: true, cancel: "generic" });
315
322
  }
316
323
  get loaded() {
317
324
  return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
@@ -349,6 +356,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
349
356
  }
350
357
  return this._protocolHandler;
351
358
  }
359
+ get connectionMode() { return this._deltaManager.connectionManager.connectionMode; }
352
360
  get IFluidRouter() { return this; }
353
361
  get resolvedUrl() {
354
362
  return this._resolvedUrl;
@@ -356,31 +364,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
356
364
  get loadedFromVersion() {
357
365
  return this._loadedFromVersion;
358
366
  }
359
- /**
360
- * Tells if container is in read-only mode.
361
- * Data stores should listen for "readonly" notifications and disallow user making changes to data stores.
362
- * Readonly state can be because of no storage write permission,
363
- * or due to host forcing readonly mode for container.
364
- *
365
- * We do not differentiate here between no write access to storage vs. host disallowing changes to container -
366
- * in all cases container runtime and data stores should respect readonly state and not allow local changes.
367
- *
368
- * It is undefined if we have not yet established websocket connection
369
- * and do not know if user has write access to a file.
370
- * @deprecated - use readOnlyInfo
371
- */
372
- get readonly() {
373
- return this._deltaManager.readonly;
374
- }
375
- /**
376
- * Tells if user has no write permissions for file in storage
377
- * It is undefined if we have not yet established websocket connection
378
- * and do not know if user has write access to a file.
379
- * @deprecated - use readOnlyInfo
380
- */
381
- get readonlyPermissions() {
382
- return this._deltaManager.readonlyPermissions;
383
- }
384
367
  get readOnlyInfo() {
385
368
  return this._deltaManager.readOnlyInfo;
386
369
  }
@@ -388,7 +371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
388
371
  * Tracks host requiring read-only mode.
389
372
  */
390
373
  forceReadonly(readonly) {
391
- this._deltaManager.forceReadonly(readonly);
374
+ this._deltaManager.connectionManager.forceReadonly(readonly);
392
375
  }
393
376
  get id() {
394
377
  var _a, _b;
@@ -422,7 +405,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
422
405
  * Set once this.connected is true, otherwise undefined
423
406
  */
424
407
  get scopes() {
425
- return this._deltaManager.scopes;
408
+ return this._deltaManager.connectionManager.scopes;
426
409
  }
427
410
  get clientDetails() {
428
411
  return this._deltaManager.clientDetails;
@@ -489,6 +472,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
489
472
  try {
490
473
  this._deltaManager.close(error);
491
474
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
475
+ this.connectionStateHandler.dispose();
492
476
  (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
493
477
  common_utils_1.assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
494
478
  (_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
@@ -538,89 +522,88 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
538
522
  return JSON.stringify(combinedSummary);
539
523
  }
540
524
  async attach(request) {
541
- if (this._lifecycleState !== "loaded") {
542
- throw new container_utils_1.UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
543
- }
544
- // If container is already attached or attach is in progress, throw an error.
545
- common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
546
- this.attachStarted = true;
547
- // If attachment blobs were uploaded in detached state we will go through a different attach flow
548
- const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
549
- && this.loader.services.detachedBlobStorage.size > 0;
550
- try {
551
- common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
552
- let summary;
553
- if (!hasAttachmentBlobs) {
554
- // Get the document state post attach - possibly can just call attach but we need to change the
555
- // semantics around what the attach means as far as async code goes.
556
- const appSummary = this.context.createSummary();
557
- const protocolSummary = this.captureProtocolSummary();
558
- summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
559
- // Set the state as attaching as we are starting the process of attaching container.
560
- // This should be fired after taking the summary because it is the place where we are
561
- // starting to attach the container to storage.
562
- // Also, this should only be fired in detached container.
563
- this._attachState = container_definitions_1.AttachState.Attaching;
564
- this.context.notifyAttaching();
565
- }
566
- // Actually go and create the resolved document
567
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
568
- driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
569
- if (this.service === undefined) {
570
- this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
525
+ await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Attach" }, async () => {
526
+ if (this._lifecycleState !== "loaded") {
527
+ throw new container_utils_1.UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
571
528
  }
572
- const resolvedUrl = this.service.resolvedUrl;
573
- driver_utils_1.ensureFluidResolvedUrl(resolvedUrl);
574
- this._resolvedUrl = resolvedUrl;
575
- await this.connectStorageService();
576
- if (hasAttachmentBlobs) {
577
- // upload blobs to storage
578
- common_utils_1.assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
579
- // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
580
- // support blob handles that only know about the local IDs
581
- const redirectTable = new Map();
582
- // if new blobs are added while uploading, upload them too
583
- while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
584
- const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
585
- for (const id of newIds) {
586
- const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
587
- const response = await this.storageService.createBlob(blob);
588
- redirectTable.set(id, response.id);
529
+ // If container is already attached or attach is in progress, throw an error.
530
+ common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
531
+ this.attachStarted = true;
532
+ // If attachment blobs were uploaded in detached state we will go through a different attach flow
533
+ const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
534
+ && this.loader.services.detachedBlobStorage.size > 0;
535
+ try {
536
+ common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
537
+ let summary;
538
+ if (!hasAttachmentBlobs) {
539
+ // Get the document state post attach - possibly can just call attach but we need to change the
540
+ // semantics around what the attach means as far as async code goes.
541
+ const appSummary = this.context.createSummary();
542
+ const protocolSummary = this.captureProtocolSummary();
543
+ summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
544
+ // Set the state as attaching as we are starting the process of attaching container.
545
+ // This should be fired after taking the summary because it is the place where we are
546
+ // starting to attach the container to storage.
547
+ // Also, this should only be fired in detached container.
548
+ this._attachState = container_definitions_1.AttachState.Attaching;
549
+ this.context.notifyAttaching();
550
+ }
551
+ // Actually go and create the resolved document
552
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
553
+ driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
554
+ if (this.service === undefined) {
555
+ this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
556
+ }
557
+ const resolvedUrl = this.service.resolvedUrl;
558
+ driver_utils_1.ensureFluidResolvedUrl(resolvedUrl);
559
+ this._resolvedUrl = resolvedUrl;
560
+ await this.connectStorageService();
561
+ if (hasAttachmentBlobs) {
562
+ // upload blobs to storage
563
+ common_utils_1.assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
564
+ // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
565
+ // support blob handles that only know about the local IDs
566
+ const redirectTable = new Map();
567
+ // if new blobs are added while uploading, upload them too
568
+ while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
569
+ const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
570
+ for (const id of newIds) {
571
+ const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
572
+ const response = await this.storageService.createBlob(blob);
573
+ redirectTable.set(id, response.id);
574
+ }
589
575
  }
576
+ // take summary and upload
577
+ const appSummary = this.context.createSummary(redirectTable);
578
+ const protocolSummary = this.captureProtocolSummary();
579
+ summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
580
+ this._attachState = container_definitions_1.AttachState.Attaching;
581
+ this.context.notifyAttaching();
582
+ await this.storageService.uploadSummaryWithContext(summary, {
583
+ referenceSequenceNumber: 0,
584
+ ackHandle: undefined,
585
+ proposalHandle: undefined,
586
+ });
587
+ }
588
+ this._attachState = container_definitions_1.AttachState.Attached;
589
+ this.emit("attached");
590
+ // Propagate current connection state through the system.
591
+ this.propagateConnectionState();
592
+ if (!this.closed) {
593
+ this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
590
594
  }
591
- // take summary and upload
592
- const appSummary = this.context.createSummary(redirectTable);
593
- const protocolSummary = this.captureProtocolSummary();
594
- summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
595
- this._attachState = container_definitions_1.AttachState.Attaching;
596
- this.context.notifyAttaching();
597
- await this.storageService.uploadSummaryWithContext(summary, {
598
- referenceSequenceNumber: 0,
599
- ackHandle: undefined,
600
- proposalHandle: undefined,
601
- });
602
- }
603
- this._attachState = container_definitions_1.AttachState.Attached;
604
- this.emit("attached");
605
- // Propagate current connection state through the system.
606
- this.propagateConnectionState();
607
- if (!this.closed) {
608
- this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
609
595
  }
610
- }
611
- catch (error) {
612
- // we should retry upon any retriable errors, so we shouldn't see them here
613
- common_utils_1.assert(!driver_utils_1.canRetryOnError(error), 0x24f /* "retriable error thrown from attach()" */);
614
- // add resolved URL on error object so that host has the ability to find this document and delete it
615
- const newError = telemetry_utils_1.normalizeError(error);
616
- const resolvedUrl = this.resolvedUrl;
617
- if (resolvedUrl) {
618
- driver_utils_1.ensureFluidResolvedUrl(resolvedUrl);
619
- newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
596
+ catch (error) {
597
+ // add resolved URL on error object so that host has the ability to find this document and delete it
598
+ const newError = telemetry_utils_1.normalizeError(error);
599
+ const resolvedUrl = this.resolvedUrl;
600
+ if (driver_utils_1.isFluidResolvedUrl(resolvedUrl)) {
601
+ newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
602
+ }
603
+ this.close(newError);
604
+ throw newError;
620
605
  }
621
- this.close(newError);
622
- throw newError;
623
- }
606
+ }, { start: true, end: true, cancel: "generic" });
624
607
  }
625
608
  async request(path) {
626
609
  return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
@@ -648,8 +631,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
648
631
  if (this.closed) {
649
632
  throw new Error("Attempting to setAutoReconnect() a closed Container");
650
633
  }
651
- const mode = reconnect ? deltaManager_1.ReconnectMode.Enabled : deltaManager_1.ReconnectMode.Disabled;
652
- const currentMode = this._deltaManager.reconnectMode;
634
+ const mode = reconnect ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Disabled;
635
+ const currentMode = this._deltaManager.connectionManager.reconnectMode;
653
636
  if (currentMode === mode) {
654
637
  return;
655
638
  }
@@ -658,11 +641,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
658
641
  this.setAutoReconnectTime = now;
659
642
  this.logger.sendTelemetryEvent({
660
643
  eventName: reconnect ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
661
- connectionMode: this._deltaManager.connectionMode,
644
+ connectionMode: this.connectionMode,
662
645
  connectionState: ConnectionState[this.connectionState],
663
646
  duration,
664
647
  });
665
- this._deltaManager.setAutoReconnect(mode);
648
+ this._deltaManager.connectionManager.setAutoReconnect(mode);
666
649
  // If container state is not attached and resumed, then don't connect to delta stream. Also don't set the
667
650
  // manual reconnection flag to true as we haven't made the initial connection yet.
668
651
  if (reconnect && this._attachState === container_definitions_1.AttachState.Attached && this.resumedOpProcessingAfterLoad) {
@@ -671,13 +654,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
671
654
  this.manualReconnectInProgress = true;
672
655
  }
673
656
  // Ensure connection to web socket
674
- this.connectToDeltaStream({ reason: "autoReconnect" }).catch((error) => {
675
- // All errors are reported through events ("error" / "disconnected") and telemetry in DeltaManager
676
- // So there shouldn't be a need to record error here.
677
- // But we have number of cases where reconnects do not happen, and no errors are recorded, so
678
- // adding this log point for easier diagnostics
679
- this.logger.sendTelemetryEvent({ eventName: "setAutoReconnectError" }, error);
680
- });
657
+ this.connectToDeltaStream({ reason: "autoReconnect" });
681
658
  }
682
659
  }
683
660
  resume() {
@@ -697,8 +674,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
697
674
  this._deltaManager.inboundSignal.resume();
698
675
  }
699
676
  // Ensure connection to web socket
700
- // All errors are reported through events ("error" / "disconnected") and telemetry in DeltaManager
701
- this.connectToDeltaStream(args).catch(() => { });
677
+ this.connectToDeltaStream(args);
702
678
  }
703
679
  /**
704
680
  * Raise non-critical error to host. Calling this API will not close container.
@@ -825,13 +801,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
825
801
  this.connectionTransitionTimes[ConnectionState.Disconnected] = common_utils_1.performance.now();
826
802
  }
827
803
  }
828
- async connectToDeltaStream(args) {
804
+ connectToDeltaStream(args) {
829
805
  this.recordConnectStartTime();
830
806
  // All agents need "write" access, including summarizer.
831
807
  if (!this._canReconnect || !this.client.details.capabilities.interactive) {
832
808
  args.mode = "write";
833
809
  }
834
- return this._deltaManager.connect(args);
810
+ this._deltaManager.connect(args);
835
811
  }
836
812
  /**
837
813
  * Load container.
@@ -845,7 +821,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
845
821
  throw new Error("Attempting to load without a resolved url");
846
822
  }
847
823
  this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger);
848
- let startConnectionP;
849
824
  // Ideally we always connect as "read" by default.
850
825
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
851
826
  // We should not rely on it by (one of them will address the issue, but we need to address both)
@@ -859,8 +834,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
859
834
  // Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
860
835
  // DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
861
836
  if (loadMode.deltaConnection === undefined) {
862
- startConnectionP = this.connectToDeltaStream(connectionArgs);
863
- startConnectionP.catch((error) => { });
837
+ this.connectToDeltaStream(connectionArgs);
864
838
  }
865
839
  await this.connectStorageService();
866
840
  this._attachState = container_definitions_1.AttachState.Attached;
@@ -1114,6 +1088,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1114
1088
  if (this.clientDetailsOverride !== undefined) {
1115
1089
  merge_1.default(client.details, this.clientDetailsOverride);
1116
1090
  }
1091
+ client.details.environment = [client.details.environment, ` loaderVersion:${packageVersion_1.pkgVersion}`].join(";");
1117
1092
  return client;
1118
1093
  }
1119
1094
  /**
@@ -1123,17 +1098,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1123
1098
  * If it's not true, runtime is not in position to send ops.
1124
1099
  */
1125
1100
  activeConnection() {
1126
- const active = this.connectionState === ConnectionState.Connected &&
1127
- this._deltaManager.connectionMode === "write";
1128
- // Check for presence of current client in quorum for "write" connections - inactive clients
1129
- // would get leave op after some long timeout (5 min) and that should automatically transition
1130
- // state to "read" mode.
1131
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1132
- common_utils_1.assert(!active || this.getQuorum().getMember(this.clientId) !== undefined, 0x276 /* "active connection not present in quorum" */);
1133
- return active;
1101
+ return this.connectionState === ConnectionState.Connected &&
1102
+ this.connectionMode === "write";
1134
1103
  }
1135
1104
  createDeltaManager() {
1136
- const deltaManager = new deltaManager_1.DeltaManager(() => this.service, this.client, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), this._canReconnect, () => this.activeConnection());
1105
+ const serviceProvider = () => this.service;
1106
+ const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, this.client, this._canReconnect, telemetry_utils_1.ChildLogger.create(this.subLogger, "ConnectionManager"), props));
1137
1107
  // Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
1138
1108
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1139
1109
  deltaManager.inbound.pause();
@@ -1141,12 +1111,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1141
1111
  deltaManager.inboundSignal.pause();
1142
1112
  deltaManager.on("connect", (details, opsBehind) => {
1143
1113
  var _a;
1144
- this.connectionStateHandler.receivedConnectEvent(this._deltaManager.connectionMode, details);
1145
1114
  // Back-compat for new client and old server.
1146
1115
  this._audience.clear();
1147
1116
  for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
1148
1117
  this._audience.addMember(priorClient.clientId, priorClient.client);
1149
1118
  }
1119
+ this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
1150
1120
  });
1151
1121
  deltaManager.on("disconnect", (reason) => {
1152
1122
  this.manualReconnectInProgress = false;
@@ -1184,7 +1154,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1184
1154
  let checkpointSequenceNumber;
1185
1155
  let opsBehind;
1186
1156
  if (value === ConnectionState.Disconnected) {
1187
- autoReconnect = this._deltaManager.reconnectMode;
1157
+ autoReconnect = this._deltaManager.connectionManager.reconnectMode;
1188
1158
  }
1189
1159
  else {
1190
1160
  if (value === ConnectionState.Connected) {
@@ -1211,8 +1181,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1211
1181
  this.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
1212
1182
  durationFromDisconnected,
1213
1183
  reason,
1214
- connectionInitiationReason, socketDocumentId: this._deltaManager.socketDocumentId, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
1215
- opsBehind, online: driver_utils_1.OnlineStatus[driver_utils_1.isOnline()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber }, this._deltaManager.connectionProps()));
1184
+ connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
1185
+ opsBehind, online: driver_utils_1.OnlineStatus[driver_utils_1.isOnline()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber }, this._deltaManager.connectionProps));
1216
1186
  if (value === ConnectionState.Connected) {
1217
1187
  this.firstConnection = false;
1218
1188
  this.manualReconnectInProgress = false;
@@ -1221,7 +1191,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1221
1191
  propagateConnectionState() {
1222
1192
  const logOpsOnReconnect = this.connectionState === ConnectionState.Connected &&
1223
1193
  !this.firstConnection &&
1224
- this._deltaManager.connectionMode === "write";
1194
+ this.connectionMode === "write";
1225
1195
  if (logOpsOnReconnect) {
1226
1196
  this.messageCountAfterDisconnection = 0;
1227
1197
  }