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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +27 -3
  3. package/dist/connectionManager.d.ts +3 -2
  4. package/dist/connectionManager.d.ts.map +1 -1
  5. package/dist/connectionManager.js +32 -13
  6. package/dist/connectionManager.js.map +1 -1
  7. package/dist/connectionStateHandler.d.ts +15 -3
  8. package/dist/connectionStateHandler.d.ts.map +1 -1
  9. package/dist/connectionStateHandler.js +24 -1
  10. package/dist/connectionStateHandler.js.map +1 -1
  11. package/dist/container.d.ts +74 -44
  12. package/dist/container.d.ts.map +1 -1
  13. package/dist/container.js +81 -111
  14. package/dist/container.js.map +1 -1
  15. package/dist/containerContext.d.ts +2 -2
  16. package/dist/containerContext.d.ts.map +1 -1
  17. package/dist/containerContext.js +3 -7
  18. package/dist/containerContext.js.map +1 -1
  19. package/dist/containerStorageAdapter.d.ts +3 -3
  20. package/dist/containerStorageAdapter.d.ts.map +1 -1
  21. package/dist/containerStorageAdapter.js +6 -15
  22. package/dist/containerStorageAdapter.js.map +1 -1
  23. package/dist/contracts.d.ts +8 -0
  24. package/dist/contracts.d.ts.map +1 -1
  25. package/dist/contracts.js.map +1 -1
  26. package/dist/deltaManager.d.ts +21 -9
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +42 -31
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.d.ts +2 -3
  31. package/dist/deltaQueue.d.ts.map +1 -1
  32. package/dist/deltaQueue.js +2 -3
  33. package/dist/deltaQueue.js.map +1 -1
  34. package/dist/index.d.ts +1 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/loader.d.ts +9 -7
  38. package/dist/loader.d.ts.map +1 -1
  39. package/dist/loader.js +47 -61
  40. package/dist/loader.js.map +1 -1
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.js +1 -1
  43. package/dist/packageVersion.js.map +1 -1
  44. package/dist/retriableDocumentStorageService.d.ts +3 -2
  45. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  46. package/dist/retriableDocumentStorageService.js.map +1 -1
  47. package/dist/tsdoc-metadata.json +11 -0
  48. package/dist/utils.d.ts +2 -0
  49. package/dist/utils.d.ts.map +1 -1
  50. package/dist/utils.js +8 -1
  51. package/dist/utils.js.map +1 -1
  52. package/lib/connectionManager.d.ts +3 -2
  53. package/lib/connectionManager.d.ts.map +1 -1
  54. package/lib/connectionManager.js +33 -14
  55. package/lib/connectionManager.js.map +1 -1
  56. package/lib/connectionStateHandler.d.ts +15 -3
  57. package/lib/connectionStateHandler.d.ts.map +1 -1
  58. package/lib/connectionStateHandler.js +25 -2
  59. package/lib/connectionStateHandler.js.map +1 -1
  60. package/lib/container.d.ts +74 -44
  61. package/lib/container.d.ts.map +1 -1
  62. package/lib/container.js +82 -112
  63. package/lib/container.js.map +1 -1
  64. package/lib/containerContext.d.ts +2 -2
  65. package/lib/containerContext.d.ts.map +1 -1
  66. package/lib/containerContext.js +3 -7
  67. package/lib/containerContext.js.map +1 -1
  68. package/lib/containerStorageAdapter.d.ts +3 -3
  69. package/lib/containerStorageAdapter.d.ts.map +1 -1
  70. package/lib/containerStorageAdapter.js +6 -15
  71. package/lib/containerStorageAdapter.js.map +1 -1
  72. package/lib/contracts.d.ts +8 -0
  73. package/lib/contracts.d.ts.map +1 -1
  74. package/lib/contracts.js.map +1 -1
  75. package/lib/deltaManager.d.ts +21 -9
  76. package/lib/deltaManager.d.ts.map +1 -1
  77. package/lib/deltaManager.js +44 -33
  78. package/lib/deltaManager.js.map +1 -1
  79. package/lib/deltaQueue.d.ts +2 -3
  80. package/lib/deltaQueue.d.ts.map +1 -1
  81. package/lib/deltaQueue.js +2 -3
  82. package/lib/deltaQueue.js.map +1 -1
  83. package/lib/index.d.ts +1 -2
  84. package/lib/index.d.ts.map +1 -1
  85. package/lib/index.js +1 -1
  86. package/lib/index.js.map +1 -1
  87. package/lib/loader.d.ts +9 -7
  88. package/lib/loader.d.ts.map +1 -1
  89. package/lib/loader.js +47 -61
  90. package/lib/loader.js.map +1 -1
  91. package/lib/packageVersion.d.ts +1 -1
  92. package/lib/packageVersion.js +1 -1
  93. package/lib/packageVersion.js.map +1 -1
  94. package/lib/retriableDocumentStorageService.d.ts +3 -2
  95. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  96. package/lib/retriableDocumentStorageService.js.map +1 -1
  97. package/lib/utils.d.ts +2 -0
  98. package/lib/utils.d.ts.map +1 -1
  99. package/lib/utils.js +7 -1
  100. package/lib/utils.js.map +1 -1
  101. package/package.json +16 -18
  102. package/src/connectionManager.ts +40 -22
  103. package/src/connectionStateHandler.ts +52 -8
  104. package/src/container.ts +191 -159
  105. package/src/containerContext.ts +3 -9
  106. package/src/containerStorageAdapter.ts +8 -20
  107. package/src/contracts.ts +10 -0
  108. package/src/deltaManager.ts +59 -37
  109. package/src/deltaQueue.ts +2 -3
  110. package/src/index.ts +1 -8
  111. package/src/loader.ts +85 -83
  112. package/src/packageVersion.ts +1 -1
  113. package/src/retriableDocumentStorageService.ts +3 -2
  114. package/src/utils.ts +15 -1
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
+ import { ITelemetryLoggerExt, PerformanceEvent } from "@fluidframework/telemetry-utils";
7
7
  import { assert, LazyPromise, TypedEventEmitter } from "@fluidframework/common-utils";
