@fluidframework/container-loader 2.0.0-internal.5.3.2 → 2.0.0-internal.6.0.0

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 (124) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +6 -3
  3. package/dist/audience.d.ts +1 -0
  4. package/dist/audience.d.ts.map +1 -1
  5. package/dist/audience.js +3 -1
  6. package/dist/audience.js.map +1 -1
  7. package/dist/connectionManager.d.ts +1 -1
  8. package/dist/connectionManager.d.ts.map +1 -1
  9. package/dist/connectionManager.js +30 -36
  10. package/dist/connectionManager.js.map +1 -1
  11. package/dist/connectionStateHandler.d.ts +2 -1
  12. package/dist/connectionStateHandler.d.ts.map +1 -1
  13. package/dist/connectionStateHandler.js +9 -16
  14. package/dist/connectionStateHandler.js.map +1 -1
  15. package/dist/container.d.ts +12 -8
  16. package/dist/container.d.ts.map +1 -1
  17. package/dist/container.js +199 -156
  18. package/dist/container.js.map +1 -1
  19. package/dist/containerContext.d.ts +2 -12
  20. package/dist/containerContext.d.ts.map +1 -1
  21. package/dist/containerContext.js +1 -20
  22. package/dist/containerContext.js.map +1 -1
  23. package/dist/containerStorageAdapter.js +3 -5
  24. package/dist/containerStorageAdapter.js.map +1 -1
  25. package/dist/contracts.d.ts +11 -2
  26. package/dist/contracts.d.ts.map +1 -1
  27. package/dist/contracts.js +3 -3
  28. package/dist/contracts.js.map +1 -1
  29. package/dist/debugLogger.d.ts +30 -0
  30. package/dist/debugLogger.d.ts.map +1 -0
  31. package/dist/debugLogger.js +95 -0
  32. package/dist/debugLogger.js.map +1 -0
  33. package/dist/deltaManager.d.ts +16 -4
  34. package/dist/deltaManager.d.ts.map +1 -1
  35. package/dist/deltaManager.js +79 -33
  36. package/dist/deltaManager.js.map +1 -1
  37. package/dist/deltaQueue.js +1 -2
  38. package/dist/deltaQueue.js.map +1 -1
  39. package/dist/loader.d.ts +12 -0
  40. package/dist/loader.d.ts.map +1 -1
  41. package/dist/loader.js +73 -47
  42. package/dist/loader.js.map +1 -1
  43. package/dist/packageVersion.d.ts +1 -1
  44. package/dist/packageVersion.js +1 -1
  45. package/dist/packageVersion.js.map +1 -1
  46. package/dist/protocol.d.ts.map +1 -1
  47. package/dist/protocol.js +2 -3
  48. package/dist/protocol.js.map +1 -1
  49. package/dist/quorum.d.ts +4 -1
  50. package/dist/quorum.d.ts.map +1 -1
  51. package/dist/quorum.js +1 -13
  52. package/dist/quorum.js.map +1 -1
  53. package/dist/utils.d.ts +8 -1
  54. package/dist/utils.d.ts.map +1 -1
  55. package/dist/utils.js +24 -6
  56. package/dist/utils.js.map +1 -1
  57. package/lib/audience.d.ts +1 -0
  58. package/lib/audience.d.ts.map +1 -1
  59. package/lib/audience.js +3 -1
  60. package/lib/audience.js.map +1 -1
  61. package/lib/connectionManager.d.ts +1 -1
  62. package/lib/connectionManager.d.ts.map +1 -1
  63. package/lib/connectionManager.js +32 -35
  64. package/lib/connectionManager.js.map +1 -1
  65. package/lib/connectionStateHandler.d.ts +2 -1
  66. package/lib/connectionStateHandler.d.ts.map +1 -1
  67. package/lib/connectionStateHandler.js +9 -16
  68. package/lib/connectionStateHandler.js.map +1 -1
  69. package/lib/container.d.ts +12 -8
  70. package/lib/container.d.ts.map +1 -1
  71. package/lib/container.js +200 -157
  72. package/lib/container.js.map +1 -1
  73. package/lib/containerContext.d.ts +2 -12
  74. package/lib/containerContext.d.ts.map +1 -1
  75. package/lib/containerContext.js +1 -20
  76. package/lib/containerContext.js.map +1 -1
  77. package/lib/containerStorageAdapter.js +3 -5
  78. package/lib/containerStorageAdapter.js.map +1 -1
  79. package/lib/contracts.d.ts +11 -2
  80. package/lib/contracts.d.ts.map +1 -1
  81. package/lib/contracts.js +3 -3
  82. package/lib/contracts.js.map +1 -1
  83. package/lib/debugLogger.d.ts +30 -0
  84. package/lib/debugLogger.d.ts.map +1 -0
  85. package/lib/debugLogger.js +91 -0
  86. package/lib/debugLogger.js.map +1 -0
  87. package/lib/deltaManager.d.ts +16 -4
  88. package/lib/deltaManager.d.ts.map +1 -1
  89. package/lib/deltaManager.js +77 -28
  90. package/lib/deltaManager.js.map +1 -1
  91. package/lib/deltaQueue.js +1 -2
  92. package/lib/deltaQueue.js.map +1 -1
  93. package/lib/loader.d.ts +12 -0
  94. package/lib/loader.d.ts.map +1 -1
  95. package/lib/loader.js +73 -47
  96. package/lib/loader.js.map +1 -1
  97. package/lib/packageVersion.d.ts +1 -1
  98. package/lib/packageVersion.js +1 -1
  99. package/lib/packageVersion.js.map +1 -1
  100. package/lib/protocol.d.ts.map +1 -1
  101. package/lib/protocol.js +2 -3
  102. package/lib/protocol.js.map +1 -1
  103. package/lib/quorum.d.ts +4 -1
  104. package/lib/quorum.d.ts.map +1 -1
  105. package/lib/quorum.js +0 -11
  106. package/lib/quorum.js.map +1 -1
  107. package/lib/utils.d.ts +8 -1
  108. package/lib/utils.d.ts.map +1 -1
  109. package/lib/utils.js +22 -5
  110. package/lib/utils.js.map +1 -1
  111. package/package.json +14 -14
  112. package/src/audience.ts +6 -0
  113. package/src/connectionManager.ts +13 -14
  114. package/src/connectionStateHandler.ts +3 -2
  115. package/src/container.ts +178 -120
  116. package/src/containerContext.ts +0 -24
  117. package/src/contracts.ts +16 -5
  118. package/src/debugLogger.ts +113 -0
  119. package/src/deltaManager.ts +50 -9
  120. package/src/loader.ts +53 -30
  121. package/src/packageVersion.ts +1 -1
  122. package/src/protocol.ts +0 -1
  123. package/src/quorum.ts +0 -10
  124. package/src/utils.ts +29 -0
