@fluidframework/container-loader 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189

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 (179) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +27 -3
  3. package/dist/catchUpMonitor.d.ts +1 -1
  4. package/dist/catchUpMonitor.d.ts.map +1 -1
  5. package/dist/catchUpMonitor.js.map +1 -1
  6. package/dist/connectionManager.d.ts +3 -2
  7. package/dist/connectionManager.d.ts.map +1 -1
  8. package/dist/connectionManager.js +32 -13
  9. package/dist/connectionManager.js.map +1 -1
  10. package/dist/connectionStateHandler.d.ts +18 -3
  11. package/dist/connectionStateHandler.d.ts.map +1 -1
  12. package/dist/connectionStateHandler.js +34 -9
  13. package/dist/connectionStateHandler.js.map +1 -1
  14. package/dist/container.d.ts +99 -70
  15. package/dist/container.d.ts.map +1 -1
  16. package/dist/container.js +260 -218
  17. package/dist/container.js.map +1 -1
  18. package/dist/containerContext.d.ts +24 -67
  19. package/dist/containerContext.d.ts.map +1 -1
  20. package/dist/containerContext.js +28 -217
  21. package/dist/containerContext.js.map +1 -1
  22. package/dist/containerStorageAdapter.d.ts +3 -3
  23. package/dist/containerStorageAdapter.d.ts.map +1 -1
  24. package/dist/containerStorageAdapter.js +29 -6
  25. package/dist/containerStorageAdapter.js.map +1 -1
  26. package/dist/contracts.d.ts +9 -3
  27. package/dist/contracts.d.ts.map +1 -1
  28. package/dist/contracts.js.map +1 -1
  29. package/dist/deltaManager.d.ts +22 -9
  30. package/dist/deltaManager.d.ts.map +1 -1
  31. package/dist/deltaManager.js +42 -31
  32. package/dist/deltaManager.js.map +1 -1
  33. package/dist/deltaQueue.d.ts +2 -3
  34. package/dist/deltaQueue.d.ts.map +1 -1
  35. package/dist/deltaQueue.js +2 -3
  36. package/dist/deltaQueue.js.map +1 -1
  37. package/dist/disposal.d.ts +13 -0
  38. package/dist/disposal.d.ts.map +1 -0
  39. package/dist/disposal.js +25 -0
  40. package/dist/disposal.js.map +1 -0
  41. package/dist/index.d.ts +1 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/loader.d.ts +9 -8
  45. package/dist/loader.d.ts.map +1 -1
  46. package/dist/loader.js +47 -61
  47. package/dist/loader.js.map +1 -1
  48. package/dist/noopHeuristic.d.ts +23 -0
  49. package/dist/noopHeuristic.d.ts.map +1 -0
  50. package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  51. package/dist/noopHeuristic.js.map +1 -0
  52. package/dist/packageVersion.d.ts +1 -1
  53. package/dist/packageVersion.js +1 -1
  54. package/dist/packageVersion.js.map +1 -1
  55. package/dist/protocol.d.ts +7 -12
  56. package/dist/protocol.d.ts.map +1 -1
  57. package/dist/protocol.js +17 -19
  58. package/dist/protocol.js.map +1 -1
  59. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  60. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  61. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  62. package/dist/quorum.d.ts +1 -17
  63. package/dist/quorum.d.ts.map +1 -1
  64. package/dist/quorum.js +1 -17
  65. package/dist/quorum.js.map +1 -1
  66. package/dist/retriableDocumentStorageService.d.ts +3 -2
  67. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  68. package/dist/retriableDocumentStorageService.js.map +1 -1
  69. package/dist/tsdoc-metadata.json +11 -0
  70. package/dist/utils.d.ts +2 -0
  71. package/dist/utils.d.ts.map +1 -1
  72. package/dist/utils.js +8 -1
  73. package/dist/utils.js.map +1 -1
  74. package/lib/catchUpMonitor.d.ts +1 -1
  75. package/lib/catchUpMonitor.d.ts.map +1 -1
  76. package/lib/catchUpMonitor.js.map +1 -1
  77. package/lib/connectionManager.d.ts +3 -2
  78. package/lib/connectionManager.d.ts.map +1 -1
  79. package/lib/connectionManager.js +33 -14
  80. package/lib/connectionManager.js.map +1 -1
  81. package/lib/connectionStateHandler.d.ts +18 -3
  82. package/lib/connectionStateHandler.d.ts.map +1 -1
  83. package/lib/connectionStateHandler.js +35 -10
  84. package/lib/connectionStateHandler.js.map +1 -1
  85. package/lib/container.d.ts +99 -70
  86. package/lib/container.d.ts.map +1 -1
  87. package/lib/container.js +264 -222
  88. package/lib/container.js.map +1 -1
  89. package/lib/containerContext.d.ts +24 -67
  90. package/lib/containerContext.d.ts.map +1 -1
  91. package/lib/containerContext.js +28 -217
  92. package/lib/containerContext.js.map +1 -1
  93. package/lib/containerStorageAdapter.d.ts +3 -3
  94. package/lib/containerStorageAdapter.d.ts.map +1 -1
  95. package/lib/containerStorageAdapter.js +29 -6
  96. package/lib/containerStorageAdapter.js.map +1 -1
  97. package/lib/contracts.d.ts +9 -3
  98. package/lib/contracts.d.ts.map +1 -1
  99. package/lib/contracts.js.map +1 -1
  100. package/lib/deltaManager.d.ts +22 -9
  101. package/lib/deltaManager.d.ts.map +1 -1
  102. package/lib/deltaManager.js +44 -33
  103. package/lib/deltaManager.js.map +1 -1
  104. package/lib/deltaQueue.d.ts +2 -3
  105. package/lib/deltaQueue.d.ts.map +1 -1
  106. package/lib/deltaQueue.js +2 -3
  107. package/lib/deltaQueue.js.map +1 -1
  108. package/lib/disposal.d.ts +13 -0
  109. package/lib/disposal.d.ts.map +1 -0
  110. package/lib/disposal.js +21 -0
  111. package/lib/disposal.js.map +1 -0
  112. package/lib/index.d.ts +1 -2
  113. package/lib/index.d.ts.map +1 -1
  114. package/lib/index.js +1 -1
  115. package/lib/index.js.map +1 -1
  116. package/lib/loader.d.ts +9 -8
  117. package/lib/loader.d.ts.map +1 -1
  118. package/lib/loader.js +47 -61
  119. package/lib/loader.js.map +1 -1
  120. package/lib/noopHeuristic.d.ts +23 -0
  121. package/lib/noopHeuristic.d.ts.map +1 -0
  122. package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  123. package/lib/noopHeuristic.js.map +1 -0
  124. package/lib/packageVersion.d.ts +1 -1
  125. package/lib/packageVersion.js +1 -1
  126. package/lib/packageVersion.js.map +1 -1
  127. package/lib/protocol.d.ts +7 -12
  128. package/lib/protocol.d.ts.map +1 -1
  129. package/lib/protocol.js +15 -18
  130. package/lib/protocol.js.map +1 -1
  131. package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
  132. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  133. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  134. package/lib/quorum.d.ts +1 -17
  135. package/lib/quorum.d.ts.map +1 -1
  136. package/lib/quorum.js +1 -16
  137. package/lib/quorum.js.map +1 -1
  138. package/lib/retriableDocumentStorageService.d.ts +3 -2
  139. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  140. package/lib/retriableDocumentStorageService.js.map +1 -1
  141. package/lib/utils.d.ts +2 -0
  142. package/lib/utils.d.ts.map +1 -1
  143. package/lib/utils.js +7 -1
  144. package/lib/utils.js.map +1 -1
  145. package/package.json +22 -20
  146. package/src/catchUpMonitor.ts +1 -1
  147. package/src/connectionManager.ts +40 -22
  148. package/src/connectionStateHandler.ts +66 -17
  149. package/src/container.ts +464 -292
  150. package/src/containerContext.ts +33 -341
  151. package/src/containerStorageAdapter.ts +40 -10
  152. package/src/contracts.ts +11 -3
  153. package/src/deltaManager.ts +74 -45
  154. package/src/deltaQueue.ts +2 -3
  155. package/src/disposal.ts +25 -0
  156. package/src/index.ts +1 -8
  157. package/src/loader.ts +85 -83
  158. package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
  159. package/src/packageVersion.ts +1 -1
  160. package/src/protocol.ts +18 -39
  161. package/src/protocolTreeDocumentStorageService.ts +1 -1
  162. package/src/quorum.ts +2 -31
  163. package/src/retriableDocumentStorageService.ts +4 -2
  164. package/src/utils.ts +15 -1
  165. package/dist/collabWindowTracker.d.ts +0 -19
  166. package/dist/collabWindowTracker.d.ts.map +0 -1
  167. package/dist/collabWindowTracker.js.map +0 -1
  168. package/dist/deltaManagerProxy.d.ts +0 -42
  169. package/dist/deltaManagerProxy.d.ts.map +0 -1
  170. package/dist/deltaManagerProxy.js +0 -79
  171. package/dist/deltaManagerProxy.js.map +0 -1
  172. package/lib/collabWindowTracker.d.ts +0 -19
  173. package/lib/collabWindowTracker.d.ts.map +0 -1
  174. package/lib/collabWindowTracker.js.map +0 -1
  175. package/lib/deltaManagerProxy.d.ts +0 -42
  176. package/lib/deltaManagerProxy.d.ts.map +0 -1
  177. package/lib/deltaManagerProxy.js +0 -74
  178. package/lib/deltaManagerProxy.js.map +0 -1
  179. package/src/deltaManagerProxy.ts +0 -109
