@fluidframework/container-loader 2.0.0-internal.5.4.0 → 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 (100) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/connectionManager.d.ts +1 -1
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +24 -25
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/connectionStateHandler.d.ts +2 -1
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +9 -16
  9. package/dist/connectionStateHandler.js.map +1 -1
  10. package/dist/container.d.ts +10 -5
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +160 -99
  13. package/dist/container.js.map +1 -1
  14. package/dist/containerContext.d.ts +2 -12
  15. package/dist/containerContext.d.ts.map +1 -1
  16. package/dist/containerContext.js +1 -20
  17. package/dist/containerContext.js.map +1 -1
  18. package/dist/containerStorageAdapter.js +3 -5
  19. package/dist/containerStorageAdapter.js.map +1 -1
  20. package/dist/contracts.d.ts +11 -2
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js +3 -3
  23. package/dist/contracts.js.map +1 -1
  24. package/dist/debugLogger.js +2 -3
  25. package/dist/debugLogger.js.map +1 -1
  26. package/dist/deltaManager.d.ts +16 -3
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +62 -24
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.js +1 -2
  31. package/dist/deltaQueue.js.map +1 -1
  32. package/dist/loader.d.ts +12 -0
  33. package/dist/loader.d.ts.map +1 -1
  34. package/dist/loader.js +57 -42
  35. package/dist/loader.js.map +1 -1
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/protocol.d.ts.map +1 -1
  40. package/dist/protocol.js +2 -3
  41. package/dist/protocol.js.map +1 -1
  42. package/dist/utils.d.ts +8 -1
  43. package/dist/utils.d.ts.map +1 -1
  44. package/dist/utils.js +24 -6
  45. package/dist/utils.js.map +1 -1
  46. package/lib/connectionManager.d.ts +1 -1
  47. package/lib/connectionManager.d.ts.map +1 -1
  48. package/lib/connectionManager.js +25 -26
  49. package/lib/connectionManager.js.map +1 -1
  50. package/lib/connectionStateHandler.d.ts +2 -1
  51. package/lib/connectionStateHandler.d.ts.map +1 -1
  52. package/lib/connectionStateHandler.js +9 -16
  53. package/lib/connectionStateHandler.js.map +1 -1
  54. package/lib/container.d.ts +10 -5
  55. package/lib/container.d.ts.map +1 -1
  56. package/lib/container.js +159 -98
  57. package/lib/container.js.map +1 -1
  58. package/lib/containerContext.d.ts +2 -12
  59. package/lib/containerContext.d.ts.map +1 -1
  60. package/lib/containerContext.js +1 -20
  61. package/lib/containerContext.js.map +1 -1
  62. package/lib/containerStorageAdapter.js +3 -5
  63. package/lib/containerStorageAdapter.js.map +1 -1
  64. package/lib/contracts.d.ts +11 -2
  65. package/lib/contracts.d.ts.map +1 -1
  66. package/lib/contracts.js +3 -3
  67. package/lib/contracts.js.map +1 -1
  68. package/lib/debugLogger.js +2 -3
  69. package/lib/debugLogger.js.map +1 -1
  70. package/lib/deltaManager.d.ts +16 -3
  71. package/lib/deltaManager.d.ts.map +1 -1
  72. package/lib/deltaManager.js +62 -24
  73. package/lib/deltaManager.js.map +1 -1
  74. package/lib/deltaQueue.js +1 -2
  75. package/lib/deltaQueue.js.map +1 -1
  76. package/lib/loader.d.ts +12 -0
  77. package/lib/loader.d.ts.map +1 -1
  78. package/lib/loader.js +57 -42
  79. package/lib/loader.js.map +1 -1
  80. package/lib/packageVersion.d.ts +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/protocol.d.ts.map +1 -1
  84. package/lib/protocol.js +2 -3
  85. package/lib/protocol.js.map +1 -1
  86. package/lib/utils.d.ts +8 -1
  87. package/lib/utils.d.ts.map +1 -1
  88. package/lib/utils.js +22 -5
  89. package/lib/utils.js.map +1 -1
  90. package/package.json +11 -11
  91. package/src/connectionManager.ts +7 -3
  92. package/src/connectionStateHandler.ts +3 -2
  93. package/src/container.ts +113 -28
  94. package/src/containerContext.ts +0 -24
  95. package/src/contracts.ts +16 -5
  96. package/src/deltaManager.ts +22 -5
  97. package/src/loader.ts +37 -23
  98. package/src/packageVersion.ts +1 -1
  99. package/src/protocol.ts +0 -1
  100. package/src/utils.ts +29 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-internal.5.4.0",
