@fluidframework/container-loader 2.0.0-dev.4.4.0.162253 → 2.0.0-dev.5.2.0.169897

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 (114) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +27 -3
  3. package/dist/connectionManager.d.ts +3 -2
  4. package/dist/connectionManager.d.ts.map +1 -1
  5. package/dist/connectionManager.js +32 -13
  6. package/dist/connectionManager.js.map +1 -1
  7. package/dist/connectionStateHandler.d.ts +15 -3
  8. package/dist/connectionStateHandler.d.ts.map +1 -1
  9. package/dist/connectionStateHandler.js +24 -1
  10. package/dist/connectionStateHandler.js.map +1 -1
  11. package/dist/container.d.ts +74 -44
  12. package/dist/container.d.ts.map +1 -1
  13. package/dist/container.js +81 -111
  14. package/dist/container.js.map +1 -1
  15. package/dist/containerContext.d.ts +2 -2
  16. package/dist/containerContext.d.ts.map +1 -1
  17. package/dist/containerContext.js +3 -7
  18. package/dist/containerContext.js.map +1 -1
  19. package/dist/containerStorageAdapter.d.ts +3 -3
  20. package/dist/containerStorageAdapter.d.ts.map +1 -1
  21. package/dist/containerStorageAdapter.js +6 -15
  22. package/dist/containerStorageAdapter.js.map +1 -1
  23. package/dist/contracts.d.ts +8 -0
  24. package/dist/contracts.d.ts.map +1 -1
  25. package/dist/contracts.js.map +1 -1
  26. package/dist/deltaManager.d.ts +21 -9
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +42 -31
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.d.ts +2 -3
  31. package/dist/deltaQueue.d.ts.map +1 -1
  32. package/dist/deltaQueue.js +2 -3
  33. package/dist/deltaQueue.js.map +1 -1
  34. package/dist/index.d.ts +1 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/loader.d.ts +9 -7
  38. package/dist/loader.d.ts.map +1 -1
  39. package/dist/loader.js +47 -61
  40. package/dist/loader.js.map +1 -1
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.js +1 -1
  43. package/dist/packageVersion.js.map +1 -1
  44. package/dist/retriableDocumentStorageService.d.ts +3 -2
  45. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  46. package/dist/retriableDocumentStorageService.js.map +1 -1
  47. package/dist/tsdoc-metadata.json +11 -0
  48. package/dist/utils.d.ts +2 -0
  49. package/dist/utils.d.ts.map +1 -1
  50. package/dist/utils.js +8 -1
  51. package/dist/utils.js.map +1 -1
  52. package/lib/connectionManager.d.ts +3 -2
  53. package/lib/connectionManager.d.ts.map +1 -1
  54. package/lib/connectionManager.js +33 -14
  55. package/lib/connectionManager.js.map +1 -1
  56. package/lib/connectionStateHandler.d.ts +15 -3
  57. package/lib/connectionStateHandler.d.ts.map +1 -1
  58. package/lib/connectionStateHandler.js +25 -2
  59. package/lib/connectionStateHandler.js.map +1 -1
  60. package/lib/container.d.ts +74 -44
  61. package/lib/container.d.ts.map +1 -1
  62. package/lib/container.js +82 -112
  63. package/lib/container.js.map +1 -1
  64. package/lib/containerContext.d.ts +2 -2
  65. package/lib/containerContext.d.ts.map +1 -1
  66. package/lib/containerContext.js +3 -7
  67. package/lib/containerContext.js.map +1 -1
  68. package/lib/containerStorageAdapter.d.ts +3 -3
  69. package/lib/containerStorageAdapter.d.ts.map +1 -1
  70. package/lib/containerStorageAdapter.js +6 -15
  71. package/lib/containerStorageAdapter.js.map +1 -1
  72. package/lib/contracts.d.ts +8 -0
  73. package/lib/contracts.d.ts.map +1 -1
  74. package/lib/contracts.js.map +1 -1
  75. package/lib/deltaManager.d.ts +21 -9
  76. package/lib/deltaManager.d.ts.map +1 -1
  77. package/lib/deltaManager.js +44 -33
  78. package/lib/deltaManager.js.map +1 -1
  79. package/lib/deltaQueue.d.ts +2 -3
  80. package/lib/deltaQueue.d.ts.map +1 -1
  81. package/lib/deltaQueue.js +2 -3
  82. package/lib/deltaQueue.js.map +1 -1
  83. package/lib/index.d.ts +1 -2
  84. package/lib/index.d.ts.map +1 -1
  85. package/lib/index.js +1 -1
  86. package/lib/index.js.map +1 -1
  87. package/lib/loader.d.ts +9 -7
  88. package/lib/loader.d.ts.map +1 -1
  89. package/lib/loader.js +47 -61
  90. package/lib/loader.js.map +1 -1
  91. package/lib/packageVersion.d.ts +1 -1
  92. package/lib/packageVersion.js +1 -1
  93. package/lib/packageVersion.js.map +1 -1
  94. package/lib/retriableDocumentStorageService.d.ts +3 -2
  95. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  96. package/lib/retriableDocumentStorageService.js.map +1 -1
  97. package/lib/utils.d.ts +2 -0
  98. package/lib/utils.d.ts.map +1 -1
  99. package/lib/utils.js +7 -1
  100. package/lib/utils.js.map +1 -1
  101. package/package.json +16 -18
  102. package/src/connectionManager.ts +40 -22
  103. package/src/connectionStateHandler.ts +52 -8
  104. package/src/container.ts +191 -159
  105. package/src/containerContext.ts +3 -9
  106. package/src/containerStorageAdapter.ts +8 -20
  107. package/src/contracts.ts +10 -0
  108. package/src/deltaManager.ts +59 -37
  109. package/src/deltaQueue.ts +2 -3
  110. package/src/index.ts +1 -8
  111. package/src/loader.ts +85 -83
  112. package/src/packageVersion.ts +1 -1
  113. package/src/retriableDocumentStorageService.ts +3 -2
  114. package/src/utils.ts +15 -1
