@fluidframework/container-loader 2.0.0-dev-rc.5.0.0.267932 → 2.0.0-dev-rc.5.0.0.270401

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 (165) hide show
  1. package/api-report/{container-loader.api.md → container-loader.alpha.api.md} +58 -15
  2. package/api-report/container-loader.beta.api.md +44 -0
  3. package/api-report/container-loader.public.api.md +44 -0
  4. package/dist/audience.js +2 -1
  5. package/dist/audience.js.map +1 -1
  6. package/dist/catchUpMonitor.js +11 -8
  7. package/dist/catchUpMonitor.js.map +1 -1
  8. package/dist/connectionManager.d.ts +2 -2
  9. package/dist/connectionManager.d.ts.map +1 -1
  10. package/dist/connectionManager.js +91 -63
  11. package/dist/connectionManager.js.map +1 -1
  12. package/dist/connectionStateHandler.js +35 -11
  13. package/dist/connectionStateHandler.js.map +1 -1
  14. package/dist/container.d.ts +2 -2
  15. package/dist/container.d.ts.map +1 -1
  16. package/dist/container.js +156 -123
  17. package/dist/container.js.map +1 -1
  18. package/dist/containerContext.d.ts +2 -2
  19. package/dist/containerContext.d.ts.map +1 -1
  20. package/dist/containerContext.js +34 -8
  21. package/dist/containerContext.js.map +1 -1
  22. package/dist/containerStorageAdapter.js +17 -9
  23. package/dist/containerStorageAdapter.js.map +1 -1
  24. package/dist/contracts.d.ts +2 -2
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/contracts.js.map +1 -1
  27. package/dist/debugLogger.js +2 -0
  28. package/dist/debugLogger.js.map +1 -1
  29. package/dist/deltaManager.d.ts +2 -2
  30. package/dist/deltaManager.d.ts.map +1 -1
  31. package/dist/deltaManager.js +48 -35
  32. package/dist/deltaManager.js.map +1 -1
  33. package/dist/deltaQueue.js +14 -7
  34. package/dist/deltaQueue.js.map +1 -1
  35. package/dist/error.js +5 -4
  36. package/dist/error.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/legacy.d.ts +5 -0
  41. package/dist/loader.js +5 -1
  42. package/dist/loader.js.map +1 -1
  43. package/dist/noopHeuristic.d.ts +1 -1
  44. package/dist/noopHeuristic.d.ts.map +1 -1
  45. package/dist/noopHeuristic.js +3 -1
  46. package/dist/noopHeuristic.js.map +1 -1
  47. package/dist/packageVersion.d.ts +1 -1
  48. package/dist/packageVersion.js +1 -1
  49. package/dist/packageVersion.js.map +1 -1
  50. package/dist/protocol/index.d.ts +7 -0
  51. package/dist/protocol/index.d.ts.map +1 -0
  52. package/dist/protocol/index.js +12 -0
  53. package/dist/protocol/index.js.map +1 -0
  54. package/dist/protocol/protocol.d.ts +52 -0
  55. package/dist/protocol/protocol.d.ts.map +1 -0
  56. package/dist/protocol/protocol.js +115 -0
  57. package/dist/protocol/protocol.js.map +1 -0
  58. package/dist/protocol/quorum.d.ts +185 -0
  59. package/dist/protocol/quorum.d.ts.map +1 -0
  60. package/dist/protocol/quorum.js +440 -0
  61. package/dist/protocol/quorum.js.map +1 -0
  62. package/dist/protocol.d.ts +2 -3
  63. package/dist/protocol.d.ts.map +1 -1
  64. package/dist/protocol.js +4 -2
  65. package/dist/protocol.js.map +1 -1
  66. package/dist/protocolTreeDocumentStorageService.d.ts +7 -7
  67. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  68. package/dist/protocolTreeDocumentStorageService.js +16 -7
  69. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  70. package/dist/retriableDocumentStorageService.js +4 -1
  71. package/dist/retriableDocumentStorageService.js.map +1 -1
  72. package/dist/serializedStateManager.d.ts +1 -2
  73. package/dist/serializedStateManager.d.ts.map +1 -1
  74. package/dist/serializedStateManager.js +10 -2
  75. package/dist/serializedStateManager.js.map +1 -1
  76. package/lib/audience.js +2 -1
  77. package/lib/audience.js.map +1 -1
  78. package/lib/catchUpMonitor.js +11 -8
  79. package/lib/catchUpMonitor.js.map +1 -1
  80. package/lib/connectionManager.d.ts +2 -2
  81. package/lib/connectionManager.d.ts.map +1 -1
  82. package/lib/connectionManager.js +91 -63
  83. package/lib/connectionManager.js.map +1 -1
  84. package/lib/connectionStateHandler.js +35 -11
  85. package/lib/connectionStateHandler.js.map +1 -1
  86. package/lib/container.d.ts +2 -2
  87. package/lib/container.d.ts.map +1 -1
  88. package/lib/container.js +156 -123
  89. package/lib/container.js.map +1 -1
  90. package/lib/containerContext.d.ts +2 -2
  91. package/lib/containerContext.d.ts.map +1 -1
  92. package/lib/containerContext.js +34 -8
  93. package/lib/containerContext.js.map +1 -1
  94. package/lib/containerStorageAdapter.js +17 -9
  95. package/lib/containerStorageAdapter.js.map +1 -1
  96. package/lib/contracts.d.ts +2 -2
  97. package/lib/contracts.d.ts.map +1 -1
  98. package/lib/contracts.js.map +1 -1
  99. package/lib/debugLogger.js +2 -0
  100. package/lib/debugLogger.js.map +1 -1
  101. package/lib/deltaManager.d.ts +2 -2
  102. package/lib/deltaManager.d.ts.map +1 -1
  103. package/lib/deltaManager.js +48 -35
  104. package/lib/deltaManager.js.map +1 -1
  105. package/lib/deltaQueue.js +14 -7
  106. package/lib/deltaQueue.js.map +1 -1
  107. package/lib/error.js +5 -4
  108. package/lib/error.js.map +1 -1
  109. package/lib/index.d.ts +1 -0
  110. package/lib/index.d.ts.map +1 -1
  111. package/lib/index.js.map +1 -1
  112. package/lib/legacy.d.ts +5 -0
  113. package/lib/loader.js +5 -1
  114. package/lib/loader.js.map +1 -1
  115. package/lib/noopHeuristic.d.ts +1 -1
  116. package/lib/noopHeuristic.d.ts.map +1 -1
  117. package/lib/noopHeuristic.js +3 -1
  118. package/lib/noopHeuristic.js.map +1 -1
  119. package/lib/packageVersion.d.ts +1 -1
  120. package/lib/packageVersion.js +1 -1
  121. package/lib/packageVersion.js.map +1 -1
  122. package/lib/protocol/index.d.ts +7 -0
  123. package/lib/protocol/index.d.ts.map +1 -0
  124. package/lib/protocol/index.js +7 -0
  125. package/lib/protocol/index.js.map +1 -0
  126. package/lib/protocol/protocol.d.ts +52 -0
  127. package/lib/protocol/protocol.d.ts.map +1 -0
  128. package/lib/protocol/protocol.js +111 -0
  129. package/lib/protocol/protocol.js.map +1 -0
  130. package/lib/protocol/quorum.d.ts +185 -0
  131. package/lib/protocol/quorum.d.ts.map +1 -0
  132. package/lib/protocol/quorum.js +431 -0
  133. package/lib/protocol/quorum.js.map +1 -0
  134. package/lib/protocol.d.ts +2 -3
  135. package/lib/protocol.d.ts.map +1 -1
  136. package/lib/protocol.js +3 -1
  137. package/lib/protocol.js.map +1 -1
  138. package/lib/protocolTreeDocumentStorageService.d.ts +7 -7
  139. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  140. package/lib/protocolTreeDocumentStorageService.js +16 -7
  141. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  142. package/lib/retriableDocumentStorageService.js +4 -1
  143. package/lib/retriableDocumentStorageService.js.map +1 -1
  144. package/lib/serializedStateManager.d.ts +1 -2
  145. package/lib/serializedStateManager.d.ts.map +1 -1
  146. package/lib/serializedStateManager.js +10 -2
  147. package/lib/serializedStateManager.js.map +1 -1
  148. package/package.json +15 -13
  149. package/src/catchUpMonitor.ts +1 -1
  150. package/src/connectionManager.ts +3 -7
  151. package/src/container.ts +4 -4
  152. package/src/containerContext.ts +2 -5
  153. package/src/contracts.ts +3 -6
  154. package/src/deltaManager.ts +3 -5
  155. package/src/index.ts +7 -0
  156. package/src/noopHeuristic.ts +1 -1
  157. package/src/packageVersion.ts +1 -1
  158. package/src/protocol/README.md +10 -0
  159. package/src/protocol/index.ts +16 -0
  160. package/src/protocol/protocol.ts +185 -0
  161. package/src/protocol/quorum.ts +584 -0
  162. package/src/protocol.ts +4 -6
  163. package/src/protocolTreeDocumentStorageService.ts +16 -8
  164. package/src/serializedStateManager.ts +1 -1
  165. package/tsconfig.json +2 -0