3
+ "version": "2.0.0-internal.6.0.0",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -37,14 +37,14 @@
37
37
  "dependencies": {
38
38
  "@fluidframework/common-definitions": "^0.20.1",
39
39
  "@fluidframework/common-utils": "^1.1.1",
40
- "@fluidframework/container-definitions": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
41
- "@fluidframework/container-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
42
- "@fluidframework/core-interfaces": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
43
- "@fluidframework/driver-definitions": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
44
- "@fluidframework/driver-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
45
- "@fluidframework/protocol-base": "^0.1039.1000",
40
+ "@fluidframework/container-definitions": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
41
+ "@fluidframework/container-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
42
+ "@fluidframework/core-interfaces": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
43
+ "@fluidframework/driver-definitions": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
44
+ "@fluidframework/driver-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
45
+ "@fluidframework/protocol-base": "^1.0.0",
46
46
  "@fluidframework/protocol-definitions": "^1.1.0",
47
- "@fluidframework/telemetry-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
47
+ "@fluidframework/telemetry-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
48
48
  "debug": "^4.1.1",
49
49
  "double-ended-queue": "^2.1.0-0",
50
50
  "events": "^3.1.0",
@@ -53,13 +53,13 @@
53
53
  "uuid": "^8.3.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@fluid-internal/test-loader-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
56
+ "@fluid-internal/test-loader-utils": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
57
57
  "@fluid-tools/build-cli": "^0.21.0",
58
- "@fluidframework/build-common": "^1.2.0",
58
+ "@fluidframework/build-common": "^2.0.0",
59
59
  "@fluidframework/build-tools": "^0.21.0",
60
60
  "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.5.2.0",
61
61
  "@fluidframework/eslint-config-fluid": "^2.0.0",
62
- "@fluidframework/mocha-test-setup": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
62
+ "@fluidframework/mocha-test-setup": ">=2.0.0-internal.6.0.0 <2.0.0-internal.6.1.0",
63
63
  "@microsoft/api-extractor": "^7.34.4",
64
64
  "@types/double-ended-queue": "^2.1.0",
65
65
  "@types/events": "^3.0.0",
@@ -6,10 +6,9 @@
6
6
  import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
8
8
  import {
9
+ ICriticalContainerError,
9
10
  IDeltaQueue,
10
11
  ReadOnlyInfo,
11
- IConnectionDetailsInternal,
12
- ICriticalContainerError,
13
12
  } from "@fluidframework/container-definitions";
14
13
  import { GenericError, UsageError } from "@fluidframework/container-utils";
15
14
  import {
@@ -44,7 +43,12 @@ import {
44
43
  ISequencedDocumentSystemMessage,
45
44
  } from "@fluidframework/protocol-definitions";
46
45
  import { ITelemetryLoggerExt, formatTick, normalizeError } from "@fluidframework/telemetry-utils";
47
- import { ReconnectMode, IConnectionManager, IConnectionManagerFactoryArgs } from "./contracts";
46
+ import {
47
+ ReconnectMode,
48
+ IConnectionManager,
49
+ IConnectionManagerFactoryArgs,
50
+ IConnectionDetailsInternal,
51
+ } from "./contracts";
48
52
  import { DeltaQueue } from "./deltaQueue";
49
53
  import { SignalType } from "./protocol";
50
54
  import { isDeltaStreamConnectionForbiddenError } from "./utils";
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
7
7
  import { assert, Timer } from "@fluidframework/common-utils";
8
- import { IConnectionDetailsInternal, IDeltaManager } from "@fluidframework/container-definitions";
8
+ import { IDeltaManager } from "@fluidframework/container-definitions";
9
9
  import { IAnyDriverError } from "@fluidframework/driver-definitions";
10
10
  import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
11
11
  import {
@@ -13,8 +13,9 @@ import {
13
13
  PerformanceEvent,
14
14
  loggerToMonitoringContext,
15
15
  } from "@fluidframework/telemetry-utils";
16
- import { ConnectionState } from "./connectionState";
17
16
  import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
17
+ import { ConnectionState } from "./connectionState";
18
+ import { IConnectionDetailsInternal } from "./contracts";
18
19
  import { IProtocolHandler } from "./protocol";
19
20
 
20
21
  // Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
package/src/container.ts CHANGED
@@ -23,27 +23,26 @@ import {
23
23
  FluidObject,
24
24
  } from "@fluidframework/core-interfaces";
25
25
  import {
26
+ AttachState,
27
+ ContainerWarning,
26
28
  IAudience,
27
- IConnectionDetailsInternal,
29
+ IBatchMessage,
30
+ ICodeDetailsLoader,
28
31
  IContainer,
29
32
  IContainerEvents,
30
- IDeltaManager,
31
- ICriticalContainerError,
32
- ContainerWarning,
33
- AttachState,
34
- IThrottlingWarning,
35
- ReadOnlyInfo,
36
33
  IContainerLoadMode,
34
+ ICriticalContainerError,
35
+ IDeltaManager,
37
36
  IFluidCodeDetails,
38
- isFluidCodeDetails,
39
- IBatchMessage,
40
- ICodeDetailsLoader,
41
37
  IHostLoader,
42
38
  IFluidModuleWithDetails,
43
39
  IProvideRuntimeFactory,
44
40
  IProvideFluidCodeDetailsComparer,
45
41
  IFluidCodeDetailsComparer,
46
42
  IRuntime,
43
+ isFluidCodeDetails,
44
+ IThrottlingWarning,
45
+ ReadOnlyInfo,
47
46
  } from "@fluidframework/container-definitions";
48
47
  import { GenericError, UsageError } from "@fluidframework/container-utils";
49
48
  import {
@@ -58,7 +57,6 @@ import {
58
57
  readAndParse,
59
58
  OnlineStatus,
60
59
  isOnline,
61
- combineAppAndProtocolSummary,
62
60
  runWithRetry,
63
61
  isCombinedAppAndProtocolSummary,
64
62
  MessageType2,
@@ -99,7 +97,12 @@ import {
99
97
  } from "@fluidframework/telemetry-utils";
100
98
  import { Audience } from "./audience";
101
99
  import { ContainerContext } from "./containerContext";
102
- import { ReconnectMode, IConnectionManagerFactoryArgs, getPackageName } from "./contracts";
100
+ import {
101
+ ReconnectMode,
102
+ IConnectionManagerFactoryArgs,
103
+ getPackageName,
104
+ IConnectionDetailsInternal,
105
+ } from "./contracts";
103
106
  import { DeltaManager, IConnectionArgs } from "./deltaManager";
104
107
  import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
105
108
  import { pkgVersion } from "./packageVersion";
@@ -110,7 +113,11 @@ import {
110
113
  ISerializableBlobContents,
111
114
  } from "./containerStorageAdapter";
112
115
  import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
113
- import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
116
+ import {
117
+ combineAppAndProtocolSummary,
118
+ getProtocolSnapshotTree,
119
+ getSnapshotTreeFromSerializedContainer,
120
+ } from "./utils";
114
121
  import { initQuorumValuesFromCodeDetails } from "./quorum";
115
122
  import { NoopHeuristic } from "./noopHeuristic";
116
123
  import { ConnectionManager } from "./connectionManager";
@@ -151,6 +158,11 @@ export interface IContainerLoadProps {
151
158
  * The pending state serialized from a pervious container instance
152
159
  */
153
160
  readonly pendingLocalState?: IPendingContainerState;
161
+
162
+ /**
163
+ * Load the container to at least this sequence number.
164
+ */
165
+ readonly loadToSequenceNumber?: number;
154
166
  }
155
167
 
156
168
  /**
@@ -369,7 +381,8 @@ export class Container
369
381
  loadProps: IContainerLoadProps,
370
382
  createProps: IContainerCreateProps,
371
383
  ): Promise<Container> {
372
- const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
384
+ const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } =
385
+ loadProps;
373
386
 
374
387
  const container = new Container(createProps, loadProps);
375
388
 
@@ -398,7 +411,7 @@ export class Container
398
411
  container.on("closed", onClosed);
399
412
 
400
413
  container
401
- .load(version, mode, resolvedUrl, pendingLocalState)
414
+ .load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
402
415
  .finally(() => {
403
416
  container.removeListener("closed", onClosed);
404
417
  })
@@ -1054,16 +1067,21 @@ export class Container
1054
1067
  }
1055
1068
  }
1056
1069
 
1057
- public closeAndGetPendingLocalState(): string {
1070
+ public async closeAndGetPendingLocalState(): Promise<string> {
1058
1071
  // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
1059
1072
  // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
1060
1073
  // a new clientId and a future container using stale pending state without the new clientId would resubmit them
1061
- const pendingState = this.getPendingLocalState();
1074
+ this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
1075
+ const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
1062
1076
  this.close();
1063
1077
  return pendingState;
1064
1078
  }
1065
1079
 
1066
- public getPendingLocalState(): string {
1080
+ public async getPendingLocalState(): Promise<string> {
1081
+ return this.getPendingLocalStateCore({ notifyImminentClosure: false });
1082
+ }
1083
+
1084
+ private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
1067
1085
  if (!this.offlineLoadEnabled) {
1068
1086
  throw new UsageError("Can't get pending local state unless offline load is enabled");
1069
1087
  }
@@ -1082,8 +1100,9 @@ export class Container
1082
1100
  );
1083
1101
  assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
1084
1102
  assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
1103
+ const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
1085
1104
  const pendingState: IPendingContainerState = {
1086
- pendingRuntimeState: this.runtime.getPendingLocalState(),
1105
+ pendingRuntimeState,
1087
1106
  baseSnapshot: this.baseSnapshot,
1088
1107
  snapshotBlobs: this.baseSnapshotBlobs,
1089
1108
  savedOps: this.savedOps,
@@ -1468,7 +1487,8 @@ export class Container
1468
1487
  specifiedVersion: string | undefined,
1469
1488
  loadMode: IContainerLoadMode,
1470
1489
  resolvedUrl: IResolvedUrl,
1471
- pendingLocalState?: IPendingContainerState,
1490
+ pendingLocalState: IPendingContainerState | undefined,
1491
+ loadToSequenceNumber: number | undefined,
1472
1492
  ) {
1473
1493
  this.service = await this.serviceFactory.createDocumentService(
1474
1494
  resolvedUrl,
@@ -1542,6 +1562,57 @@ export class Container
1542
1562
 
1543
1563
  let opsBeforeReturnP: Promise<void> | undefined;
1544
1564
 
1565
+ if (loadMode.pauseAfterLoad === true) {
1566
+ // If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
1567
+ if (loadMode.opsBeforeReturn === "sequenceNumber") {
1568
+ assert(
1569
+ loadToSequenceNumber !== undefined,
1570
+ 0x727 /* sequenceNumber should be defined */,
1571
+ );
1572
+ // Note: It is possible that we think the latest snapshot is newer than the specified sequence number
1573
+ // due to saved ops that may be replayed after the snapshot.
1574
+ // https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
1575
+ if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
1576
+ throw new Error(
1577
+ "Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.",
1578
+ );
1579
+ }
1580
+ }
1581
+
1582
+ // Force readonly mode - this will ensure we don't receive an error for the lack of join op
1583
+ this.forceReadonly(true);
1584
+
1585
+ // We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
1586
+ const opHandler = () => {
1587
+ if (loadToSequenceNumber === undefined) {
1588
+ // If there is no specified sequence number, pause after the inbound queue is empty.
1589
+ if (this.deltaManager.inbound.length !== 0) {
1590
+ return;
1591
+ }
1592
+ } else {
1593
+ // If there is a specified sequence number, keep processing until we reach it.
1594
+ if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
1595
+ return;
1596
+ }
1597
+ }
1598
+
1599
+ // Pause op processing once we have processed the desired number of ops.
1600
+ void this.deltaManager.inbound.pause();
1601
+ void this.deltaManager.outbound.pause();
1602
+ this.off("op", opHandler);
1603
+ };
1604
+ if (
1605
+ (loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
1606
+ this.deltaManager.lastSequenceNumber === loadToSequenceNumber
1607
+ ) {
1608
+ // If we have already reached the desired sequence number, call opHandler() to pause immediately.
1609
+ opHandler();
1610
+ } else {
1611
+ // If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
1612
+ this.on("op", opHandler);
1613
+ }
1614
+ }
1615
+
1545
1616
  // Attach op handlers to finish initialization and be able to start processing ops
1546
1617
  // Kick off any ops fetching if required.
1547
1618
  switch (loadMode.opsBeforeReturn) {
@@ -1553,6 +1624,9 @@ export class Container
1553
1624
  loadMode.deltaConnection !== "none" ? "all" : "none",
1554
1625
  );
1555
1626
  break;
1627
+ case "sequenceNumber":
1628
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
1629
+ break;
1556
1630
  case "cached":
1557
1631
  opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
1558
1632
  break;
@@ -1636,6 +1710,22 @@ export class Container
1636
1710
  }