package/src/contracts.ts CHANGED
@@ -5,19 +5,20 @@
5
5
 
6
6
  import { ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import {
8
- IDeltaQueue,
9
- ReadOnlyInfo,
10
- IConnectionDetailsInternal,
8
+ IConnectionDetails,
11
9
  ICriticalContainerError,
10
+ IDeltaQueue,
12
11
  IFluidCodeDetails,
13
12
  isFluidPackage,
13
+ ReadOnlyInfo,
14
14
  } from "@fluidframework/container-definitions";
15
15
  import {
16
16
  ConnectionMode,
17
- IDocumentMessage,
18
- ISequencedDocumentMessage,
19
17
  IClientConfiguration,
20
18
  IClientDetails,
19
+ IDocumentMessage,
20
+ ISequencedDocumentMessage,
21
+ ISignalClient,
21
22
  ISignalMessage,
22
23
  } from "@fluidframework/protocol-definitions";
23
24
  import { IAnyDriverError, IContainerPackageInfo } from "@fluidframework/driver-definitions";
@@ -28,6 +29,16 @@ export enum ReconnectMode {
28
29
  Enabled = "Enabled",
29
30
  }
30
31
 
32
+ /**
33
+ * Internal version of IConnectionDetails with props are only exposed internally
34
+ */
35
+ export interface IConnectionDetailsInternal extends IConnectionDetails {
36
+ mode: ConnectionMode;
37
+ version: string;
38
+ initialClients: ISignalClient[];
39
+ reason: string;
40
+ }
41
+
31
42
  /**
32
43
  * Connection manager (implements this interface) is responsible for maintaining connection
33
44
  * to relay service.
@@ -0,0 +1,113 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ ITelemetryBaseEvent,
8
+ ITelemetryBaseLogger,
9
+ ITelemetryProperties,
10
+ } from "@fluidframework/core-interfaces";
11
+ import { performance } from "@fluidframework/common-utils";
12
+ import { debug as registerDebug, IDebugger } from "debug";
13
+ import {
14
+ ITelemetryLoggerExt,
15
+ ITelemetryLoggerPropertyBags,
16
+ createMultiSinkLogger,
17
+ eventNamespaceSeparator,
18
+ formatTick,
19
+ } from "@fluidframework/telemetry-utils";
20
+
21
+ /**
22
+ * Implementation of debug logger
23
+ */
24
+ export class DebugLogger implements ITelemetryBaseLogger {
25
+ /**
26
+ * Mix in debug logger with another logger.
27
+ * Returned logger will output events to both newly created debug logger, as well as base logger
28
+ * @param namespace - Telemetry event name prefix to add to all events
29
+ * @param properties - Base properties to add to all events
30
+ * @param propertyGetters - Getters to add additional properties to all events
31
+ * @param baseLogger - Base logger to output events (in addition to debug logger being created). Can be undefined.
32
+ */
33
+ public static mixinDebugLogger(
34
+ namespace: string,
35
+ baseLogger?: ITelemetryBaseLogger,
36
+ properties?: ITelemetryLoggerPropertyBags,
37
+ ): ITelemetryLoggerExt {
38
+ // Setup base logger upfront, such that host can disable it (if needed)
39
+ const debug = registerDebug(namespace);
40
+
41
+ // Create one for errors that is always enabled
42
+ // It can be silenced by replacing console.error if the debug namespace is not enabled.
43
+ const debugErr = registerDebug(namespace);
44
+ debugErr.log = function (...args) {
45
+ if (debug.enabled === true) {
46
+ // if the namespace is enabled, just use the default logger
47
+ registerDebug.log(...args);
48
+ } else {
49
+ // other wise, use the console logger (which could be replaced and silenced)
50
+ console.error(...args);
51
+ }
52
+ };
53
+ debugErr.enabled = true;
54
+
55
+ return createMultiSinkLogger({
56
+ namespace,
57
+ loggers: [baseLogger, new DebugLogger(debug, debugErr)],
58
+ properties,
59
+ tryInheritProperties: true,
60
+ });
61
+ }
62
+
63
+ private constructor(private readonly debug: IDebugger, private readonly debugErr: IDebugger) {}
64
+
65
+ /**
66
+ * Send an event to debug loggers
67
+ *
68
+ * @param event - the event to send
69
+ */
70
+ public send(event: ITelemetryBaseEvent): void {
71
+ const newEvent: ITelemetryProperties = { ...event };
72
+ const isError = newEvent.category === "error";
73
+ let logger = isError ? this.debugErr : this.debug;
74
+
75
+ // Use debug's coloring schema for base of the event
76
+ const index = event.eventName.lastIndexOf(eventNamespaceSeparator);
77
+ const name = event.eventName.substring(index + 1);
78
+ if (index > 0) {
79
+ logger = logger.extend(event.eventName.substring(0, index));
80
+ }
81
+ newEvent.eventName = undefined;
82
+
83
+ let tick = "";
84
+ tick = `tick=${formatTick(performance.now())}`;
85
+
86
+ // Extract stack to put it last, but also to avoid escaping '\n' in it by JSON.stringify below
87
+ const stack = newEvent.stack ?? "";
88
+ newEvent.stack = undefined;
89
+
90
+ // Watch out for circular references - they can come from two sources
91
+ // 1) error object - we do not control it and should remove it and retry
92
+ // 2) properties supplied by telemetry caller - that's a bug that should be addressed!
93
+ let payload: string;
94
+ try {
95
+ payload = JSON.stringify(newEvent);
96
+ } catch (error) {
97
+ newEvent.error = undefined;
98
+ payload = JSON.stringify(newEvent);
99
+ }
100
+
101
+ if (payload === "{}") {
102
+ payload = "";
103
+ }
104
+
105
+ // Force errors out, to help with diagnostics
106
+ if (isError) {
107
+ logger.enabled = true;
108
+ }
109
+
110
+ // Print multi-line.
111
+ logger(`${name} ${payload} ${tick} ${stack}`);
112
+ }
113
+ }
@@ -3,18 +3,15 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { default as AbortController } from "abort-controller";
7
6
  import { v4 as uuid } from "uuid";
8
7
  import { IEventProvider } from "@fluidframework/common-definitions";
9
8
  import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
10
9
  import {
11
- IDeltaHandlerStrategy,
10
+ ICriticalContainerError,
12
11
  IDeltaManager,
13
12
  IDeltaManagerEvents,
14
13
  IDeltaQueue,
15
- ICriticalContainerError,
16
14
  IThrottlingWarning,
17
- IConnectionDetailsInternal,
18
15
  } from "@fluidframework/container-definitions";
19
16
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
20
17
  import {
@@ -45,7 +42,11 @@ import {
45
42
  DataProcessingError,
46
43
  UsageError,
47
44
  } from "@fluidframework/container-utils";
48
- import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
45
+ import {
46
+ IConnectionDetailsInternal,
47
+ IConnectionManager,
48
+ IConnectionManagerFactoryArgs,
49
+ } from "./contracts";
49
50
  import { DeltaQueue } from "./deltaQueue";
50
51
  import { OnlyValidTermValue } from "./protocol";
51
52
 
@@ -74,6 +75,21 @@ interface IBatchMetadata {
74
75
  batch?: boolean;
75
76
  }
76
77
 
78
+ /**
79
+ * Interface used to define a strategy for handling incoming delta messages
80
+ */
81
+ export interface IDeltaHandlerStrategy {
82
+ /**
83
+ * Processes the message.
84
+ */
85
+ process: (message: ISequencedDocumentMessage) => void;
86
+
87
+ /**
88
+ * Processes the signal.
89
+ */
90
+ processSignal: (message: ISignalMessage) => void;
91
+ }
92
+
77
93
  /**
78
94
  * Determines if message was sent by client, not service
79
95
  */
@@ -93,6 +109,17 @@ function isClientMessage(message: ISequencedDocumentMessage | IDocumentMessage):
93
109
  }
94
110
  }