package/lib/container.js CHANGED
@@ -8,7 +8,7 @@ import { v4 as uuid } from "uuid";
8
8
  import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
9
9
  import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
10
10
  import { GenericError, UsageError } from "@fluidframework/container-utils";
11
- import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, isCombinedAppAndProtocolSummary, } from "@fluidframework/driver-utils";
11
+ import { readAndParse, OnlineStatus, isOnline, combineAppAndProtocolSummary, runWithRetry, isCombinedAppAndProtocolSummary, } from "@fluidframework/driver-utils";
12
12
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
13
13
  import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
14
14
  import { Audience } from "./audience";
@@ -119,26 +119,18 @@ export async function ReportIfTooLong(logger, eventName, action) {
119
119
  }
120
120
  }
121
121
  const summarizerClientType = "summarizer";
122
- /**
123
- * @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
124
- */
125
122
  export class Container extends EventEmitterWithErrorHandling {
126
123
  /**
127
124
  * @internal
128
125
  */
129
- constructor(loader, config, protocolHandlerBuilder) {
130
- var _a, _b, _c;
126
+ constructor(createProps, loadProps) {
127
+ var _a;
131
128
  super((name, error) => {
132
129
  this.mc.logger.sendErrorEvent({
133
130
  eventName: "ContainerEventHandlerException",
134
131
  name: typeof name === "string" ? name : undefined,
135
132
  }, error);
136
133
  });
137
- this.loader = loader;
138
- this.protocolHandlerBuilder = protocolHandlerBuilder;
139
- // Tells if container can reconnect on losing fist connection
140
- // If false, container gets closed on loss of connection.
141
- this._canReconnect = true;
142
134
  /**
143
135
  * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
144
136
  *
@@ -166,26 +158,41 @@ export class Container extends EventEmitterWithErrorHandling {
166
158
  this.savedOps = [];
167
159
  this.setAutoReconnectTime = performance.now();
168
160
  this._disposed = false;
169
- this.clientDetailsOverride = config.clientDetailsOverride;
170
- this._resolvedUrl = config.resolvedUrl;
171
- if (config.canReconnect !== undefined) {
172
- this._canReconnect = config.canReconnect;
173
- }
161
+ const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
162
+ this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
163
+ const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
164
+ this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
165
+ this.clientDetailsOverride = clientDetailsOverride;
166
+ this.urlResolver = urlResolver;
167
+ this.serviceFactory = documentServiceFactory;
168
+ this.codeLoader = codeLoader;
169
+ // Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
170
+ // all clients that were loaded from the same loader (including summarizer clients).
171
+ // Tracking alternative ways to handle this in AB#4129.
172
+ this.options = Object.assign({}, options);
173
+ this.scope = scope;
174
+ this.detachedBlobStorage = detachedBlobStorage;
175
+ this.protocolHandlerBuilder =
176
+ protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new ProtocolHandler(...args, new Audience()));
177
+ // Note that we capture the createProps here so we can replicate the creation call when we want to clone.
178
+ this.clone = async (_loadProps, createParamOverrides) => {
179
+ return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
180
+ };
174
181
  // Create logger for data stores to use
175
182
  const type = this.client.details.type;
176
183
  const interactive = this.client.details.capabilities.interactive;
177
184
  const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
178
185
  // Need to use the property getter for docId because for detached flow we don't have the docId initially.
179
186
  // We assign the id later so property getter is used.
180
- this.subLogger = ChildLogger.create(loader.services.subLogger, undefined, {
187
+ this.subLogger = ChildLogger.create(subLogger, undefined, {
181
188
  all: {
182
189
  clientType,
183
190
  containerId: uuid(),
184
- docId: () => { var _a, _b; return (_b = (_a = this._resolvedUrl) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined; },
191
+ docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
185
192
  containerAttachState: () => this._attachState,
186
193
  containerLifecycleState: () => this._lifecycleState,
187
194
  containerConnectionState: () => ConnectionState[this.connectionState],
188
- serializedContainer: config.serializedContainerState !== undefined,
195
+ serializedContainer: pendingLocalState !== undefined,
189
196
  },
190
197
  // we need to be judicious with our logging here to avoid generating too much data
191
198
  // all data logged here should be broadly applicable, and not specific to a
@@ -208,10 +215,6 @@ export class Container extends EventEmitterWithErrorHandling {
208
215
  });
209
216
  // Prefix all events in this file with container-loader
210
217
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
211
- // Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
212
- // all clients that were loaded from the same loader (including summarizer clients).
213
- // Tracking alternative ways to handle this in AB#4129.
214
- this.options = Object.assign({}, this.loader.services.options);
215
218
  this._deltaManager = this.createDeltaManager();
216
219
  this.connectionStateHandler = createConnectionStateHandler({
217
220
  logger: this.mc.logger,
@@ -227,7 +230,7 @@ export class Container extends EventEmitterWithErrorHandling {
227
230
  }
228
231
  },
229
232
  shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
230
- maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
233
+ maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
231
234
  logConnectionIssue: (eventName, category, details) => {
232
235
  const mode = this.connectionMode;
233
236
  // We get here when socket does not receive any ops on "write" connection, including
@@ -251,7 +254,7 @@ export class Container extends EventEmitterWithErrorHandling {
251
254
  this.connect();
252
255
  }
253
256
  },
254
- }, this.deltaManager, (_a = config.serializedContainerState) === null || _a === void 0 ? void 0 : _a.clientId);
257
+ }, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
255
258
  this.on(savedContainerEvent, () => {
256
259
  this.connectionStateHandler.containerSaved();
257
260
  });
@@ -263,8 +266,8 @@ export class Container extends EventEmitterWithErrorHandling {
263
266
  : combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
264
267
  // Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
265
268
  // Even if not forced on via this flag, combined summaries may still be enabled by service policy.
266
- const forceEnableSummarizeProtocolTree = (_b = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _b !== void 0 ? _b : this.loader.services.options.summarizeProtocolTree;
267
- this.storageAdapter = new ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, (_c = config.serializedContainerState) === null || _c === void 0 ? void 0 : _c.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
269
+ const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
270
+ this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
268
271
  const isDomAvailable = typeof document === "object" &&
269
272
  document !== null &&
270
273
  typeof document.addEventListener === "function" &&
@@ -290,33 +293,28 @@ export class Container extends EventEmitterWithErrorHandling {
290
293
  * Load an existing container.
291
294
  * @internal
292
295
  */
293
- static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
294
- const container = new Container(loader, {
295
- clientDetailsOverride: loadOptions.clientDetailsOverride,
296
- resolvedUrl: loadOptions.resolvedUrl,
297
- canReconnect: loadOptions.canReconnect,
298
- serializedContainerState: pendingLocalState,
299
- }, protocolHandlerBuilder);
296
+ static async load(loadProps, createProps) {
297
+ const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
298
+ const container = new Container(createProps, loadProps);
299
+ const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
300
300
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
301
- var _a, _b;
302
- const version = loadOptions.version;
303
301
  const defaultMode = { opsBeforeReturn: "cached" };
304
302
  // if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
305
303
  // to return container, so ignore this value and use undefined for opsBeforeReturn
306
304
  const mode = pendingLocalState
307
- ? Object.assign(Object.assign({}, ((_a = loadOptions.loadMode) !== null && _a !== void 0 ? _a : defaultMode)), { opsBeforeReturn: undefined }) : (_b = loadOptions.loadMode) !== null && _b !== void 0 ? _b : defaultMode;
305
+ ? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
308
306
  const onClosed = (err) => {
309
307
  // pre-0.58 error message: containerClosedWithoutErrorDuringLoad
310
308
  reject(err !== null && err !== void 0 ? err : new GenericError("Container closed without error during load"));
311
309
  };
312
310
  container.on("closed", onClosed);
313
311
  container
314
- .load(version, mode, pendingLocalState)
312
+ .load(version, mode, resolvedUrl, pendingLocalState)
315
313
  .finally(() => {
316
314
  container.removeListener("closed", onClosed);
317
315
  })
318
316
  .then((props) => {
319
- event.end(Object.assign(Object.assign({}, props), loadOptions.loadMode));
317
+ event.end(Object.assign(Object.assign({}, props), loadMode));
320
318
  resolve(container);
321
319
  }, (error) => {
322
320
  const err = normalizeError(error);
@@ -326,13 +324,13 @@ export class Container extends EventEmitterWithErrorHandling {
326
324
  container.close(err);
327
325
  onClosed(err);
328
326
  });
329
- }), { start: true, end: true, cancel: "generic" });
327
+ }), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
330
328
  }