@@ -5,12 +5,8 @@
5
5
 
6
6
  import { default as AbortController } from "abort-controller";
7
7
  import { v4 as uuid } from "uuid";
8
- import {
9
- ITelemetryLogger,
10
- IEventProvider,
11
- ITelemetryProperties,
12
- ITelemetryErrorEvent,
13
- } from "@fluidframework/common-definitions";
8
+ import { IEventProvider } from "@fluidframework/common-definitions";
9
+ import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
14
10
  import {
15
11
  IDeltaHandlerStrategy,
16
12
  IDeltaManager,
@@ -21,7 +17,13 @@ import {
21
17
  IConnectionDetailsInternal,
22
18
  } from "@fluidframework/container-definitions";
23
19
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
24
- import { normalizeError, logIfFalse, safeRaiseEvent } from "@fluidframework/telemetry-utils";
20
+ import {
21
+ normalizeError,
22
+ logIfFalse,
23
+ safeRaiseEvent,
24
+ isFluidError,
25
+ ITelemetryLoggerExt,
26
+ } from "@fluidframework/telemetry-utils";
25
27
  import {
26
28
  IDocumentDeltaStorageService,
27
29
  IDocumentService,
@@ -41,6 +43,7 @@ import {
41
43
  DataCorruptionError,
42
44
  extractSafePropertiesFromMessage,
43
45
  DataProcessingError,
46
+ UsageError,
44
47
  } from "@fluidframework/container-utils";
45
48
  import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
46
49
  import { DeltaQueue } from "./deltaQueue";
@@ -60,6 +63,15 @@ export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
60
63
  (event: "throttled", listener: (error: IThrottlingWarning) => void);
61
64
  (event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
62
65
  (event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
66
+ (event: "establishingConnection", listener: (reason: string) => void);
67
+ (event: "cancelEstablishingConnection", listener: (reason: string) => void);
68
+ }
69
+
70
+ /**
71
+ * Batching makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.
72
+ */
73
+ interface IBatchMetadata {
74
+ batch?: boolean;
63
75
  }
64
76
 
65
77
  /**
@@ -285,13 +297,16 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
285
297
 
286
298
  if (batch.length === 1) {
287
299
  assert(
288
- batch[0].metadata?.batch === undefined,
300
+ (batch[0].metadata as IBatchMetadata)?.batch === undefined,
289
301
  0x3c9 /* no batch markup on single message */,
290
302
  );
291
303
  } else {
292
- assert(batch[0].metadata?.batch === true, 0x3ca /* no start batch markup */);
293
304
  assert(
294
- batch[batch.length - 1].metadata?.batch === false,
305
+ (batch[0].metadata as IBatchMetadata)?.batch === true,
306
+ 0x3ca /* no start batch markup */,
307
+ );
308
+ assert(
309
+ (batch[batch.length - 1].metadata as IBatchMetadata)?.batch === false,
295
310
  0x3cb /* no end batch markup */,
296
311
  );
297
312
  }
@@ -340,7 +355,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
340
355
 
341
356
  constructor(
342
357
  private readonly serviceProvider: () => IDocumentService | undefined,
343
- private readonly logger: ITelemetryLogger,
358
+ private readonly logger: ITelemetryLoggerExt,
344
359
  private readonly _active: () => boolean,
345
360
  createConnectionManager: (props: IConnectionManagerFactoryArgs) => TConnectionManager,
346
361
  ) {
@@ -365,6 +380,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
365
380
  pongHandler: (latency: number) => this.emit("pong", latency),
366
381
  readonlyChangeHandler: (readonly?: boolean) =>
367
382
  safeRaiseEvent(this, this.logger, "readonly", readonly),
383
+ establishConnectionHandler: (reason: string) => this.establishingConnection(reason),
384
+ cancelConnectionHandler: (reason: string) => this.cancelEstablishingConnection(reason),
368
385
  };
369
386
 
370
387
  this.connectionManager = createConnectionManager(props);
@@ -404,6 +421,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
404
421
  // - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
405
422
  }