1637
1711
  }
1638
1712
 
1713
+ // If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
1714
+ if (
1715
+ loadToSequenceNumber !== undefined &&
1716
+ this.deltaManager.lastSequenceNumber < loadToSequenceNumber
1717
+ ) {
1718
+ await new Promise<void>((resolve, reject) => {
1719
+ const opHandler = (message: ISequencedDocumentMessage) => {
1720
+ if (message.sequenceNumber >= loadToSequenceNumber) {
1721
+ resolve();
1722
+ this.off("op", opHandler);
1723
+ }
1724
+ };
1725
+ this.on("op", opHandler);
1726
+ });
1727
+ }
1728
+
1639
1729
  // Safety net: static version of Container.load() should have learned about it through "closed" handler.
1640
1730
  // But if that did not happen for some reason, fail load for sure.
1641
1731
  // Otherwise we can get into situations where container is closed and does not try to connect to ordering
@@ -1965,7 +2055,7 @@ export class Container
1965
2055
 
1966
2056
  private async attachDeltaManagerOpHandler(
1967
2057
  attributes: IDocumentAttributes,
1968
- prefetchType?: "cached" | "all" | "none",
2058
+ prefetchType?: "sequenceNumber" | "cached" | "all" | "none",
1969
2059
  ) {
1970
2060
  return this._deltaManager.attachOpHandler(
1971
2061
  attributes.minimumSequenceNumber,
@@ -2323,9 +2413,7 @@ export class Container
2323
2413
  (error?: ICriticalContainerError) => this.close(error),
2324
2414
  this.updateDirtyContainerState,
2325
2415
  this.getAbsoluteUrl,
2326
- () => this.resolvedUrl?.id,
2327
2416
  () => this.clientId,
2328
- () => this._deltaManager.serviceConfiguration,
2329
2417
  () => this.attachState,
2330
2418
  () => this.connected,
2331
2419
  getSpecifiedCodeDetails,
@@ -2334,9 +2422,6 @@ export class Container
2334
2422
  this.subLogger,
2335
2423
  pendingLocalState,
2336
2424
  );
2337
- this._lifecycleEvents.once("disposed", () => {
2338
- context.dispose();
2339
- });
2340
2425
 
2341
2426
  this._runtime = await PerformanceEvent.timedExecAsync(
2342
2427
  this.subLogger,
@@ -2388,7 +2473,7 @@ export interface IContainerExperimental extends IContainer {
2388
2473
  * @experimental misuse of this API can result in duplicate op submission and potential document corruption
2389
2474
  * {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
2390
2475
  */
2391
- getPendingLocalState?(): string;
2476
+ getPendingLocalState?(): Promise<string>;
2392
2477
 
2393
2478
  /**
2394
2479
  * Closes the container and returns serialized local state intended to be
@@ -2396,5 +2481,5 @@ export interface IContainerExperimental extends IContainer {
2396
2481
  * @experimental
2397
2482
  * {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
2398
2483
  */
2399
- closeAndGetPendingLocalState(): string;
2484
+ closeAndGetPendingLocalState?(): Promise<string>;
2400
2485
  }
@@ -18,7 +18,6 @@ import {
18
18
  import { FluidObject } from "@fluidframework/core-interfaces";
19
19
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
20
20
  import {
21
- IClientConfiguration,
22
21
  IClientDetails,
23
22
  IDocumentMessage,
24
23
  IQuorumClients,
@@ -46,13 +45,6 @@ export class ContainerContext implements IContainerContext {
46
45
  return this._getClientId();
47
46
  }
48
47
 
49
- /**
50
- * DISCLAIMER: this id is only for telemetry purposes. Not suitable for any other usages.
51
- */
52
- public get id(): string {
53
- return this._getContainerDiagnosticId() ?? "";
54
- }
55
-
56
48
  /**
57
49
  * When true, ops are free to flow
58
50
  * When false, ops should be kept as pending or rejected
@@ -61,16 +53,6 @@ export class ContainerContext implements IContainerContext {
61
53
  return this._getConnected();
62
54
  }
63
55
 
64
- public get serviceConfiguration(): IClientConfiguration | undefined {
65
- return this._getServiceConfiguration();
66
- }
67
-
68
- private _disposed = false;
69
-
70
- public get disposed() {
71
- return this._disposed;
72
- }
73
-
74
56
  constructor(
75
57
  public readonly options: ILoaderOptions,
76
58
  public readonly scope: FluidObject,
@@ -101,9 +83,7 @@ export class ContainerContext implements IContainerContext {
101
83
  public readonly closeFn: (error?: ICriticalContainerError) => void,
102
84
  public readonly updateDirtyContainerState: (dirty: boolean) => void,
103
85
  public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
104
- private readonly _getContainerDiagnosticId: () => string | undefined,
105
86
  private readonly _getClientId: () => string | undefined,
106
- private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
107
87
  private readonly _getAttachState: () => AttachState,
108
88
  private readonly _getConnected: () => boolean,
109
89
  public readonly getSpecifiedCodeDetails: () => IFluidCodeDetails | undefined,
@@ -113,10 +93,6 @@ export class ContainerContext implements IContainerContext {
113
93
  public readonly pendingLocalState?: unknown,
114
94
  ) {}
115
95
 
116
- public dispose(error?: Error): void {
117
- this._disposed = true;
118
- }
119
-
120
96
  public getLoadedFromVersion(): IVersion | undefined {
121
97
  return this._version;
122
98
  }
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.
@@ -7,13 +7,11 @@ import { v4 as uuid } from "uuid";
7
7
  import { IEventProvider } from "@fluidframework/common-definitions";
8
8
  import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
9
9
  import {
10
- IDeltaHandlerStrategy,
10
+ ICriticalContainerError,
11
11
  IDeltaManager,
12
12
  IDeltaManagerEvents,
13
13
  IDeltaQueue,
14
- ICriticalContainerError,
15
14
  IThrottlingWarning,
16
- IConnectionDetailsInternal,
17
15
  } from "@fluidframework/container-definitions";
18
16
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
19
17
  import {
@@ -44,7 +42,11 @@ import {
44
42
  DataProcessingError,
45
43
  UsageError,
46
44
  } from "@fluidframework/container-utils";
47
- import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
45
+ import {
46
+ IConnectionDetailsInternal,
47
+ IConnectionManager,
48
+ IConnectionManagerFactoryArgs,
49
+ } from "./contracts";
48
50
  import { DeltaQueue } from "./deltaQueue";
49
51
  import { OnlyValidTermValue } from "./protocol";
50
52
 
@@ -73,6 +75,21 @@ interface IBatchMetadata {
73
75
  batch?: boolean;
74
76
  }
75
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
+
76
93
  /**
77
94
  * Determines if message was sent by client, not service
78
95
  */
@@ -495,7 +512,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
495
512
  minSequenceNumber: number,
496
513
  sequenceNumber: number,
497
514
  handler: IDeltaHandlerStrategy,
498
- prefetchType: "cached" | "all" | "none" = "none",
515
+ prefetchType: "sequenceNumber" | "cached" | "all" | "none" = "none",
499
516
  ) {
500
517
  this.initSequenceNumber = sequenceNumber;
501
518
  this.lastProcessedSequenceNumber = sequenceNumber;
package/src/loader.ts CHANGED
@@ -37,7 +37,7 @@ import {
37
37
  IResolvedUrl,
38
38
  IUrlResolver,
39
39
  } from "@fluidframework/driver-definitions";
40
- import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
40
+ import { UsageError } from "@fluidframework/container-utils";
41
41
  import { Container, IPendingContainerState } from "./container";
42
42
  import { IParsedUrl, parseUrl } from "./utils";
43
43
  import { pkgVersion } from "./packageVersion";
@@ -45,11 +45,7 @@ import { ProtocolHandlerBuilder } from "./protocol";
45
45
  import { DebugLogger } from "./debugLogger";
46
46
 
47
47
  function canUseCache(request: IRequest): boolean {
48
- if (request.headers === undefined) {
49
- return true;
50
- }
51
-
52
- return request.headers[LoaderHeader.cache] !== false;
48
+ return request.headers?.[LoaderHeader.cache] === true;
53
49
  }
54
50
 
55
51
  function ensureResolvedUrlDefined(
@@ -68,6 +64,9 @@ export class RelativeLoader implements ILoader {
68
64
  private readonly loader: ILoader | undefined,
69
65
  ) {}
70
66
 
67
+ /**
68
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
69
+ */
71
70
  public get IFluidRouter(): IFluidRouter {
72
71
  return this;
73
72
  }
@@ -99,6 +98,9 @@ export class RelativeLoader implements ILoader {
99
98
  return this.loader.resolve(request);
100
99
  }
101
100
 
101
+ /**
102
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
103
+ */
102
104
  public async request(request: IRequest): Promise<IResponse> {
103
105
  if (request.url.startsWith("/")) {
104
106
  const container = await this.resolve(request);
@@ -348,6 +350,9 @@ export class Loader implements IHostLoader {
348
350
  });
349
351
  }
350
352
 
353
+ /**
354
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
355
+ */
351
356
  public get IFluidRouter(): IFluidRouter {
352
357
  return this;
353
358
  }
@@ -383,6 +388,9 @@ export class Loader implements IHostLoader {
383
388
  });
384
389
  }
385
390
 
391
+ /**
392
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the Container's IFluidRouter/request.
393
+ */
386
394
  public async request(request: IRequest): Promise<IResponse> {
387
395
  return PerformanceEvent.timedExecAsync(
388
396
  this.mc.logger,
@@ -456,11 +464,29 @@ export class Loader implements IHostLoader {
456
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.
457
465
  request.headers[LoaderHeader.version] =
458
466
  parsed.version ?? request.headers[LoaderHeader.version];
467
+ const cacheHeader = request.headers[LoaderHeader.cache];
459
468
  const canCache =
460
- this.cachingEnabled &&
461
- request.headers[LoaderHeader.cache] !== false &&
469
+ // Take header value if present, else use ILoaderOptions.cache value
470
+ (cacheHeader !== undefined ? cacheHeader === true : this.cachingEnabled) &&
462
471
  pendingLocalState === undefined;
463
- 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
+ }
464
490
 
465
491
  let container: Container;
466
492
  if (canCache) {
@@ -477,24 +503,11 @@ export class Loader implements IHostLoader {
477
503
  container = await this.loadContainer(request, resolvedAsFluid, pendingLocalState);
478
504
  }
479
505
 
480
- if (container.deltaManager.lastSequenceNumber <= fromSequenceNumber) {
481
- await new Promise<void>((resolve, reject) => {
482
- function opHandler(message: ISequencedDocumentMessage) {
483
- if (message.sequenceNumber > fromSequenceNumber) {
484
- resolve();
485
- container.removeListener("op", opHandler);
486
- }
487
- }
488
-
489
- container.on("op", opHandler);
490
- });
491
- }
492
-
493
506
  return { container, parsed };
494
507
  }
495
508
 
496
509
  private get cachingEnabled() {
497
- return this.services.options.cache !== false;
510
+ return this.services.options.cache === true;
498
511
  }
499
512
 
500
513
  private async loadContainer(
@@ -508,6 +521,7 @@ export class Loader implements IHostLoader {
508
521
  version: request.headers?.[LoaderHeader.version] ?? undefined,
509
522
  loadMode: request.headers?.[LoaderHeader.loadMode],
510
523
  pendingLocalState,
524
+ loadToSequenceNumber: request.headers?.[LoaderHeader.sequenceNumber],
511
525
  },
512
526
  {
513
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.4.0";
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,