331
329
  /**
332
330
  * Create a new container in a detached state.
333
331
  */
334
- static async createDetached(loader, codeDetails, protocolHandlerBuilder) {
335
- const container = new Container(loader, {}, protocolHandlerBuilder);
332
+ static async createDetached(createProps, codeDetails) {
333
+ const container = new Container(createProps);
336
334
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
337
335
  await container.createDetached(codeDetails);
338
336
  return container;
@@ -342,8 +340,8 @@ export class Container extends EventEmitterWithErrorHandling {
342
340
  * Create a new container in a detached state that is initialized with a
343
341
  * snapshot from a previous detached container.
344
342
  */
345
- static async rehydrateDetachedFromSnapshot(loader, snapshot, protocolHandlerBuilder) {
346
- const container = new Container(loader, {}, protocolHandlerBuilder);
343
+ static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
344
+ const container = new Container(createProps);
347
345
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
348
346
  const deserializedSummary = JSON.parse(snapshot);
349
347
  await container.rehydrateDetachedFromSnapshot(deserializedSummary);
@@ -387,7 +385,19 @@ export class Container extends EventEmitterWithErrorHandling {
387
385
  return this;
388
386
  }
389
387
  get resolvedUrl() {
390
- return this._resolvedUrl;
388
+ var _a;
389
+ /**
390
+ * All attached containers will have a document service,
391
+ * this is required, as attached containers are attached to
392
+ * a service. Detached containers will neither have a document
393
+ * service or a resolved url as they only exist locally.
394
+ * in order to create a document service a resolved url must
395
+ * first be obtained, this is how the container is identified.
396
+ * Because of this, the document service's resolved url
397
+ * is always the same as the containers, as we had to
398
+ * obtain the resolved url, and then create the service from it.
399
+ */
400
+ return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
391
401
  }
392
402
  get loadedFromVersion() {
393
403
  return this._loadedFromVersion;
@@ -473,18 +483,6 @@ export class Container extends EventEmitterWithErrorHandling {
473
483
  get isDirty() {
474
484
  return this._dirtyContainer;
475
485
  }
476
- get serviceFactory() {
477
- return this.loader.services.documentServiceFactory;
478
- }
479
- get urlResolver() {
480
- return this.loader.services.urlResolver;
481
- }
482
- get scope() {
483
- return this.loader.services.scope;
484
- }
485
- get codeLoader() {
486
- return this.loader.services.codeLoader;
487
- }
488
486
  /**
489
487
  * {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
490
488
  */
@@ -524,7 +522,7 @@ export class Container extends EventEmitterWithErrorHandling {
524
522
  return this.protocolHandler.quorum;
525
523
  }
526
524
  dispose(error) {
527
- this._deltaManager.close(error, true /* doDispose */);
525
+ this._deltaManager.dispose(error);
528
526
  this.verifyClosed();
529
527
  }
530
528
  close(error) {
@@ -540,7 +538,7 @@ export class Container extends EventEmitterWithErrorHandling {
540
538
  assert(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
541
539
  }
542
540
  closeCore(error) {
543
- var _a, _b, _c;
541
+ var _a;
544
542
  assert(!this.closed, 0x315 /* re-entrancy */);
545
543
  try {
546
544
  // Ensure that we raise all key events even if one of these throws
@@ -558,12 +556,6 @@ export class Container extends EventEmitterWithErrorHandling {
558
556
  this._lifecycleState = "closing";
559
557
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
560
558
  this.connectionStateHandler.dispose();
561
- (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
562
- this.storageAdapter.dispose();
563
- // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
564
- // about file, like file being overwritten in storage, but client having stale local cache.
565
- // Driver need to ensure all caches are cleared on critical errors
566
- (_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
567
559
  }
568
560
  catch (exception) {
569
561
  this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
@@ -652,8 +644,7 @@ export class Container extends EventEmitterWithErrorHandling {
652
644
  const appSummary = this.context.createSummary();
653
645
  const protocolSummary = this.captureProtocolSummary();
654
646
  const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
655
- if (this.loader.services.detachedBlobStorage &&
656
- this.loader.services.detachedBlobStorage.size > 0) {
647
+ if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
657
648
  combinedSummary.tree[".hasAttachmentBlobs"] = {
658
649
  type: SummaryType.Blob,
659
650
  content: "true",
@@ -672,8 +663,7 @@ export class Container extends EventEmitterWithErrorHandling {
672
663
  assert(this._attachState === AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
673
664
  this.attachStarted = true;
674
665
  // If attachment blobs were uploaded in detached state we will go through a different attach flow
675
- const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined &&
676
- this.loader.services.detachedBlobStorage.size > 0;
666
+ const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
677
667
  try {
678
668
  assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
679
669
  let summary;
@@ -697,31 +687,28 @@ export class Container extends EventEmitterWithErrorHandling {
697
687
  }
698
688
  }
699
689
  // Actually go and create the resolved document
700
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
701
- ensureFluidResolvedUrl(createNewResolvedUrl);
702
690
  if (this.service === undefined) {
703
- assert(this.client.details.type !== summarizerClientType, 0x2c4 /* "client should not be summarizer before container is created" */);
691
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
692
+ assert(this.client.details.type !== summarizerClientType &&
693
+ createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
704
694
  this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
705
695
  cancel: this.closeSignal,
706
696
  });
707
697
  }
708
- const resolvedUrl = this.service.resolvedUrl;
709
- ensureFluidResolvedUrl(resolvedUrl);
710
- this._resolvedUrl = resolvedUrl;
711
698
  await this.storageAdapter.connectToService(this.service);
712
699
  if (hasAttachmentBlobs) {
713
700
  // upload blobs to storage
714
- assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
701
+ assert(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
715
702
  // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
716
703
  // support blob handles that only know about the local IDs
717
704
  const redirectTable = new Map();
718
705
  // if new blobs are added while uploading, upload them too
719
- while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
720
- const newIds = this.loader.services.detachedBlobStorage
706
+ while (redirectTable.size < this.detachedBlobStorage.size) {
707
+ const newIds = this.detachedBlobStorage
721
708
  .getBlobIds()
722
709
  .filter((id) => !redirectTable.has(id));
723
710
  for (const id of newIds) {
724
- const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
711
+ const blob = await this.detachedBlobStorage.readBlob(id);
725
712
  const response = await this.storageAdapter.createBlob(blob);
726
713
  redirectTable.set(id, response.id);
727
714
  }
@@ -756,12 +743,8 @@ export class Container extends EventEmitterWithErrorHandling {
756
743
  catch (error) {
757
744
  // add resolved URL on error object so that host has the ability to find this document and delete it
758
745
  const newError = normalizeError(error);
759
- const resolvedUrl = this.resolvedUrl;
760
- if (isFluidResolvedUrl(resolvedUrl)) {
761
- newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
762
- }
746
+ newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
763
747
  this.close(newError);
764
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
765
748
  throw newError;
766
749
  }
767
750
  }, { start: true, end: true, cancel: "generic" });
@@ -856,7 +839,6 @@ export class Container extends EventEmitterWithErrorHandling {
856
839
  .catch(() => false);
857
840
  }
858
841
  async processCodeProposal() {
859
- var _a;
860
842
  const codeDetails = this.getCodeDetailsFromQuorum();
861
843
  await Promise.all([
862
844
  this.deltaManager.inbound.pause(),
@@ -870,19 +852,12 @@ export class Container extends EventEmitterWithErrorHandling {
870
852
  // pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
871
853
  const error = new GenericError("Existing context does not satisfy incoming proposal");
872
854
  this.close(error);
873
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
874
855
  }
875
856
  async getVersion(version) {
876
857
  const versions = await this.storageAdapter.getVersions(version, 1);
877
858
  return versions[0];
878
859
  }
879
- recordConnectStartTime() {
880
- if (this.connectionTransitionTimes[ConnectionState.Disconnected] === undefined) {
881
- this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
882
- }
883
- }
884
860
  connectToDeltaStream(args) {
885
- this.recordConnectStartTime();
886
861
  // All agents need "write" access, including summarizer.
887
862
  if (!this._canReconnect || !this.client.details.capabilities.interactive) {
888
863
  args.mode = "write";
@@ -894,12 +869,9 @@ export class Container extends EventEmitterWithErrorHandling {
894
869
  *
895
870
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
896
871
  */
897
- async load(specifiedVersion, loadMode, pendingLocalState) {
872
+ async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
898
873
  var _a;
899
- if (this._resolvedUrl === undefined) {
900
- throw new Error("Attempting to load without a resolved url");
901
- }
902
- this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
874
+ this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
903
875
  // Ideally we always connect as "read" by default.
904
876
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
905
877
  // We should not rely on it by (one of them will address the issue, but we need to address both)
@@ -925,9 +897,7 @@ export class Container extends EventEmitterWithErrorHandling {
925
897
  else {
926
898
  // if we have pendingLocalState we can load without storage; don't wait for connection
927
899
  this.storageAdapter.connectToService(this.service).catch((error) => {
928
- var _a;
929
900
  this.close(error);
930
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
931
901
  });
932
902
  }
933
903
  this._attachState = AttachState.Attached;
@@ -1053,8 +1023,7 @@ export class Container extends EventEmitterWithErrorHandling {
1053
1023
  }
1054
1024
  async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
1055
1025
  if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
1056
- assert(!!this.loader.services.detachedBlobStorage &&
1057
- this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
1026
+ assert(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
1058
1027
  delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
1059
1028
  }
1060
1029
  const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
@@ -1107,9 +1076,7 @@ export class Container extends EventEmitterWithErrorHandling {
1107
1076
  this.initializeProtocolState(attributes, quorumSnapshot);
1108
1077
  }
1109
1078
  initializeProtocolState(attributes, quorumSnapshot) {
1110
- var _a;
1111
- const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new ProtocolHandler(...args, new Audience()));
1112
- const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
1079
+ const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
1113
1080
  const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
1114
1081
  protocol.quorum.on("error", (error) => {
1115
1082
  protocolLogger.sendErrorEvent(error);
@@ -1129,10 +1096,8 @@ export class Container extends EventEmitterWithErrorHandling {
1129
1096
  });
1130
1097
  }
1131
1098
  this.processCodeProposal().catch((error) => {
1132
- var _a;
1133
1099
  const normalizedError = normalizeError(error);
1134
1100
  this.close(normalizedError);
1135
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
1136
1101
  throw error;
1137
1102
  });
1138
1103
  }
@@ -1216,6 +1181,12 @@ export class Container extends EventEmitterWithErrorHandling {
1216
1181
  assert(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
1217
1182
  this.connectionStateHandler.receivedConnectEvent(details);
1218
1183
  });
1184
+ deltaManager.on("establishingConnection", (reason) => {
1185
+ this.connectionStateHandler.establishingConnection(reason);
1186
+ });
1187
+ deltaManager.on("cancelEstablishingConnection", (reason) => {
1188
+ this.connectionStateHandler.cancelEstablishingConnection(reason);
1189
+ });
1219
1190
  deltaManager.on("disconnect", (reason, error) => {
1220
1191
  var _a;
1221
1192
  (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
@@ -1272,8 +1243,8 @@ export class Container extends EventEmitterWithErrorHandling {
1272
1243
  time - this.connectionTransitionTimes[ConnectionState.Disconnected];
1273
1244
  durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
1274
1245
  }
1275
- else {
1276
- // This info is of most interest on establishing connection only.
1246
+ else if (value === ConnectionState.CatchingUp) {
1247
+ // This info is of most interesting while Catching Up.
1277
1248
  checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
1278
1249
  if (this.deltaManager.hasCheckpointSequenceNumber) {
1279
1250
  opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
@@ -1322,7 +1293,6 @@ export class Container extends EventEmitterWithErrorHandling {
1322
1293
  }
1323
1294
  // back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
1324
1295
  submitContainerMessage(type, contents, batch, metadata) {
1325
- var _a;
1326
1296
  switch (type) {
1327
1297
  case MessageType.Operation:
1328
1298
  return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
@@ -1331,7 +1301,6 @@ export class Container extends EventEmitterWithErrorHandling {
1331
1301
  default: {
1332
1302
  const newError = new GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
1333
1303
  this.close(newError);
1334
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
1335
1304
  return -1;
1336
1305
  }
1337
1306
  }
@@ -1440,8 +1409,9 @@ export class Container extends EventEmitterWithErrorHandling {
1440
1409
  assert(((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing context not disposed" */);
1441
1410
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1442
1411
  // are set. Global requests will still go directly to the loader
1443
- const loader = new RelativeLoader(this, this.loader);
1444
- this._context = await ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new DeltaManagerProxy(this._deltaManager), new QuorumProxy(this.protocolHandler.quorum), 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) => { var _a; return (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error); }, (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1412
+ const maybeLoader = this.scope;
1413
+ const loader = new RelativeLoader(this, maybeLoader.ILoader);
1414
+ this._context = await ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new DeltaManagerProxy(this._deltaManager), new QuorumProxy(this.protocolHandler.quorum), 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), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1445
1415
  this.emit("contextChanged", codeDetails);
1446
1416
  }
1447
1417
  updateDirtyContainerState(dirty) {