406
423
 
424
+ private cancelEstablishingConnection(reason: string) {
425
+ this.emit("cancelEstablishingConnection", reason);
426
+ }
427
+
428
+ private establishingConnection(reason: string) {
429
+ this.emit("establishingConnection", reason);
430
+ }
431
+
407
432
  private connectHandler(connection: IConnectionDetailsInternal) {
408
433
  this.refreshDelayInfo(this.deltaStreamDelayId);
409
434
 
@@ -453,10 +478,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
453
478
  }
454
479
  }
455
480
 
456
- public dispose() {
457
- throw new Error("Not implemented.");
458
- }
459
-
460
481
  /**
461
482
  * Sets the sequence number from which inbound messages should be returned
462
483
  */
@@ -644,23 +665,51 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
644
665
  /**
645
666
  * Closes the connection and clears inbound & outbound queues.
646
667
  *
647
- * @param doDispose - should the DeltaManager treat this close call as a dispose?
648
- * Differences between close and dispose:
649
- * - dispose will emit "disposed" event while close emits "closed"
650
- * - dispose will remove all listeners
651
- * - dispose can be called after closure, but not vis versa
668
+ * Differences from dispose:
669
+ * - close will trigger readonly notification
670
+ * - close emits "closed"
671
+ * - close cannot be called after dispose
652
672
  */
