@fluidframework/container-loader 2.118.1 → 3.0.1

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 (213) hide show
  1. package/CHANGELOG.md +74 -4
  2. package/README.md +53 -50
  3. package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
  4. package/api-report/container-loader.legacy.alpha.api.md +4 -3
  5. package/dist/attachment.d.ts.map +1 -1
  6. package/dist/audience.js +3 -2
  7. package/dist/audience.js.map +1 -1
  8. package/dist/captureReferencedContents.js +9 -9
  9. package/dist/captureReferencedContents.js.map +1 -1
  10. package/dist/catchUpMonitor.js +11 -8
  11. package/dist/catchUpMonitor.js.map +1 -1
  12. package/dist/connectionManager.d.ts.map +1 -1
  13. package/dist/connectionManager.js +86 -51
  14. package/dist/connectionManager.js.map +1 -1
  15. package/dist/connectionStateHandler.js +40 -14
  16. package/dist/connectionStateHandler.js.map +1 -1
  17. package/dist/container.d.ts +1 -1
  18. package/dist/container.d.ts.map +1 -1
  19. package/dist/container.js +193 -143
  20. package/dist/container.js.map +1 -1
  21. package/dist/containerContext.js +48 -11
  22. package/dist/containerContext.js.map +1 -1
  23. package/dist/containerStorageAdapter.js +25 -17
  24. package/dist/containerStorageAdapter.js.map +1 -1
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/createAndLoadContainerUtils.d.ts +34 -13
  27. package/dist/createAndLoadContainerUtils.d.ts.map +1 -1
  28. package/dist/createAndLoadContainerUtils.js +97 -37
  29. package/dist/createAndLoadContainerUtils.js.map +1 -1
  30. package/dist/debugLogger.d.ts +7 -22
  31. package/dist/debugLogger.d.ts.map +1 -1
  32. package/dist/debugLogger.js +34 -34
  33. package/dist/debugLogger.js.map +1 -1
  34. package/dist/deltaManager.d.ts +16 -4
  35. package/dist/deltaManager.d.ts.map +1 -1
  36. package/dist/deltaManager.js +73 -48
  37. package/dist/deltaManager.js.map +1 -1
  38. package/dist/deltaManagerFactory.d.ts +23 -0
  39. package/dist/deltaManagerFactory.d.ts.map +1 -0
  40. package/dist/deltaManagerFactory.js +17 -0
  41. package/dist/deltaManagerFactory.js.map +1 -0
  42. package/dist/deltaQueue.js +14 -7
  43. package/dist/deltaQueue.js.map +1 -1
  44. package/dist/disposal.js +1 -2
  45. package/dist/disposal.js.map +1 -1
  46. package/dist/error.js +5 -4
  47. package/dist/error.js.map +1 -1
  48. package/dist/frozenServices.d.ts +2 -2
  49. package/dist/frozenServices.d.ts.map +1 -1
  50. package/dist/frozenServices.js +52 -34
  51. package/dist/frozenServices.js.map +1 -1
  52. package/dist/index.d.ts +1 -1
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +2 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/loadContainerToSequenceNumber.js +1 -2
  57. package/dist/loadContainerToSequenceNumber.js.map +1 -1
  58. package/dist/loadPaused.js +1 -2
  59. package/dist/loadPaused.js.map +1 -1
  60. package/dist/loader.js +5 -1
  61. package/dist/loader.js.map +1 -1
  62. package/dist/loaderLayerCompatState.d.ts +25 -1
  63. package/dist/loaderLayerCompatState.d.ts.map +1 -1
  64. package/dist/loaderLayerCompatState.js +79 -11
  65. package/dist/loaderLayerCompatState.js.map +1 -1
  66. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +2 -3
  67. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  68. package/dist/memoryBlobStorage.js +2 -3
  69. package/dist/memoryBlobStorage.js.map +1 -1
  70. package/dist/noopHeuristic.js +3 -1
  71. package/dist/noopHeuristic.js.map +1 -1
  72. package/dist/packageVersion.d.ts +1 -1
  73. package/dist/packageVersion.d.ts.map +1 -1
  74. package/dist/packageVersion.js +1 -1
  75. package/dist/packageVersion.js.map +1 -1
  76. package/dist/pendingLocalStateStore.d.ts +4 -3
  77. package/dist/pendingLocalStateStore.d.ts.map +1 -1
  78. package/dist/pendingLocalStateStore.js +26 -37
  79. package/dist/pendingLocalStateStore.js.map +1 -1
  80. package/dist/pointInTimeServices.js +4 -2
  81. package/dist/pointInTimeServices.js.map +1 -1
  82. package/dist/protocol/protocol.js +3 -0
  83. package/dist/protocol/protocol.js.map +1 -1
  84. package/dist/protocol/quorum.js +26 -5
  85. package/dist/protocol/quorum.js.map +1 -1
  86. package/dist/protocol.js +5 -3
  87. package/dist/protocol.js.map +1 -1
  88. package/dist/protocolTreeDocumentStorageService.js +10 -0
  89. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  90. package/dist/quorum.js +1 -2
  91. package/dist/quorum.js.map +1 -1
  92. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  93. package/dist/retriableDocumentStorageService.js +5 -1
  94. package/dist/retriableDocumentStorageService.js.map +1 -1
  95. package/dist/serializedStateManager.d.ts +6 -0
  96. package/dist/serializedStateManager.d.ts.map +1 -1
  97. package/dist/serializedStateManager.js +19 -24
  98. package/dist/serializedStateManager.js.map +1 -1
  99. package/dist/snapshotRefresher.js +31 -36
  100. package/dist/snapshotRefresher.js.map +1 -1
  101. package/dist/utils.d.ts +1 -1
  102. package/dist/utils.d.ts.map +1 -1
  103. package/dist/utils.js +13 -13
  104. package/dist/utils.js.map +1 -1
  105. package/lib/attachment.d.ts.map +1 -1
  106. package/lib/audience.js +3 -2
  107. package/lib/audience.js.map +1 -1
  108. package/lib/catchUpMonitor.js +11 -8
  109. package/lib/catchUpMonitor.js.map +1 -1
  110. package/lib/connectionManager.d.ts.map +1 -1
  111. package/lib/connectionManager.js +86 -51
  112. package/lib/connectionManager.js.map +1 -1
  113. package/lib/connectionStateHandler.js +37 -11
  114. package/lib/connectionStateHandler.js.map +1 -1
  115. package/lib/container.d.ts +1 -1
  116. package/lib/container.d.ts.map +1 -1
  117. package/lib/container.js +191 -141
  118. package/lib/container.js.map +1 -1
  119. package/lib/containerContext.js +48 -11
  120. package/lib/containerContext.js.map +1 -1
  121. package/lib/containerStorageAdapter.js +22 -14
  122. package/lib/containerStorageAdapter.js.map +1 -1
  123. package/lib/contracts.d.ts.map +1 -1
  124. package/lib/createAndLoadContainerUtils.d.ts +34 -13
  125. package/lib/createAndLoadContainerUtils.d.ts.map +1 -1
  126. package/lib/createAndLoadContainerUtils.js +93 -32
  127. package/lib/createAndLoadContainerUtils.js.map +1 -1
  128. package/lib/debugLogger.d.ts +7 -22
  129. package/lib/debugLogger.d.ts.map +1 -1
  130. package/lib/debugLogger.js +34 -33
  131. package/lib/debugLogger.js.map +1 -1
  132. package/lib/deltaManager.d.ts +16 -4
  133. package/lib/deltaManager.d.ts.map +1 -1
  134. package/lib/deltaManager.js +73 -48
  135. package/lib/deltaManager.js.map +1 -1
  136. package/lib/deltaManagerFactory.d.ts +23 -0
  137. package/lib/deltaManagerFactory.d.ts.map +1 -0
  138. package/lib/deltaManagerFactory.js +14 -0
  139. package/lib/deltaManagerFactory.js.map +1 -0
  140. package/lib/deltaQueue.js +14 -7
  141. package/lib/deltaQueue.js.map +1 -1
  142. package/lib/error.js +5 -4
  143. package/lib/error.js.map +1 -1
  144. package/lib/frozenServices.d.ts +2 -2
  145. package/lib/frozenServices.d.ts.map +1 -1
  146. package/lib/frozenServices.js +48 -30
  147. package/lib/frozenServices.js.map +1 -1
  148. package/lib/index.d.ts +1 -1
  149. package/lib/index.d.ts.map +1 -1
  150. package/lib/index.js +1 -1
  151. package/lib/index.js.map +1 -1
  152. package/lib/legacy.d.ts +1 -1
  153. package/lib/legacyAlpha.d.ts +1 -1
  154. package/lib/loader.js +6 -2
  155. package/lib/loader.js.map +1 -1
  156. package/lib/loaderLayerCompatState.d.ts +25 -1
  157. package/lib/loaderLayerCompatState.d.ts.map +1 -1
  158. package/lib/loaderLayerCompatState.js +76 -9
  159. package/lib/loaderLayerCompatState.js.map +1 -1
  160. package/lib/noopHeuristic.js +3 -1
  161. package/lib/noopHeuristic.js.map +1 -1
  162. package/lib/packageVersion.d.ts +1 -1
  163. package/lib/packageVersion.d.ts.map +1 -1
  164. package/lib/packageVersion.js +1 -1
  165. package/lib/packageVersion.js.map +1 -1
  166. package/lib/pendingLocalStateStore.d.ts +4 -3
  167. package/lib/pendingLocalStateStore.d.ts.map +1 -1
  168. package/lib/pendingLocalStateStore.js +26 -37
  169. package/lib/pendingLocalStateStore.js.map +1 -1
  170. package/lib/pointInTimeServices.js +2 -0
  171. package/lib/pointInTimeServices.js.map +1 -1
  172. package/lib/protocol/protocol.js +3 -0
  173. package/lib/protocol/protocol.js.map +1 -1
  174. package/lib/protocol/quorum.js +26 -5
  175. package/lib/protocol/quorum.js.map +1 -1
  176. package/lib/protocol.js +2 -0
  177. package/lib/protocol.js.map +1 -1
  178. package/lib/protocolTreeDocumentStorageService.js +10 -0
  179. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  180. package/lib/public.d.ts +1 -1
  181. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  182. package/lib/retriableDocumentStorageService.js +5 -1
  183. package/lib/retriableDocumentStorageService.js.map +1 -1
  184. package/lib/serializedStateManager.d.ts +6 -0
  185. package/lib/serializedStateManager.d.ts.map +1 -1
  186. package/lib/serializedStateManager.js +15 -20
  187. package/lib/serializedStateManager.js.map +1 -1
  188. package/lib/snapshotRefresher.js +31 -36
  189. package/lib/snapshotRefresher.js.map +1 -1
  190. package/lib/utils.d.ts +1 -1
  191. package/lib/utils.d.ts.map +1 -1
  192. package/lib/utils.js +3 -3
  193. package/lib/utils.js.map +1 -1
  194. package/package.json +44 -106
  195. package/src/container.ts +35 -22
  196. package/src/createAndLoadContainerUtils.ts +131 -46
  197. package/src/debugLogger.ts +39 -40
  198. package/src/deltaManager.ts +16 -4
  199. package/src/deltaManagerFactory.ts +53 -0
  200. package/src/frozenServices.ts +6 -7
  201. package/src/index.ts +1 -0
  202. package/src/loader.ts +2 -2
  203. package/src/loaderLayerCompatState.ts +85 -20
  204. package/src/packageVersion.ts +1 -1
  205. package/src/pendingLocalStateStore.ts +7 -3
  206. package/src/serializedStateManager.ts +6 -0
  207. package/src/utils.ts +3 -3
  208. package/tsconfig.json +1 -1
  209. package/internal.d.ts +0 -11
  210. package/legacy/alpha.d.ts +0 -11
  211. package/legacy.d.ts +0 -11
  212. /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
  213. /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