95
111
 
112
+ /**
113
+ * Type is used to cast AbortController to represent new version of DOM API and prevent build issues
114
+ * TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
115
+ */
116
+ type AbortControllerReal = AbortController & { abort(reason?: any): void };
117
+ /**
118
+ * Type is used to cast AbortSignal to represent new version of DOM API and prevent build issues
119
+ * TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
120
+ */
121
+ type AbortSignalReal = AbortSignal & { reason: any };
122
+
96
123
  /**
97
124
  * Manages the flow of both inbound and outbound messages. This class ensures that shared objects receive delta
98
125
  * messages in order regardless of possible network conditions or timings causing out of order delivery.
@@ -485,7 +512,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
485
512
  minSequenceNumber: number,
486
513
  sequenceNumber: number,
487
514
  handler: IDeltaHandlerStrategy,
488
- prefetchType: "cached" | "all" | "none" = "none",
515
+ prefetchType: "sequenceNumber" | "cached" | "all" | "none" = "none",
489
516
  ) {
490
517
  this.initSequenceNumber = sequenceNumber;
491
518
  this.lastProcessedSequenceNumber = sequenceNumber;
@@ -624,7 +651,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
624
651
  // This is useless for known ranges (to is defined) as it means request is over either way.
625
652
  // And it will cancel unbound request too early, not allowing us to learn where the end of the file is.
626
653
  if (!opsFromFetch && cancelFetch(op)) {
627
- controller.abort();
654
+ // TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
655
+ (controller as AbortControllerReal).abort("DeltaManager getDeltas fetch cancelled");
628
656
  this._inbound.off("push", opListener);
629
657
  }
630
658
  };
@@ -632,7 +660,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
632
660
  try {
633
661
  this._inbound.on("push", opListener);
634
662
  assert(this.closeAbortController.signal.onabort === null, 0x1e8 /* "reentrancy" */);
