@fluidframework/container-loader 2.0.0-dev-rc.5.0.0.268409 → 2.0.0-dev-rc.5.0.0.270987

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