package/lib/container.js CHANGED
@@ -15,15 +15,14 @@ import { EventEmitterWithErrorHandling, GenericError, PerformanceEvent, UsageErr
15
15
  import { v4 as uuid } from "uuid";
16
16
  import { runRetriableAttachProcess, } from "./attachment.js";
17
17
  import { Audience } from "./audience.js";
18
- import { ConnectionManager } from "./connectionManager.js";
19
18
  import { ConnectionState } from "./connectionState.js";
20
19
  import { createConnectionStateHandler, } from "./connectionStateHandler.js";
21
20
  import { ContainerContext } from "./containerContext.js";
22
21
  import { ContainerStorageAdapter } from "./containerStorageAdapter.js";
23
22
  import { ReconnectMode, getPackageName, } from "./contracts.js";
24
- import { DeltaManager } from "./deltaManager.js";
23
+ import { createDeltaManager } from "./deltaManagerFactory.js";
25
24
  import { RelativeLoader } from "./loader.js";
26
- import { validateDriverCompatibility, validateRuntimeCompatibility, } from "./loaderLayerCompatState.js";
25
+ import { validateDriverCompatibility, validateLoaderCompatibilityWithDriver, validateRuntimeCompatibility, } from "./loaderLayerCompatState.js";
27
26
  import { createMemoryDetachedBlobStorage, tryInitializeMemoryDetachedBlobStorage, } from "./memoryBlobStorage.js";
28
27
  import { NoopHeuristic } from "./noopHeuristic.js";
29
28
  import { pkgVersion } from "./packageVersion.js";
@@ -177,6 +176,41 @@ export class Container extends EventEmitterWithErrorHandling {
177
176
  return container;
178
177
  }, { start: true, end: true, cancel: "generic" });