package/dist/container.js CHANGED
@@ -197,6 +197,41 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
197
197
  return container;
198
198
  }, { start: true, end: true, cancel: "generic" });
199
199
  }
200
+ // Tells if container can reconnect on losing fist connection
201
+ // If false, container gets closed on loss of connection.
202
+ _canReconnect;
203
+ clientDetailsOverride;
204
+ urlResolver;
205
+ serviceFactory;
206
+ codeLoader;
207
+ options;
208
+ scope;
209
+ subLogger;
210
+ // eslint-disable-next-line import/no-deprecated
211
+ detachedBlobStorage;
212
+ protocolHandlerBuilder;
213
+ client;
214
+ mc;
215
+ /**
216
+ * Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
217
+ */
218
+ clone;
219
+ /**
220
+ * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
221
+ *
222
+ * States are allowed to progress to further states:
223
+ * "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
224
+ *
225
+ * For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
226
+ *
227
+ * loading: Container has been created, but is not yet in normal/loaded state
228
+ * loaded: Container is in normal/loaded state
229
+ * closing: Container has started closing process (for re-entrancy prevention)
230
+ * disposing: Container has started disposing process (for re-entrancy prevention)
231
+ * closed: Container has closed
232
+ * disposed: Container has been disposed
233
+ */
234
+ _lifecycleState = "loading";
200
235
  setLoaded() {
201
236
  // It's conceivable the container could be closed when this is called
202
237
  // Only transition states if currently loading
@@ -239,18 +274,39 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
239
274
  get disposed() {
240
275
  return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
241
276
  }
277
+ storageAdapter;
278
+ _deltaManager;
279
+ service;
280
+ _runtime;
242
281
  get runtime() {
243
282
  if (this._runtime === undefined) {
244
283
  throw new Error("Attempted to access runtime before it was defined");
245
284
  }
246
285
  return this._runtime;
247
286
  }
287
+ _protocolHandler;
248
288
  get protocolHandler() {
249
289
  if (this._protocolHandler === undefined) {
250
290
  throw new Error("Attempted to access protocolHandler before it was defined");
251
291
  }
252
292
  return this._protocolHandler;
253
293
  }
294
+ /** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
295
+ inboundQueuePausedFromInit = true;
296
+ firstConnection = true;
297
+ connectionTransitionTimes = [];
298
+ _loadedFromVersion;
299
+ _dirtyContainer = false;
300
+ attachmentData = { state: container_definitions_1.AttachState.Detached };
301
+ serializedStateManager;
302
+ _containerId;
303
+ lastVisible;
304
+ visibilityEventHandler;
305
+ connectionStateHandler;
306
+ clientsWhoShouldHaveLeft = new Set();
307
+ _containerMetadata = {};
308
+ setAutoReconnectTime = client_utils_1.performance.now();
309
+ noopHeuristic;
254
310
  get connectionMode() {
255
311
  return this._deltaManager.connectionManager.connectionMode;
256
312
  }
@@ -321,6 +377,7 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
321
377
  getSpecifiedCodeDetails() {
322
378
  return this.getCodeDetailsFromQuorum();
323
379
  }
380
+ _loadedCodeDetails;
324
381
  /**
325
382
  * Get the code details that were used to load the container.
326
383
  * @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
@@ -329,6 +386,7 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
329
386
  getLoadedCodeDetails() {
330
387
  return this._loadedCodeDetails;
331
388
  }
389
+ _loadedModule;
332
390
  /**
333
391
  * Retrieves the audience associated with the document
334
392
  */
@@ -367,6 +425,7 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
367
425
  this._lifecycleEvents.once("disposed", disposedHandler);
368
426
  });