653
- public close(error?: ICriticalContainerError, doDispose?: boolean): void {
673
+ public close(error?: ICriticalContainerError): void {
654
674
  if (this._closed) {
655
- if (doDispose === true) {
656
- this.disposeInternal(error);
657
- }
658
675
  return;
659
676
  }
660
677
  this._closed = true;
661
678
 
662
- this.connectionManager.dispose(error, doDispose !== true);
679
+ this.connectionManager.dispose(error, true /* switchToReadonly */);
680
+ this.clearQueues();
681
+ this.emit("closed", error);
682
+ }
663
683
 
684
+ /**
685
+ * Disposes the connection and clears the inbound & outbound queues.
686
+ *
687
+ * Differences from close:
688
+ * - dispose will emit "disposed"
689
+ * - dispose will remove all listeners
690
+ * - dispose can be called after closure
691
+ */
692
+ public dispose(error?: Error | ICriticalContainerError): void {
693
+ if (this._disposed) {
694
+ return;
695
+ }
696
+ if (error !== undefined && !isFluidError(error)) {
697
+ throw new UsageError("Error must be a Fluid error");
698
+ }
699
+
700
+ this._disposed = true;
701
+ this._closed = true; // We consider "disposed" as a further state than "closed"
702
+
703
+ this.connectionManager.dispose(error, false /* switchToReadonly */);
704
+ this.clearQueues();
705
+
706
+ // This needs to be the last thing we do (before removing listeners), as it causes
707
+ // Container to dispose context and break ability of data stores / runtime to "hear" from delta manager.
708
+ this.emit("disposed", error);
709
+ this.removeAllListeners();
710
+ }
711
+
712
+ private clearQueues() {
664
713
  this.closeAbortController.abort();
665
714
 
666
715
  this._inbound.clear();
@@ -673,26 +722,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
673
722
 
674
723
  // Drop pending messages - this will ensure catchUp() does not go into infinite loop
675
724
  this.pending = [];
676
-
677
- if (doDispose === true) {
678
- this.disposeInternal(error);
679
- } else {
680
- this.emit("closed", error);
681
- this.disposeInternal(error); // ! TODO: remove this call when Container close no longer disposes
682
- }
683
- }
684
-
685
- private disposeInternal(error?: ICriticalContainerError): void {
686
- if (this._disposed) {
687
- return;
688
- }
689
- this._disposed = true;
690
-
691
- // This needs to be the last thing we do (before removing listeners), as it causes
692
- // Container to dispose context and break ability of data stores / runtime to "hear"
693
- // from delta manager, including notification (above) about readonly state.
694
- this.emit("disposed", error);
695
- this.removeAllListeners();
696
725
  }
697
726
 
698
727
  public refreshDelayInfo(id: string) {
package/src/deltaQueue.ts CHANGED
@@ -20,8 +20,8 @@ export class DeltaQueue<T>
20
20
  private readonly q = new Deque<T>();
21
21
 
22
22
  /**
23
- * Tracks the number of pause requests for the queue
24
- * The DeltaQueue is create initially paused.
23
+ * Tracks the number of pause requests for the queue.
24
+ * The DeltaQueue is created initially paused.
25
25
  */
26
26
  private pauseCount = 1;
27
27
 
@@ -58,7 +58,6 @@ export class DeltaQueue<T>
58
58
 
59
59
  /**
60
60
  * @param worker - A callback to process a delta.
61
- * @param logger - For logging telemetry.
62
61
  */
63
62
  constructor(private readonly worker: (delta: T) => void) {
64
63
  super();
@@ -0,0 +1,25 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IDisposable } from "@fluidframework/common-definitions";
7
+
8
+ /**
9
+ * Returns a wrapper around the provided function, which will only invoke the inner function if the provided
10
+ * {@link @fluidframework/common-definitions#IDisposable | disposable} object has not yet been disposed.
11
+ *
12
+ * @throws Will throw an error if the item has already been disposed.
13
+ */
14
+ export function doIfNotDisposed<T>(
15
+ disposable: IDisposable,
16
+ f: (...args: any[]) => T,
17
+ ): (...args: any[]) => T {
18
+ return (...args: any[]): T => {
19
+ if (disposable.disposed) {
20
+ throw new Error("Already disposed");
21
+ } else {
22
+ return f(...args);
23
+ }
24
+ };
25
+ }
package/src/index.ts CHANGED
@@ -4,14 +4,7 @@
4
4
  */
5
5
 
6
6
  export { ConnectionState } from "./connectionState";
7
- export {
8
- IContainerConfig,
9
- IContainerExperimental,
10
- IContainerLoadOptions,
11
- IPendingContainerState,
12
- waitContainerToCatchUp,
13
- } from "./container";
14
- export { ISerializableBlobContents } from "./containerStorageAdapter";
7
+ export { IContainerExperimental, waitContainerToCatchUp } from "./container";
15
8
  export {
16
9
  ICodeDetailsLoader,
17
10
  IDetachedBlobStorage,
package/src/loader.ts CHANGED
@@ -4,8 +4,19 @@
4
4
  */
5
5
 
6
6
  import { v4 as uuid } from "uuid";
7
- import { ITelemetryBaseLogger, ITelemetryLogger } from "@fluidframework/common-definitions";
8
7
  import {
8
+ ITelemetryLoggerExt,
9
+ ChildLogger,
10
+ DebugLogger,
11
+ IConfigProviderBase,
12
+ loggerToMonitoringContext,
13
+ mixinMonitoringContext,
14
+ MonitoringContext,
15
+ PerformanceEvent,
16
+ sessionStorageConfigProvider,
17
+ } from "@fluidframework/telemetry-utils";
18
+ import {
19
+ ITelemetryBaseLogger,
9
20
  FluidObject,
10
21
  IFluidRouter,
11
22
  IRequest,
@@ -22,24 +33,13 @@ import {
22
33
  IProvideFluidCodeDetailsComparer,
23
34
  IFluidCodeDetails,
24
35
  } from "@fluidframework/container-definitions";
25
- import {
26
- ChildLogger,
27
- DebugLogger,
28
- IConfigProviderBase,
29
- loggerToMonitoringContext,
30
- mixinMonitoringContext,
31
- MonitoringContext,
32
- PerformanceEvent,
33
- sessionStorageConfigProvider,
34
- } from "@fluidframework/telemetry-utils";
35
36
  import {
36
37
  IDocumentServiceFactory,
37
38
  IDocumentStorageService,
38
- IFluidResolvedUrl,
39
+ IResolvedUrl,
39
40
  IUrlResolver,
40
41
  } from "@fluidframework/driver-definitions";
41
42
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
42
- import { ensureFluidResolvedUrl } from "@fluidframework/driver-utils";
43
43
  import { Container, IPendingContainerState } from "./container";
44
44
  import { IParsedUrl, parseUrl } from "./utils";
45
45
  import { pkgVersion } from "./packageVersion";
@@ -53,8 +53,15 @@ function canUseCache(request: IRequest): boolean {
53
53
  return request.headers[LoaderHeader.cache] !== false;
54
54
  }
55
55
 
56
+ function ensureResolvedUrlDefined(
57
+ resolved: IResolvedUrl | undefined,
58
+ ): asserts resolved is IResolvedUrl {
59
+ if (resolved === undefined) {
60
+ throw new Error(`Object is not a IResolveUrl.`);
61
+ }
62
+ }
56
63
  /**
57
- * @deprecated - In the next release RelativeLoader will no longer be exported. It is an internal class that should not be used directly.
64
+ * @internal
58
65
  */
59
66
  export class RelativeLoader implements ILoader {
60
67
  constructor(
@@ -71,15 +78,18 @@ export class RelativeLoader implements ILoader {
71
78
  if (canUseCache(request)) {
72
79
  return this.container;
73
80
  } else {
74
- const resolvedUrl = this.container.resolvedUrl;
75
- ensureFluidResolvedUrl(resolvedUrl);
76
- const container = await Container.load(this.loader as Loader, {
77
- canReconnect: request.headers?.[LoaderHeader.reconnect],
78
- clientDetailsOverride: request.headers?.[LoaderHeader.clientDetails],
79
- resolvedUrl: { ...resolvedUrl },
80
- version: request.headers?.[LoaderHeader.version] ?? undefined,
81
- loadMode: request.headers?.[LoaderHeader.loadMode],
82
- });
81
+ ensureResolvedUrlDefined(this.container.resolvedUrl);
82
+ const container = await this.container.clone(
83
+ {
84
+ resolvedUrl: { ...this.container.resolvedUrl },
85
+ version: request.headers?.[LoaderHeader.version] ?? undefined,
86
+ loadMode: request.headers?.[LoaderHeader.loadMode],
87
+ },
88
+ {
89
+ canReconnect: request.headers?.[LoaderHeader.reconnect],
90
+ clientDetailsOverride: request.headers?.[LoaderHeader.clientDetails],
91
+ },
92
+ );
83
93
  return container;
84
94
  }
85
95
  }
@@ -237,12 +247,18 @@ export interface ILoaderServices {
237
247
  /**
238
248
  * The logger downstream consumers should construct their loggers from
239
249
  */
240
- readonly subLogger: ITelemetryLogger;
250
+ readonly subLogger: ITelemetryLoggerExt;
241
251
 
242
252
  /**
243
253
  * Blobs storage for detached containers.
244
254
  */
245
255
  readonly detachedBlobStorage?: IDetachedBlobStorage;
256
+
257
+ /**
258
+ * Optional property for allowing the container to use a custom
259
+ * protocol implementation for handling the quorum and/or the audience.
260
+ */
261
+ readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
246
262
  }
247
263
 
248
264
  /**
@@ -266,7 +282,7 @@ export async function requestResolvedObjectFromContainer(
266
282
  container: IContainer,
267
283
  headers?: IRequestHeader,
268
284
  ): Promise<IResponse> {
269
- ensureFluidResolvedUrl(container.resolvedUrl);
285
+ ensureResolvedUrlDefined(container.resolvedUrl);
270
286
  const parsedUrl = parseUrl(container.resolvedUrl.url);
271
287
 
272
288
  if (parsedUrl === undefined) {
@@ -289,37 +305,45 @@ export class Loader implements IHostLoader {
289
305
  private readonly containers = new Map<string, Promise<Container>>();
290
306
  public readonly services: ILoaderServices;
291
307
  private readonly mc: MonitoringContext;
292
- private readonly protocolHandlerBuilder: ProtocolHandlerBuilder | undefined;
293
308
 
294
309
  constructor(loaderProps: ILoaderProps) {
295
- const scope: FluidObject<ILoader> = { ...loaderProps.scope };
296
- if (loaderProps.options?.provideScopeLoader !== false) {
297
- scope.ILoader = this;
298
- }
310
+ const {
311
+ urlResolver,
312
+ documentServiceFactory,
313
+ codeLoader,
314
+ options,
315
+ scope,
316
+ logger,
317
+ detachedBlobStorage,
318
+ configProvider,
319
+ protocolHandlerBuilder,
320
+ } = loaderProps;
321
+
299
322
  const telemetryProps = {
300
323
  loaderId: uuid(),
301
324
  loaderVersion: pkgVersion,
302
325
  };
303
326
 
304
327
  const subMc = mixinMonitoringContext(
305
- DebugLogger.mixinDebugLogger("fluid:telemetry", loaderProps.logger, {
328
+ DebugLogger.mixinDebugLogger("fluid:telemetry", logger, {
306
329
  all: telemetryProps,
307
330
  }),
308
331
  sessionStorageConfigProvider.value,
309
- loaderProps.configProvider,
332
+ configProvider,
310
333
  );
311
334
 
312
335
  this.services = {
313
- urlResolver: loaderProps.urlResolver,
314
- documentServiceFactory: loaderProps.documentServiceFactory,
315
- codeLoader: loaderProps.codeLoader,
316
- options: loaderProps.options ?? {},
317
- scope,
336
+ urlResolver,
337
+ documentServiceFactory,
338
+ codeLoader,
339
+ options: options ?? {},
340
+ scope:
341
+ options?.provideScopeLoader !== false ? { ...scope, ILoader: this } : { ...scope },
342
+ detachedBlobStorage,
343
+ protocolHandlerBuilder,
318
344
  subLogger: subMc.logger,
319
- detachedBlobStorage: loaderProps.detachedBlobStorage,
320
345
  };
321
346
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.services.subLogger, "Loader"));
322
- this.protocolHandlerBuilder = loaderProps.protocolHandlerBuilder;
323
347
  }
324
348
 
325
349
  public get IFluidRouter(): IFluidRouter {
@@ -327,15 +351,11 @@ export class Loader implements IHostLoader {
327
351
  }
328
352
 
329
353
  public async createDetachedContainer(codeDetails: IFluidCodeDetails): Promise<IContainer> {
330
- const container = await Container.createDetached(
331
- this,
332
- codeDetails,
333
- this.protocolHandlerBuilder,
334
- );
354
+ const container = await Container.createDetached(this.services, codeDetails);
335
355
 
336
356
  if (this.cachingEnabled) {
337
357
  container.once("attached", () => {
338
- ensureFluidResolvedUrl(container.resolvedUrl);
358
+ ensureResolvedUrlDefined(container.resolvedUrl);
339
359
  const parsedUrl = parseUrl(container.resolvedUrl.url);
340
360
  if (parsedUrl !== undefined) {
341
361
  this.addToContainerCache(parsedUrl.id, Promise.resolve(container));
@@ -347,7 +367,7 @@ export class Loader implements IHostLoader {
347
367
  }
348
368
 
349
369
  public async rehydrateDetachedContainerFromSnapshot(snapshot: string): Promise<IContainer> {
350
- return Container.rehydrateDetachedFromSnapshot(this, snapshot, this.protocolHandlerBuilder);
370
+ return Container.rehydrateDetachedFromSnapshot(this.services, snapshot);
351
371
  }
352
372
 
353
373
  public async resolve(request: IRequest, pendingLocalState?: string): Promise<IContainer> {
@@ -404,7 +424,7 @@ export class Loader implements IHostLoader {
404
424
  pendingLocalState?: IPendingContainerState,
405
425
  ): Promise<{ container: Container; parsed: IParsedUrl }> {
406
426
  const resolvedAsFluid = await this.services.urlResolver.resolve(request);
407
- ensureFluidResolvedUrl(resolvedAsFluid);
427
+ ensureResolvedUrlDefined(resolvedAsFluid);
408
428
 
409
429
  // Parse URL into data stores
410
430
  const parsed = parseUrl(resolvedAsFluid.url);
@@ -423,11 +443,18 @@ export class Loader implements IHostLoader {
423
443
  }
424
444
  }
425
445
 
426
- const { canCache, fromSequenceNumber } = this.parseHeader(parsed, request);
427
- const shouldCache = pendingLocalState !== undefined ? false : canCache;
446
+ request.headers ??= {};
447
+ // If set in both query string and headers, use query string. Also write the value from the query string into the header either way.
448
+ request.headers[LoaderHeader.version] =
449
+ parsed.version ?? request.headers[LoaderHeader.version];
450
+ const canCache =
451
+ this.cachingEnabled &&
452
+ request.headers[LoaderHeader.cache] !== false &&
453
+ pendingLocalState === undefined;
454
+ const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] ?? -1;
428
455
 
429
456
  let container: Container;
430
- if (shouldCache) {
457
+ if (canCache) {
431
458
  const key = this.getKeyForContainerCache(request, parsed);
432
459
  const maybeContainer = await this.containers.get(key);
433
460
  if (maybeContainer !== undefined) {
@@ -461,48 +488,23 @@ export class Loader implements IHostLoader {
461
488
  return this.services.options.cache !== false;
462
489
  }
463
490
 
464
- private canCacheForRequest(headers: IRequestHeader): boolean {
465
- return this.cachingEnabled && headers[LoaderHeader.cache] !== false;
466
- }
467
-
468
- private parseHeader(parsed: IParsedUrl, request: IRequest) {
469
- let fromSequenceNumber = -1;
470
-
471
- request.headers = request.headers ?? {};
472
-
473
- const headerSeqNum = request.headers[LoaderHeader.sequenceNumber];
474
- if (headerSeqNum !== undefined) {
475
- fromSequenceNumber = headerSeqNum;
476
- }
477
-
478
- // If set in both query string and headers, use query string
479
- request.headers[LoaderHeader.version] =
480
- parsed.version ?? request.headers[LoaderHeader.version];
481
-
482
- const canCache = this.canCacheForRequest(request.headers);
483
-
484
- return {
485
- canCache,
486
- fromSequenceNumber,
487
- };
488
- }
489
-
490
491
  private async loadContainer(
491
492
  request: IRequest,
492
- resolved: IFluidResolvedUrl,
493
+ resolvedUrl: IResolvedUrl,
493
494
  pendingLocalState?: IPendingContainerState,
494
495
  ): Promise<Container> {
495
496
  return Container.load(
496
- this,
497
497
  {
498
- canReconnect: request.headers?.[LoaderHeader.reconnect],
499
- clientDetailsOverride: request.headers?.[LoaderHeader.clientDetails],
500
- resolvedUrl: resolved,
498
+ resolvedUrl,
501
499
  version: request.headers?.[LoaderHeader.version] ?? undefined,
502
500
  loadMode: request.headers?.[LoaderHeader.loadMode],
501
+ pendingLocalState,
502
+ },
503
+ {
504
+ canReconnect: request.headers?.[LoaderHeader.reconnect],
505
+ clientDetailsOverride: request.headers?.[LoaderHeader.clientDetails],
506
+ ...this.services,
503
507
  },
504
- pendingLocalState,
505
- this.protocolHandlerBuilder,
506
508
  );
507
509
  }
508
510
  }