179
178
  }
179
+ // Tells if container can reconnect on losing fist connection
180
+ // If false, container gets closed on loss of connection.
181
+ _canReconnect;
182
+ clientDetailsOverride;
183
+ urlResolver;
184
+ serviceFactory;
185
+ codeLoader;
186
+ options;
187
+ scope;
188
+ subLogger;
189
+ detachedBlobStorage;
190
+ protocolHandlerBuilder;
191
+ signalAudience = new Audience();
192
+ client;
193
+ mc;
194
+ /**
195
+ * Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
196
+ */
197
+ clone;
198
+ /**
199
+ * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
200
+ *
201
+ * States are allowed to progress to further states:
202
+ * "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
203
+ *
204
+ * For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
205
+ *
206
+ * loading: Container has been created, but is not yet in normal/loaded state
207
+ * loaded: Container is in normal/loaded state
208
+ * closing: Container has started closing process (for re-entrancy prevention)
209
+ * disposing: Container has started disposing process (for re-entrancy prevention)
210
+ * closed: Container has closed
211
+ * disposed: Container has been disposed
212
+ */
213
+ _lifecycleState = "loading";
180
214
  setLoaded() {
181
215
  // It's conceivable the container could be closed when this is called
182
216
  // Only transition states if currently loading
@@ -223,18 +257,48 @@ export class Container extends EventEmitterWithErrorHandling {
223
257
  get disposed() {
224
258
  return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
225
259
  }
260
+ storageAdapter;
261
+ _deltaManager;
262
+ service;
263
+ fetchOps = async (from, to, abortSignal) => {
264
+ const deltaStorage = await this.service?.connectToDeltaStorage();
265
+ if (deltaStorage === undefined) {
266
+ throw new Error("Cannot fetch ops: delta storage is unavailable");
267
+ }
268
+ return deltaStorage.fetchMessages(from, to, abortSignal);
269
+ };
270
+ _runtime;
226
271
  get runtime() {
227
272
  if (this._runtime === undefined) {
228
273
  throw new Error("Attempted to access runtime before it was defined");
229
274
  }
230
275
  return this._runtime;
231
276
  }
277
+ _protocolHandler;
232
278
  get protocolHandler() {
233
279
  if (this._protocolHandler === undefined) {
234
280
  throw new Error("Attempted to access protocolHandler before it was defined");
235
281
  }
236
282
  return this._protocolHandler;
237
283
  }
284
+ /**
285
+ * During initialization we pause the inbound queues. We track this state to ensure we only call resume once
286
+ */
287
+ inboundQueuePausedFromInit = true;
288
+ connectionCount = 0;
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 = performanceNow();
301
+ noopHeuristic;
238
302
  get connectionMode() {
239
303
  return this._deltaManager.connectionManager.connectionMode;
240
304
  }
@@ -310,6 +374,7 @@ export class Container extends EventEmitterWithErrorHandling {
310
374
  getSpecifiedCodeDetails() {
311
375
  return this.getCodeDetailsFromQuorum();
312
376
  }
377
+ _loadedCodeDetails;
313
378
  /**
314
379
  * Get the code details that were used to load the container.
315
380
  * @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
@@ -318,6 +383,7 @@ export class Container extends EventEmitterWithErrorHandling {
318
383
  getLoadedCodeDetails() {
319
384
  return this._loadedCodeDetails;
320
385
  }
386
+ _loadedModule;
321
387
  /**
322
388
  * Retrieves the audience associated with the document
323
389
  */
@@ -356,6 +422,7 @@ export class Container extends EventEmitterWithErrorHandling {
356
422
  this._lifecycleEvents.once("disposed", disposedHandler);
357
423
  });
358
424
  }
425
+ _lifecycleEvents = new TypedEventEmitter();
359
426
  constructor(createProps, loadProps) {
360
427
  super((name, error) => {
361
428
  this.mc.logger.sendErrorEvent({
@@ -364,144 +431,22 @@ export class Container extends EventEmitterWithErrorHandling {
364
431
  }, error);
365
432
  this.close(normalizeError(error));
366
433
  });
367
- this.signalAudience = new Audience();
368
- /**
369
- * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
370
- *
371
- * States are allowed to progress to further states:
372
- * "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
373
- *
374
- * For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
375
- *
376
- * loading: Container has been created, but is not yet in normal/loaded state
377
- * loaded: Container is in normal/loaded state
378
- * closing: Container has started closing process (for re-entrancy prevention)
379
- * disposing: Container has started disposing process (for re-entrancy prevention)
380
- * closed: Container has closed
381
- * disposed: Container has been disposed
382
- */
383
- this._lifecycleState = "loading";
384
- this.fetchOps = async (from, to, abortSignal) => {
385
- const deltaStorage = await this.service?.connectToDeltaStorage();
386
- if (deltaStorage === undefined) {
387
- throw new Error("Cannot fetch ops: delta storage is unavailable");
388
- }
389
- return deltaStorage.fetchMessages(from, to, abortSignal);
390
- };
391
- /**
392
- * During initialization we pause the inbound queues. We track this state to ensure we only call resume once
393
- */
394
- this.inboundQueuePausedFromInit = true;
395
- this.connectionCount = 0;
396
- this.connectionTransitionTimes = [];
397
- this._dirtyContainer = false;
398
- this.attachmentData = { state: AttachState.Detached };
399
- this.clientsWhoShouldHaveLeft = new Set();
400
- this._containerMetadata = {};
401
- this.setAutoReconnectTime = performanceNow();
402
- this._lifecycleEvents = new TypedEventEmitter();
403
- this.verifyClosedAfterCalls = 0;
404
- this._disposed = false;
405
- this.attach = runSingle(async (request, attachProps) => {
406
- await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
407
- if (this._lifecycleState !== "loaded" ||
408
- this.attachmentData.state === AttachState.Attached) {
409
- // pre-0.58 error message: containerNotValidForAttach
410
- throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
411
- }
412
- const normalizeErrorAndClose = (error) => {
413
- const newError = 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 !== 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
- assert(this._deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
438
- return 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
- assert(this.client.details.type !== summarizerClientType &&
449
- createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
450
- this.service = await this.createDocumentService(createNewResolvedUrl, {
451
- mode: "attach",
452
- summary,
453
- });
454
- }
455
- this.storageAdapter.connectToService(this.service);
456
- return this.storageAdapter;
457
- };
458
- let attachP = runRetriableAttachProcess({
459
- initialAttachmentData: this.attachmentData,
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.DisableCloseOnAttachFailure") !== 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, 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
434
  const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, protocolHandlerBuilder, } = createProps;
501
- // Validate that the Driver is compatible with this Loader.
502
- const maybeDriverCompatDetails = documentServiceFactory;
503
- validateDriverCompatibility(maybeDriverCompatDetails.ILayerCompatDetails, (error) => { } /* disposeFn */, // There is nothing to dispose here, so just ignore the error.
504
- createChildMonitoringContext({ logger: subLogger, namespace: "Container" }));
435
+ const maybeDriverCompat = documentServiceFactory;
436
+ const driverCompatMonitoringContext = createChildMonitoringContext({
437
+ logger: subLogger,
438
+ namespace: "Container",
439
+ });
440
+ // Validate that the Driver is compatible with this Loader. This is the standard direction: the Loader holds
441
+ // a reference to the Driver and validates it.
442
+ validateDriverCompatibility(maybeDriverCompat.ILayerCompatDetails, (error) => { } /* disposeFn */, // There is nothing to dispose here, so just ignore the error.
443
+ driverCompatMonitoringContext);
444
+ // Validate that this Loader is compatible with the Driver. This is the reverse, non-standard direction: a
445
+ // layer normally validates the layer it holds a reference to, but the Driver has no reference to the Loader
446
+ // and so cannot validate it itself. The Driver instead publishes the requirements it has for the Loader (via
447
+ // ILayerCompatSupportRequirements), and the Loader validates itself against them here, on the Driver's behalf.
448
+ validateLoaderCompatibilityWithDriver(maybeDriverCompat.ILayerCompatDetails, maybeDriverCompat.ILayerCompatSupportRequirements, (error) => { } /* disposeFn */, // There is nothing to dispose here, so just ignore the error.
449
+ driverCompatMonitoringContext);
505
450
  this.connectionTransitionTimes[ConnectionState.Disconnected] = performanceNow();
506
451
  const pendingLocalState = loadProps?.pendingLocalState;
507
452
  this._canReconnect = canReconnect ?? true;
@@ -682,6 +627,7 @@ export class Container extends EventEmitterWithErrorHandling {
682
627
  // "closing" will lose that info (can also solve by tracking extra state).
683
628
  this.verifyClosedAfter(() => this._deltaManager.close(error));
684
629
  }
630
+ verifyClosedAfterCalls = 0;
685
631
  verifyClosedAfter(callback) {
686
632
  this.verifyClosedAfterCalls++;
687
633
  try {
@@ -736,6 +682,7 @@ export class Container extends EventEmitterWithErrorHandling {
736
682
  }
737
683
  }
738
684
  }
685
+ _disposed = false;
739
686
  disposeCore(error) {
740
687
  assert(!this._disposed, 0x54c /* Container already disposed */);
741
688
  this._disposed = true;
@@ -822,6 +769,84 @@ export class Container extends EventEmitterWithErrorHandling {
822
769
  };
823
770
  return JSON.stringify(detachedContainerState);
824
771
  }
772
+ attach = runSingle(async (request, attachProps) => {
773
+ await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
774
+ if (this._lifecycleState !== "loaded" ||
775
+ this.attachmentData.state === AttachState.Attached) {
776
+ // pre-0.58 error message: containerNotValidForAttach
777
+ throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
778
+ }
779
+ const normalizeErrorAndClose = (error) => {
780
+ const newError = normalizeError(error);
781
+ this.close(newError);
782
+ // add resolved URL on error object so that host has the ability to find this document and delete it
783
+ newError.addTelemetryProperties({
784
+ resolvedUrl: this.service?.resolvedUrl?.url,
785
+ });
786
+ return newError;
787
+ };
788
+ const setAttachmentData = (attachmentData) => {
789
+ const previousState = this.attachmentData.state;
790
+ this.attachmentData = attachmentData;
791
+ const state = this.attachmentData.state;
792
+ if (state !== previousState && state !== AttachState.Detached) {
793
+ try {
794
+ this.runtime.setAttachState(state);
795
+ this.emit(state.toLocaleLowerCase());
796
+ }
797
+ catch (error) {
798
+ throw normalizeErrorAndClose(error);
799
+ }
800
+ }
801
+ };
802
+ const createAttachmentSummary = (redirectTable) => {
803
+ try {
804
+ assert(this._deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
805
+ return combineAppAndProtocolSummary(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
806
+ }
807
+ catch (error) {
808
+ throw normalizeErrorAndClose(error);
809
+ }
810
+ };
811
+ const createOrGetStorageService = async (summary) => {
812
+ // Actually go and create the resolved document
813
+ if (this.service === undefined) {
814
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
815
+ assert(this.client.details.type !== summarizerClientType &&
816
+ createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
817
+ this.service = await this.createDocumentService(createNewResolvedUrl, {
818
+ mode: "attach",
819
+ summary,
820
+ });
821
+ }
822
+ this.storageAdapter.connectToService(this.service);
823
+ return this.storageAdapter;
824
+ };
825
+ let attachP = runRetriableAttachProcess({
826
+ initialAttachmentData: this.attachmentData,
827
+ detachedBlobStorage: this.detachedBlobStorage,
828
+ setAttachmentData,
829
+ createAttachmentSummary,
830
+ createOrGetStorageService,
831
+ });
832
+ // only enable the new behavior if the config is set
833
+ if (this.mc.config.getBoolean("Fluid.Container.DisableCloseOnAttachFailure") !== true) {
834
+ attachP = attachP.catch((error) => {
835
+ throw normalizeErrorAndClose(error);
836
+ });
837
+ }
838
+ // If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
839
+ const snapshotWithBlobs = await attachP;
840
+ this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
841
+ if (!this.closed) {
842
+ this.detachedBlobStorage?.dispose?.();
843
+ this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
844
+ fetchOpsFromStorage: false,
845
+ reason: { text: "createDetached" },
846
+ });
847
+ }
848
+ }, { start: true, end: true, cancel: "generic" });
849
+ });
825
850
  setAutoReconnectInternal(mode, reason) {
826
851
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
827
852
  if (currentMode === mode) {
@@ -894,6 +919,12 @@ export class Container extends EventEmitterWithErrorHandling {
894
919
  // Ensure connection to web socket
895
920
  this.connectToDeltaStream(args);
896
921
  }
922
+ getAbsoluteUrl = async (relativeUrl) => {
923
+ if (this.resolvedUrl === undefined) {
924
+ return undefined;
925
+ }
926
+ return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
927
+ };
897
928
  async proposeCodeDetails(codeDetails) {
898
929
  if (!isFluidCodeDetails(codeDetails)) {
899
930
  throw new Error("Provided codeDetails are not IFluidCodeDetails");
@@ -962,6 +993,10 @@ export class Container extends EventEmitterWithErrorHandling {
962
993
  }
963
994
  this._deltaManager.connect(args);
964
995
  }
996
+ metadataUpdateHandler = (metadata) => {
997
+ this._containerMetadata = { ...this._containerMetadata, ...metadata };
998
+ this.emit("metadataUpdate", metadata);
999
+ };
965
1000
  /**
966
1001
  * Creates a document service during container attachment or loading.
967
1002
  * @param resolvedUrl - The resolved URL of the container.
@@ -1283,7 +1318,15 @@ export class Container extends EventEmitterWithErrorHandling {
1283
1318
  createDeltaManager() {
1284
1319
  const serviceProvider = () => this.service;
1285
1320
  const disableLoadConnectionRetries = this.mc.config.getBoolean("Fluid.Container.DisableLoadConnectionRetries") === true;
1286
- const deltaManager = new DeltaManager(serviceProvider, createChildLogger({ logger: this.subLogger, namespace: "DeltaManager" }), () => this.activeConnection(), (props) => new ConnectionManager(serviceProvider, () => this.isDirty, this.client, this._canReconnect, createChildLogger({ logger: this.subLogger, namespace: "ConnectionManager" }), props, disableLoadConnectionRetries ? 1 : undefined /* maxInitialConnectionAttempts */));
1321
+ const deltaManager = createDeltaManager({
1322
+ serviceProvider,
1323
+ logger: this.subLogger,
1324
+ active: () => this.activeConnection(),
1325
+ containerDirty: () => this.isDirty,
1326
+ client: this.client,
1327
+ reconnectAllowed: this._canReconnect,
1328
+ maxInitialConnectionAttempts: disableLoadConnectionRetries ? 1 : undefined,
1329
+ });
1287
1330
  // Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
1288
1331
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1289
1332
  deltaManager.inbound.pause();
@@ -1607,6 +1650,13 @@ export class Container extends EventEmitterWithErrorHandling {
1607
1650
  throw error;
1608
1651
  }
1609
1652
  }
1653
+ updateDirtyContainerState = (dirty) => {
1654
+ if (this._dirtyContainer === dirty) {
1655
+ return;
1656
+ }
1657
+ this._dirtyContainer = dirty;
1658
+ this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
1659
+ };
1610
1660
  /**
1611
1661
  * Send the connected status to the runtime.
1612
1662
  * @param readonly - Is the container in readonly mode?