369
427
  }
428
+ _lifecycleEvents = new client_utils_1.TypedEventEmitter();
370
429
  constructor(createProps, loadProps) {
371
430
  super((name, error) => {
372
431
  this.mc.logger.sendErrorEvent({
@@ -375,128 +434,6 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
375
434
  }, error);
376
435
  this.close((0, internal_5.normalizeError)(error));
377
436
  });
378
- /**
379
- * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
380
- *
381
- * States are allowed to progress to further states:
382
- * "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
383
- *
384
- * For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
385
- *
386
- * loading: Container has been created, but is not yet in normal/loaded state
387
- * loaded: Container is in normal/loaded state
388
- * closing: Container has started closing process (for re-entrancy prevention)
389
- * disposing: Container has started disposing process (for re-entrancy prevention)
390
- * closed: Container has closed
391
- * disposed: Container has been disposed
392
- */
393
- this._lifecycleState = "loading";
394
- /** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
395
- this.inboundQueuePausedFromInit = true;
396
- this.firstConnection = true;
397
- this.connectionTransitionTimes = [];
398
- this._dirtyContainer = false;
399
- this.attachmentData = { state: container_definitions_1.AttachState.Detached };
400
- this.clientsWhoShouldHaveLeft = new Set();
401
- this._containerMetadata = {};
402
- this.setAutoReconnectTime = client_utils_1.performance.now();
403
- this._lifecycleEvents = new client_utils_1.TypedEventEmitter();
404
- this._disposed = false;
405
- this.attach = (0, utils_js_1.runSingle)(async (request, attachProps) => {
406
- await internal_5.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
407
- if (this._lifecycleState !== "loaded" ||
408
- this.attachmentData.state === container_definitions_1.AttachState.Attached) {
409
- // pre-0.58 error message: containerNotValidForAttach
410
- throw new internal_5.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
411
- }
412
- const normalizeErrorAndClose = (error) => {
413
- const newError = (0, internal_5.normalizeError)(error);
414
- this.close(newError);
415
- // add resolved URL on error object so that host has the ability to find this document and delete it
416
- newError.addTelemetryProperties({
417
- resolvedUrl: this.service?.resolvedUrl?.url,
418
- });
419
- return newError;
420
- };
421
- const setAttachmentData = (attachmentData) => {
422
- const previousState = this.attachmentData.state;
423
- this.attachmentData = attachmentData;
424
- const state = this.attachmentData.state;
425
- if (state !== previousState && state !== container_definitions_1.AttachState.Detached) {
426
- try {
427
- this.runtime.setAttachState(state);
428
- this.emit(state.toLocaleLowerCase());
429
- }
430
- catch (error) {
431
- throw normalizeErrorAndClose(error);
432
- }
433
- }
434
- };
435
- const createAttachmentSummary = (redirectTable) => {
436
- try {
437
- (0, internal_2.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
438
- return (0, utils_js_1.combineAppAndProtocolSummary)(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
439
- }
440
- catch (error) {
441
- throw normalizeErrorAndClose(error);
442
- }
443
- };
444
- const createOrGetStorageService = async (summary) => {
445
- // Actually go and create the resolved document
446
- if (this.service === undefined) {
447
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
448
- (0, internal_2.assert)(this.client.details.type !== summarizerClientType &&
449
- createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
450
- this.service = await (0, internal_4.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
451
- cancel: this._deltaManager.closeAbortController.signal,
452
- });
453
- }
454
- this.storageAdapter.connectToService(this.service);
455
- return this.storageAdapter;
456
- };
457
- let attachP = (0, attachment_js_1.runRetriableAttachProcess)({
458
- initialAttachmentData: this.attachmentData,
459
- offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
460
- detachedBlobStorage: this.detachedBlobStorage,
461
- setAttachmentData,
462
- createAttachmentSummary,
463
- createOrGetStorageService,
464
- });
465
- // only enable the new behavior if the config is set
466
- if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
467
- attachP = attachP.catch((error) => {
468
- throw normalizeErrorAndClose(error);
469
- });
470
- }
471
- // If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
472
- const snapshotWithBlobs = await attachP;
473
- this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
474
- if (!this.closed) {
475
- this.detachedBlobStorage.dispose?.();
476
- this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
477
- fetchOpsFromStorage: false,
478
- reason: { text: "createDetached" },
479
- });
480
- }
481
- }, { start: true, end: true, cancel: "generic" });
482
- });
483
- this.getAbsoluteUrl = async (relativeUrl) => {
484
- if (this.resolvedUrl === undefined) {
485
- return undefined;
486
- }
487
- return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_js_1.getPackageName)(this._loadedCodeDetails));
488
- };
489
- this.metadataUpdateHandler = (metadata) => {
490
- this._containerMetadata = { ...this._containerMetadata, ...metadata };
491
- this.emit("metadataUpdate", metadata);
492
- };
493
- this.updateDirtyContainerState = (dirty) => {
494
- if (this._dirtyContainer === dirty) {
495
- return;
496
- }
497
- this._dirtyContainer = dirty;
498
- this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
499
- };
500
437
  const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
501
438
  this.connectionTransitionTimes[connectionState_js_1.ConnectionState.Disconnected] = client_utils_1.performance.now();
502
439
  const pendingLocalState = loadProps?.pendingLocalState;
@@ -641,7 +578,7 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
641
578
  const offlineLoadEnabled = (this.isInteractiveClient &&
642
579
  this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
643
580
  options.enableOfflineLoad === true;
644
- this.serializedStateManager = new serializedStateManager_js_1.SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this.isDirty);
581
+ this.serializedStateManager = new serializedStateManager_js_1.SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this._deltaManager.connectionManager.shouldJoinWrite());
645
582
  const isDomAvailable = typeof document === "object" &&
646
583
  document !== null &&
647
584
  typeof document.addEventListener === "function" &&
@@ -724,6 +661,7 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
724
661
  }
725
662
  }
726
663
  }
664
+ _disposed = false;
727
665
  disposeCore(error) {
728
666
  (0, internal_2.assert)(!this._disposed, 0x54c /* Container already disposed */);