635
- this.closeAbortController.signal.onabort = () => controller.abort();
663
+ this.closeAbortController.signal.onabort = () =>
664
+ // TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
665
+ (controller as AbortControllerReal).abort(
666
+ (this.closeAbortController.signal as AbortSignalReal).reason,
667
+ );
636
668
 
637
669
  const stream = this.deltaStorage.fetchMessages(
638
670
  from, // inclusive
@@ -656,6 +688,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
656
688
  }
657
689
  }
658
690
  } finally {
691
+ if (controller.signal.aborted) {
692
+ this.logger.sendTelemetryEvent({
693
+ eventName: "DeltaManager_GetDeltasAborted",
694
+ fetchReason,
695
+ // TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
696
+ reason: (controller.signal as AbortSignalReal).reason,
697
+ });
698
+ }
659
699
  this.closeAbortController.signal.onabort = null;
660
700
  this._inbound.off("push", opListener);
661
701
  assert(!opsFromFetch, 0x289 /* "logic error" */);
@@ -710,7 +750,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
710
750
  }
711
751
 
712
752
  private clearQueues() {
713
- this.closeAbortController.abort();
753
+ // TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
754
+ (this.closeAbortController as AbortControllerReal).abort("DeltaManager is closed");
714
755
 
715
756
  this._inbound.clear();
716
757
  this._inboundSignal.clear();
package/src/loader.ts CHANGED
@@ -6,14 +6,12 @@
6
6
  import { v4 as uuid } from "uuid";
7
7
  import {
8
8
  ITelemetryLoggerExt,
9
- ChildLogger,
10
- DebugLogger,
11
9
  IConfigProviderBase,
12
- loggerToMonitoringContext,
13
10
  mixinMonitoringContext,
14
11
  MonitoringContext,
15
12
  PerformanceEvent,
16
13
  sessionStorageConfigProvider,
14
+ createChildMonitoringContext,
17
15
  } from "@fluidframework/telemetry-utils";
18
16
  import {
19
17
  ITelemetryBaseLogger,
@@ -39,18 +37,15 @@ import {
39
37
  IResolvedUrl,
40
38
  IUrlResolver,
41
39
  } from "@fluidframework/driver-definitions";
42
- import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
40
+ import { UsageError } from "@fluidframework/container-utils";
43
41
  import { Container, IPendingContainerState } from "./container";
44
42
  import { IParsedUrl, parseUrl } from "./utils";
45
43
  import { pkgVersion } from "./packageVersion";
46
44
  import { ProtocolHandlerBuilder } from "./protocol";
45
+ import { DebugLogger } from "./debugLogger";
47
46
 
48
47
  function canUseCache(request: IRequest): boolean {
49
- if (request.headers === undefined) {
50
- return true;
51
- }
52
-
53
- return request.headers[LoaderHeader.cache] !== false;
48
+ return request.headers?.[LoaderHeader.cache] === true;
54
49
  }
55
50
 
56
51
  function ensureResolvedUrlDefined(
@@ -69,6 +64,9 @@ export class RelativeLoader implements ILoader {
69
64
  private readonly loader: ILoader | undefined,
70
65
  ) {}
71
66
 
67
+ /**
68
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
69
+ */
72
70
  public get IFluidRouter(): IFluidRouter {
73
71
  return this;
74
72
  }
@@ -100,6 +98,9 @@ export class RelativeLoader implements ILoader {
100
98
  return this.loader.resolve(request);
101
99
  }
102
100
 
101
+ /**
102
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
103
+ */
103
104
  public async request(request: IRequest): Promise<IResponse> {
104
105
  if (request.url.startsWith("/")) {
105
106
  const container = await this.resolve(request);
@@ -343,9 +344,15 @@ export class Loader implements IHostLoader {
343
344
  protocolHandlerBuilder,
344
345
  subLogger: subMc.logger,
345
346
  };
346
- this.mc = loggerToMonitoringContext(ChildLogger.create(this.services.subLogger, "Loader"));
347
+ this.mc = createChildMonitoringContext({
348
+ logger: this.services.subLogger,
349
+ namespace: "Loader",
350
+ });
347
351
  }
348
352
 
353
+ /**
354
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
355
+ */
349
356
  public get IFluidRouter(): IFluidRouter {
350
357
  return this;
351
358
  }
@@ -381,6 +388,9 @@ export class Loader implements IHostLoader {
381
388
  });
382
389
  }
383
390
 
391
+ /**
392
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
393
+ */
384
394
  public async request(request: IRequest): Promise<IResponse> {
385
395
  return PerformanceEvent.timedExecAsync(
386
396
  this.mc.logger,
@@ -407,16 +417,23 @@ export class Loader implements IHostLoader {
407
417
  this.containers.set(key, containerP);
408
418
  containerP
409
419
  .then((container) => {
410
- // If the container is closed or becomes closed after we resolve it, remove it from the cache.
411
- if (container.closed) {
420
+ // If the container is closed/disposed or becomes closed/disposed after we resolve it,
421
+ // remove it from the cache.
422
+ if (container.closed || container.disposed) {
412
423
  this.containers.delete(key);
413
424
  } else {
414
425
  container.once("closed", () => {
415
426
  this.containers.delete(key);
416
427
  });
428
+ container.once("disposed", () => {
429
+ this.containers.delete(key);
430
+ });
417
431
  }
418
432
  })
419
- .catch((error) => {});
433
+ .catch((error) => {
434
+ // If an error occured while resolving the container request, then remove it from the cache.
435
+ this.containers.delete(key);
436
+ });
420
437
  }
421
438
 
422
439
  private async resolveCore(
@@ -447,11 +464,29 @@ export class Loader implements IHostLoader {
447
464
  // 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
465
  request.headers[LoaderHeader.version] =
449
466
  parsed.version ?? request.headers[LoaderHeader.version];
467
+ const cacheHeader = request.headers[LoaderHeader.cache];
450
468
  const canCache =
451
- this.cachingEnabled &&
452
- request.headers[LoaderHeader.cache] !== false &&
469
+ // Take header value if present, else use ILoaderOptions.cache value
470
+ (cacheHeader !== undefined ? cacheHeader === true : this.cachingEnabled) &&
453
471
  pendingLocalState === undefined;
454
- const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] ?? -1;
472
+ const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] as
473
+ | number
474
+ | undefined;
475
+ const opsBeforeReturn = request.headers[LoaderHeader.loadMode]?.opsBeforeReturn as
476
+ | string
477
+ | undefined;
478
+
479
+ if (
480
+ opsBeforeReturn === "sequenceNumber" &&
481
+ (fromSequenceNumber === undefined || fromSequenceNumber < 0)
482
+ ) {
483
+ // If opsBeforeReturn is set to "sequenceNumber", then fromSequenceNumber should be set to a non-negative integer.
484
+ throw new UsageError("sequenceNumber must be set to a non-negative integer");
485
+ } else if (opsBeforeReturn !== "sequenceNumber" && fromSequenceNumber !== undefined) {
486
+ // If opsBeforeReturn is not set to "sequenceNumber", then fromSequenceNumber should be undefined (default value).
487
+ // In this case, we should throw an error since opsBeforeReturn is not explicitly set to "sequenceNumber".
488
+ throw new UsageError('opsBeforeReturn must be set to "sequenceNumber"');
489
+ }
455
490
 
456
491
  let container: Container;
457
492
  if (canCache) {
@@ -468,24 +503,11 @@ export class Loader implements IHostLoader {
468
503
  container = await this.loadContainer(request, resolvedAsFluid, pendingLocalState);
469
504
  }
470
505
 
471
- if (container.deltaManager.lastSequenceNumber <= fromSequenceNumber) {
472
- await new Promise<void>((resolve, reject) => {
473
- function opHandler(message: ISequencedDocumentMessage) {
474
- if (message.sequenceNumber > fromSequenceNumber) {
475
- resolve();
476
- container.removeListener("op", opHandler);
477
- }
478
- }
479
-
480
- container.on("op", opHandler);
481
- });
482
- }
483
-
484
506
  return { container, parsed };
485
507
  }
486
508
 
487
509
  private get cachingEnabled() {
488
- return this.services.options.cache !== false;
510
+ return this.services.options.cache === true;
489
511
  }
490
512
 
491
513
  private async loadContainer(
@@ -499,6 +521,7 @@ export class Loader implements IHostLoader {
499
521
  version: request.headers?.[LoaderHeader.version] ?? undefined,
500
522
  loadMode: request.headers?.[LoaderHeader.loadMode],
501
523
  pendingLocalState,
524
+ loadToSequenceNumber: request.headers?.[LoaderHeader.sequenceNumber],
502
525
  },
503
526
  {
504
527
  canReconnect: request.headers?.[LoaderHeader.reconnect],
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-internal.5.3.2";
9
+ export const pkgVersion = "2.0.0-internal.6.0.0";
package/src/protocol.ts CHANGED
@@ -49,7 +49,6 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
49
49
  super(
50
50
  attributes.minimumSequenceNumber,
51
51
  attributes.sequenceNumber,
52
- OnlyValidTermValue,
53
52
  quorumSnapshot.members,
54
53
  quorumSnapshot.proposals,
55
54
  quorumSnapshot.values,
package/src/quorum.ts CHANGED
@@ -2,19 +2,9 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert } from "@fluidframework/common-utils";
6
5
  import { IFluidCodeDetails } from "@fluidframework/core-interfaces";
7
6
  import { ICommittedProposal } from "@fluidframework/protocol-definitions";
8
7
 
9
- export function getCodeDetailsFromQuorumValues(
10
- quorumValues: [string, ICommittedProposal][],
11
- ): IFluidCodeDetails {
12
- const qValuesMap = new Map(quorumValues);
13
- const proposal = qValuesMap.get("code");
14
- assert(proposal !== undefined, 0x2dc /* "Cannot find code proposal" */);
15
- return proposal?.value as IFluidCodeDetails;
16
- }
17
-
18
8
  export function initQuorumValuesFromCodeDetails(
19
9
  source: IFluidCodeDetails,
20
10
  ): [string, ICommittedProposal][] {
package/src/utils.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  import { ISummaryTree, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
15
15
  import { LoggingError } from "@fluidframework/telemetry-utils";
16
16
  import {
17
+ CombinedAppAndProtocolSummary,
17
18
  DeltaStreamConnectionForbiddenError,
18
19
  isCombinedAppAndProtocolSummary,
19
20
  } from "@fluidframework/driver-utils";
@@ -51,6 +52,34 @@ export function parseUrl(url: string): IParsedUrl | undefined {
51
52
  : undefined;
52
53
  }
53
54
 
55
+ /**
56
+ * Combine the app summary and protocol summary in 1 tree.
57
+ * @param appSummary - Summary of the app.
58
+ * @param protocolSummary - Summary of the protocol.
59
+ * @internal
60
+ */
61
+ export function combineAppAndProtocolSummary(
62
+ appSummary: ISummaryTree,
63
+ protocolSummary: ISummaryTree,
64
+ ): CombinedAppAndProtocolSummary {
65
+ assert(
66
+ !isCombinedAppAndProtocolSummary(appSummary),
67
+ 0x5a8 /* app summary is already a combined tree! */,
68
+ );
69
+ assert(
70
+ !isCombinedAppAndProtocolSummary(protocolSummary),
71
+ 0x5a9 /* protocol summary is already a combined tree! */,
72
+ );
73
+ const createNewSummary: CombinedAppAndProtocolSummary = {
74
+ type: SummaryType.Tree,
75
+ tree: {
76
+ ".protocol": protocolSummary,
77
+ ".app": appSummary,
78
+ },
79
+ };
80
+ return createNewSummary;
81
+ }
82
+
54
83
  /**
55
84
  * Converts summary tree (for upload) to snapshot tree (for download).
56
85
  * Summary tree blobs contain contents, but snapshot tree blobs normally