8
8
  import {
9
9
  IAudience,
@@ -25,7 +25,6 @@ import {
25
25
  } from "@fluidframework/container-definitions";
26
26
  import { IRequest, IResponse, FluidObject } from "@fluidframework/core-interfaces";
27
27
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
28
- import { isFluidResolvedUrl } from "@fluidframework/driver-utils";
29
28
  import {
30
29
  IClientConfiguration,
31
30
  IClientDetails,
@@ -40,7 +39,6 @@ import {
40
39
  MessageType,
41
40
  ISummaryContent,
42
41
  } from "@fluidframework/protocol-definitions";
43
- import { PerformanceEvent } from "@fluidframework/telemetry-utils";
44
42
  import { UsageError } from "@fluidframework/container-utils";
45
43
  import { Container } from "./container";
46
44
 
@@ -101,7 +99,7 @@ export class ContainerContext implements IContainerContext {
101
99
  return context;
102
100
  }
103
101
 
104
- public readonly taggedLogger: ITelemetryLogger;
102
+ public readonly taggedLogger: ITelemetryLoggerExt;
105
103
  public readonly supportedFeatures: ReadonlyMap<string, unknown>;
106
104
 
107
105
  public get clientId(): string | undefined {
@@ -112,11 +110,7 @@ export class ContainerContext implements IContainerContext {
112
110
  * DISCLAIMER: this id is only for telemetry purposes. Not suitable for any other usages.
113
111
  */
114
112
  public get id(): string {
115
- const resolvedUrl = this.container.resolvedUrl;
116
- if (isFluidResolvedUrl(resolvedUrl)) {
117
- return resolvedUrl.id;
118
- }
119
- return "";
113
+ return this.container.resolvedUrl?.id ?? "";
120
114
  }
121
115
 
122
116
  public get clientDetails(): IClientDetails {
@@ -3,7 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
6
+ import { IDisposable } from "@fluidframework/common-definitions";
7
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
7
8
  import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
8
9
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
9
10
  import {
@@ -27,7 +28,6 @@ import { RetriableDocumentStorageService } from "./retriableDocumentStorageServi
27
28
 
28
29
  /**
29
30
  * Stringified blobs from a summary/snapshot tree.
30
- * @deprecated this is an internal interface and will not longer be exported in future versions
31
31
  * @internal
32
32
  */
33
33
  export interface ISerializableBlobContents {
@@ -60,7 +60,7 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
60
60
  */
61
61
  public constructor(
62
62
  detachedBlobStorage: IDetachedBlobStorage | undefined,
63
- private readonly logger: ITelemetryLogger,
63
+ private readonly logger: ITelemetryLoggerExt,
64
64
  /**
65
65
  * ArrayBufferLikes or utf8 encoded strings, containing blobs from a snapshot
66
66
  */
@@ -183,7 +183,7 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
183
183
  class BlobOnlyStorage implements IDocumentStorageService {
184
184
  constructor(
185
185
  private readonly detachedStorage: IDetachedBlobStorage | undefined,
186
- private readonly logger: ITelemetryLogger,
186
+ private readonly logger: ITelemetryLoggerExt,
187
187
  ) {}
188
188
 
189
189
  public async createBlob(content: ArrayBufferLike): Promise<ICreateBlobResponse> {
@@ -229,12 +229,6 @@ class BlobOnlyStorage implements IDocumentStorageService {
229
229
  }
230
230
  }
231
231
 
232
- // runtime will write a tree to the summary containing only "attachment" type entries
233
- // which reference attachment blobs by ID. However, some drivers do not support this type
234
- // and will convert them to "blob" type entries. We want to avoid saving these to reduce
235
- // the size of stashed change blobs.
236
- const blobsTreeName = ".blobs";
237
-
238
232
  /**
239
233
  * Get blob contents of a snapshot tree from storage (or, ideally, cache)
240
234
  */
@@ -251,13 +245,10 @@ async function getBlobContentsFromTreeCore(
251
245
  tree: ISnapshotTree,
252
246
  blobs: ISerializableBlobContents,
253
247
  storage: IDocumentStorageService,
254
- root = true,
255
248
  ) {
256
249
  const treePs: Promise<any>[] = [];
257
- for (const [key, subTree] of Object.entries(tree.trees)) {
258
- if (!root || key !== blobsTreeName) {
259
- treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage, false));
260
- }
250
+ for (const subTree of Object.values(tree.trees)) {
251
+ treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage));
261
252
  }
262
253
  for (const id of Object.values(tree.blobs)) {
263
254
  const blob = await storage.readBlob(id);
@@ -281,12 +272,9 @@ export function getBlobContentsFromTreeWithBlobContents(
281
272
  function getBlobContentsFromTreeWithBlobContentsCore(
282
273
  tree: ISnapshotTreeWithBlobContents,
283
274
  blobs: ISerializableBlobContents,
284
- root = true,
285
275
  ) {
286
- for (const [key, subTree] of Object.entries(tree.trees)) {
287
- if (!root || key !== blobsTreeName) {
288
- getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
289
- }
276
+ for (const subTree of Object.values(tree.trees)) {
277
+ getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs);
290
278
  }
291
279
  for (const id of Object.values(tree.blobs)) {
292
280
  const blob = tree.blobsContents[id];
package/src/contracts.ts CHANGED
@@ -167,6 +167,16 @@ export interface IConnectionManagerFactoryArgs {
167
167
  * `undefined` indicates that user permissions are not yet known.
168
168
  */
169
169
  readonly readonlyChangeHandler: (readonly?: boolean) => void;
170
+
171
+ /**
172
+ * Called whenever we try to start establishing a new connection.
173
+ */
174
+ readonly establishConnectionHandler: (reason: string) => void;
175
+
176
+ /**
177
+ * Called whenever we cancel the connection in progress.
178
+ */
179
+ readonly cancelConnectionHandler: (reason: string) => void;
170
180
  }
171
181
 
172
182
  /**
@@ -6,7 +6,6 @@
6
6
  import { default as AbortController } from "abort-controller";
7
7
  import { v4 as uuid } from "uuid";
8
8
  import {
9
- ITelemetryLogger,
10
9
  IEventProvider,
11
10
  ITelemetryProperties,
12
11
  ITelemetryErrorEvent,
@@ -21,7 +20,13 @@ import {
21
20
  IConnectionDetailsInternal,
22
21
  } from "@fluidframework/container-definitions";
23
22
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
24
- import { normalizeError, logIfFalse, safeRaiseEvent } from "@fluidframework/telemetry-utils";
23
+ import {
24
+ normalizeError,
25
+ logIfFalse,
26
+ safeRaiseEvent,
27
+ isFluidError,
28
+ ITelemetryLoggerExt,
29
+ } from "@fluidframework/telemetry-utils";
25
30
  import {
26
31
  IDocumentDeltaStorageService,
27
32
  IDocumentService,
@@ -41,6 +46,7 @@ import {
41
46
  DataCorruptionError,
42
47
  extractSafePropertiesFromMessage,
43
48
  DataProcessingError,
49
+ UsageError,
44
50
  } from "@fluidframework/container-utils";
45
51
  import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
46
52
  import { DeltaQueue } from "./deltaQueue";
@@ -60,6 +66,8 @@ export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
60
66
  (event: "throttled", listener: (error: IThrottlingWarning) => void);
61
67
  (event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
62
68
  (event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
69
+ (event: "establishingConnection", listener: (reason: string) => void);
70
+ (event: "cancelEstablishingConnection", listener: (reason: string) => void);
63
71
  }
64
72
 
65
73
  /**
@@ -340,7 +348,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
340
348
 
341
349
  constructor(
342
350
  private readonly serviceProvider: () => IDocumentService | undefined,
343
- private readonly logger: ITelemetryLogger,
351
+ private readonly logger: ITelemetryLoggerExt,
344
352
  private readonly _active: () => boolean,
345
353
  createConnectionManager: (props: IConnectionManagerFactoryArgs) => TConnectionManager,
346
354
  ) {
@@ -365,6 +373,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
365
373
  pongHandler: (latency: number) => this.emit("pong", latency),
366
374
  readonlyChangeHandler: (readonly?: boolean) =>
367
375
  safeRaiseEvent(this, this.logger, "readonly", readonly),
376
+ establishConnectionHandler: (reason: string) => this.establishingConnection(reason),
377
+ cancelConnectionHandler: (reason: string) => this.cancelEstablishingConnection(reason),
368
378
  };
369
379
 
370
380
  this.connectionManager = createConnectionManager(props);
@@ -404,6 +414,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
404
414
  // - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
405
415
  }
406
416
 
417
+ private cancelEstablishingConnection(reason: string) {
418
+ this.emit("cancelEstablishingConnection", reason);
419
+ }
420
+
421
+ private establishingConnection(reason: string) {
422
+ this.emit("establishingConnection", reason);
423
+ }
424
+
407
425
  private connectHandler(connection: IConnectionDetailsInternal) {
408
426
  this.refreshDelayInfo(this.deltaStreamDelayId);
409
427
 
@@ -453,10 +471,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
453
471
  }
454
472
  }
455
473
 
456
- public dispose() {
457
- throw new Error("Not implemented.");
458
- }
459
-
460
474
  /**
461
475
  * Sets the sequence number from which inbound messages should be returned
462
476
  */
@@ -644,23 +658,51 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
644
658
  /**
645
659
  * Closes the connection and clears inbound & outbound queues.
646
660
  *
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
661
+ * Differences from dispose:
662
+ * - close will trigger readonly notification
663
+ * - close emits "closed"
664
+ * - close cannot be called after dispose
652
665
  */
653
- public close(error?: ICriticalContainerError, doDispose?: boolean): void {
666
+ public close(error?: ICriticalContainerError): void {
654
667
  if (this._closed) {
655
- if (doDispose === true) {
656
- this.disposeInternal(error);
657
- }
658
668
  return;
659
669
  }
660
670
  this._closed = true;
661
671
 
662
- this.connectionManager.dispose(error, doDispose !== true);
672
+ this.connectionManager.dispose(error, true /* switchToReadonly */);
673
+ this.clearQueues();
674
+ this.emit("closed", error);
675
+ }
676
+
677
+ /**
678
+ * Disposes the connection and clears the inbound & outbound queues.
679
+ *
680
+ * Differences from close:
681
+ * - dispose will emit "disposed"
682
+ * - dispose will remove all listeners
683
+ * - dispose can be called after closure
684
+ */
685
+ public dispose(error?: Error | ICriticalContainerError): void {
686
+ if (this._disposed) {
687
+ return;
688
+ }
689
+ if (error !== undefined && !isFluidError(error)) {
690
+ throw new UsageError("Error must be a Fluid error");
691
+ }
692
+
693
+ this._disposed = true;
694
+ this._closed = true; // We consider "disposed" as a further state than "closed"
695
+
696
+ this.connectionManager.dispose(error, false /* switchToReadonly */);
697
+ this.clearQueues();
698
+
699
+ // This needs to be the last thing we do (before removing listeners), as it causes
700
+ // Container to dispose context and break ability of data stores / runtime to "hear" from delta manager.
701
+ this.emit("disposed", error);
702
+ this.removeAllListeners();
703
+ }
663
704
 
705
+ private clearQueues() {
664
706
  this.closeAbortController.abort();
665
707
 
666
708
  this._inbound.clear();
@@ -673,26 +715,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
673
715
 
674
716
  // Drop pending messages - this will ensure catchUp() does not go into infinite loop
675
717
  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
718
  }
697
719
 
698
720
  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();
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,7 +4,18 @@
4
4
  */
5
5
 
6
6
  import { v4 as uuid } from "uuid";
7
- import { ITelemetryBaseLogger, ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
8
+ import {
9
+ ITelemetryLoggerExt,
10
+ ChildLogger,
11
+ DebugLogger,
12
+ IConfigProviderBase,
13
+ loggerToMonitoringContext,
14
+ mixinMonitoringContext,
15
+ MonitoringContext,
16
+ PerformanceEvent,
17
+ sessionStorageConfigProvider,
18
+ } from "@fluidframework/telemetry-utils";
8
19
  import {
9
20
  FluidObject,
10
21
  IFluidRouter,
@@ -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
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-dev.4.4.0.162253";
9
+ export const pkgVersion = "2.0.0-dev.5.2.0.169897";
@@ -18,14 +18,15 @@ import {
18
18
  ISummaryTree,
19
19
  IVersion,
20
20
  } from "@fluidframework/protocol-definitions";
21
- import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
21
+ import { IDisposable } from "@fluidframework/common-definitions";
22
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
22
23
  import { runWithRetry } from "@fluidframework/driver-utils";
23
24
 
24
25
  export class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {
25
26
  private _disposed = false;
26
27
  constructor(
27
28
  private readonly internalStorageService: IDocumentStorageService,
28
- private readonly logger: ITelemetryLogger,
29
+ private readonly logger: ITelemetryLoggerExt,
29
30
  ) {}
30
31
 
31
32
  public get policies(): IDocumentStorageServicePolicies | undefined {