729
667
  this._disposed = true;
@@ -822,6 +760,84 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
822
760
  };
823
761
  return JSON.stringify(detachedContainerState);
824
762
  }
763
+ attach = (0, utils_js_1.runSingle)(async (request, attachProps) => {
764
+ await internal_5.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
765
+ if (this._lifecycleState !== "loaded" ||
766
+ this.attachmentData.state === container_definitions_1.AttachState.Attached) {
767
+ // pre-0.58 error message: containerNotValidForAttach
768
+ throw new internal_5.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
769
+ }
770
+ const normalizeErrorAndClose = (error) => {
771
+ const newError = (0, internal_5.normalizeError)(error);
772
+ this.close(newError);
773
+ // add resolved URL on error object so that host has the ability to find this document and delete it
774
+ newError.addTelemetryProperties({
775
+ resolvedUrl: this.service?.resolvedUrl?.url,
776
+ });
777
+ return newError;
778
+ };
779
+ const setAttachmentData = (attachmentData) => {
780
+ const previousState = this.attachmentData.state;
781
+ this.attachmentData = attachmentData;
782
+ const state = this.attachmentData.state;
783
+ if (state !== previousState && state !== container_definitions_1.AttachState.Detached) {
784
+ try {
785
+ this.runtime.setAttachState(state);
786
+ this.emit(state.toLocaleLowerCase());
787
+ }
788
+ catch (error) {
789
+ throw normalizeErrorAndClose(error);
790
+ }
791
+ }
792
+ };
793
+ const createAttachmentSummary = (redirectTable) => {
794
+ try {
795
+ (0, internal_2.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
796
+ return (0, utils_js_1.combineAppAndProtocolSummary)(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
797
+ }
798
+ catch (error) {
799
+ throw normalizeErrorAndClose(error);
800
+ }
801
+ };
802
+ const createOrGetStorageService = async (summary) => {
803
+ // Actually go and create the resolved document
804
+ if (this.service === undefined) {
805
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
806
+ (0, internal_2.assert)(this.client.details.type !== summarizerClientType &&
807
+ createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
808
+ this.service = await (0, internal_4.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
809
+ cancel: this._deltaManager.closeAbortController.signal,
810
+ });
811
+ }
812
+ this.storageAdapter.connectToService(this.service);
813
+ return this.storageAdapter;
814
+ };
815
+ let attachP = (0, attachment_js_1.runRetriableAttachProcess)({
816
+ initialAttachmentData: this.attachmentData,
817
+ offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
818
+ detachedBlobStorage: this.detachedBlobStorage,
819
+ setAttachmentData,
820
+ createAttachmentSummary,
821
+ createOrGetStorageService,
822
+ });
823
+ // only enable the new behavior if the config is set
824
+ if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
825
+ attachP = attachP.catch((error) => {
826
+ throw normalizeErrorAndClose(error);
827
+ });
828
+ }
829
+ // If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
830
+ const snapshotWithBlobs = await attachP;
831
+ this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
832
+ if (!this.closed) {
833
+ this.detachedBlobStorage.dispose?.();
834
+ this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
835
+ fetchOpsFromStorage: false,
836
+ reason: { text: "createDetached" },
837
+ });
838
+ }
839
+ }, { start: true, end: true, cancel: "generic" });
840
+ });
825
841
  setAutoReconnectInternal(mode, reason) {
826
842
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
827
843
  if (currentMode === mode) {
@@ -894,6 +910,12 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
894
910
  // Ensure connection to web socket
895
911
  this.connectToDeltaStream(args);
896
912
  }
913
+ getAbsoluteUrl = async (relativeUrl) => {
914
+ if (this.resolvedUrl === undefined) {
915
+ return undefined;
916
+ }
917
+ return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_js_1.getPackageName)(this._loadedCodeDetails));
918
+ };
897
919
  async proposeCodeDetails(codeDetails) {
898
920
  if (!(0, internal_1.isFluidCodeDetails)(codeDetails)) {
899
921
  throw new Error("Provided codeDetails are not IFluidCodeDetails");
@@ -962,6 +984,10 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
962
984
  }
963
985
  this._deltaManager.connect(args);
964
986
  }
987
+ metadataUpdateHandler = (metadata) => {
988
+ this._containerMetadata = { ...this._containerMetadata, ...metadata };
989
+ this.emit("metadataUpdate", metadata);
990
+ };
965
991
  async createDocumentService(serviceProvider) {
966
992
  const service = await serviceProvider();
967
993
  // Back-compat for Old driver
@@ -1507,6 +1533,13 @@ class Container extends internal_5.EventEmitterWithErrorHandling {
1507
1533
  this._lifecycleEvents.emit("runtimeInstantiated");
1508
1534
  this._loadedCodeDetails = codeDetails;
1509
1535
  }
1536
+ updateDirtyContainerState = (dirty) => {
1537
+ if (this._dirtyContainer === dirty) {
1538
+ return;
1539
+ }
1540
+ this._dirtyContainer = dirty;
1541
+ this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
1542
+ };
1510
1543
  /**
1511
1544
  * Set the connected state of the ContainerContext
1512
1545
  * This controls the "connected" state of the ContainerRuntime as well