@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917

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 +14 -0
  2. package/README.md +45 -4
  3. package/closeAndGetPendingLocalState.md +51 -0
  4. package/dist/connectionManager.d.ts +2 -1
  5. package/dist/connectionManager.d.ts.map +1 -1
  6. package/dist/connectionManager.js +59 -17
  7. package/dist/connectionManager.js.map +1 -1
  8. package/dist/connectionStateHandler.d.ts +4 -4
  9. package/dist/connectionStateHandler.d.ts.map +1 -1
  10. package/dist/connectionStateHandler.js +7 -0
  11. package/dist/connectionStateHandler.js.map +1 -1
  12. package/dist/container.d.ts +44 -4
  13. package/dist/container.d.ts.map +1 -1
  14. package/dist/container.js +160 -105
  15. package/dist/container.js.map +1 -1
  16. package/dist/containerContext.d.ts +18 -8
  17. package/dist/containerContext.d.ts.map +1 -1
  18. package/dist/containerContext.js +47 -4
  19. package/dist/containerContext.js.map +1 -1
  20. package/dist/containerStorageAdapter.d.ts +41 -2
  21. package/dist/containerStorageAdapter.d.ts.map +1 -1
  22. package/dist/containerStorageAdapter.js +87 -11
  23. package/dist/containerStorageAdapter.js.map +1 -1
  24. package/dist/contracts.d.ts +2 -2
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/contracts.js.map +1 -1
  27. package/dist/deltaManager.d.ts +4 -5
  28. package/dist/deltaManager.d.ts.map +1 -1
  29. package/dist/deltaManager.js +7 -10
  30. package/dist/deltaManager.js.map +1 -1
  31. package/dist/deltaManagerProxy.d.ts +10 -22
  32. package/dist/deltaManagerProxy.d.ts.map +1 -1
  33. package/dist/deltaManagerProxy.js +14 -50
  34. package/dist/deltaManagerProxy.js.map +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/loader.d.ts +10 -1
  40. package/dist/loader.d.ts.map +1 -1
  41. package/dist/loader.js +25 -16
  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 +1 -0
  47. package/dist/protocol.d.ts.map +1 -1
  48. package/dist/protocol.js +4 -2
  49. package/dist/protocol.js.map +1 -1
  50. package/dist/protocolTreeDocumentStorageService.d.ts +6 -2
  51. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  52. package/dist/protocolTreeDocumentStorageService.js +7 -4
  53. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  54. package/dist/utils.d.ts.map +1 -1
  55. package/dist/utils.js +2 -1
  56. package/dist/utils.js.map +1 -1
  57. package/lib/connectionManager.d.ts +2 -1
  58. package/lib/connectionManager.d.ts.map +1 -1
  59. package/lib/connectionManager.js +60 -18
  60. package/lib/connectionManager.js.map +1 -1
  61. package/lib/connectionStateHandler.d.ts +4 -4
  62. package/lib/connectionStateHandler.d.ts.map +1 -1
  63. package/lib/connectionStateHandler.js +7 -0
  64. package/lib/connectionStateHandler.js.map +1 -1
  65. package/lib/container.d.ts +44 -4
  66. package/lib/container.d.ts.map +1 -1
  67. package/lib/container.js +164 -109
  68. package/lib/container.js.map +1 -1
  69. package/lib/containerContext.d.ts +18 -8
  70. package/lib/containerContext.d.ts.map +1 -1
  71. package/lib/containerContext.js +48 -5
  72. package/lib/containerContext.js.map +1 -1
  73. package/lib/containerStorageAdapter.d.ts +41 -2
  74. package/lib/containerStorageAdapter.d.ts.map +1 -1
  75. package/lib/containerStorageAdapter.js +85 -11
  76. package/lib/containerStorageAdapter.js.map +1 -1
  77. package/lib/contracts.d.ts +2 -2
  78. package/lib/contracts.d.ts.map +1 -1
  79. package/lib/contracts.js.map +1 -1
  80. package/lib/deltaManager.d.ts +4 -5
  81. package/lib/deltaManager.d.ts.map +1 -1
  82. package/lib/deltaManager.js +7 -10
  83. package/lib/deltaManager.js.map +1 -1
  84. package/lib/deltaManagerProxy.d.ts +10 -22
  85. package/lib/deltaManagerProxy.d.ts.map +1 -1
  86. package/lib/deltaManagerProxy.js +14 -50
  87. package/lib/deltaManagerProxy.js.map +1 -1
  88. package/lib/index.d.ts +3 -2
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +2 -2
  91. package/lib/index.js.map +1 -1
  92. package/lib/loader.d.ts +10 -1
  93. package/lib/loader.d.ts.map +1 -1
  94. package/lib/loader.js +24 -16
  95. package/lib/loader.js.map +1 -1
  96. package/lib/packageVersion.d.ts +1 -1
  97. package/lib/packageVersion.js +1 -1
  98. package/lib/packageVersion.js.map +1 -1
  99. package/lib/protocol.d.ts +1 -0
  100. package/lib/protocol.d.ts.map +1 -1
  101. package/lib/protocol.js +3 -1
  102. package/lib/protocol.js.map +1 -1
  103. package/lib/protocolTreeDocumentStorageService.d.ts +6 -2
  104. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  105. package/lib/protocolTreeDocumentStorageService.js +7 -4
  106. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  107. package/lib/utils.d.ts.map +1 -1
  108. package/lib/utils.js +2 -1
  109. package/lib/utils.js.map +1 -1
  110. package/package.json +64 -56
  111. package/src/connectionManager.ts +65 -24
  112. package/src/connectionStateHandler.ts +17 -5
  113. package/src/container.ts +239 -137
  114. package/src/containerContext.ts +74 -11
  115. package/src/containerStorageAdapter.ts +113 -9
  116. package/src/contracts.ts +2 -2
  117. package/src/deltaManager.ts +12 -14
  118. package/src/deltaManagerProxy.ts +18 -73
  119. package/src/index.ts +3 -3
  120. package/src/loader.ts +31 -26
  121. package/src/packageVersion.ts +1 -1
  122. package/src/protocol.ts +4 -1
  123. package/src/protocolTreeDocumentStorageService.ts +6 -3
  124. package/src/utils.ts +7 -4
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { LazyPromise } from "@fluidframework/common-utils";
7
+ import { assert, LazyPromise, TypedEventEmitter } from "@fluidframework/common-utils";
8
8
  import {
9
9
  IAudience,
10
10
  IContainerContext,
@@ -21,7 +21,6 @@ import {
21
21
  IProvideFluidCodeDetailsComparer,
22
22
  ICodeDetailsLoader,
23
23
  IFluidModuleWithDetails,
24
- ISnapshotTreeWithBlobContents,
25
24
  IBatchMessage,
26
25
  } from "@fluidframework/container-definitions";
27
26
  import { IRequest, IResponse, FluidObject } from "@fluidframework/core-interfaces";
@@ -42,10 +41,21 @@ import {
42
41
  ISummaryContent,
43
42
  } from "@fluidframework/protocol-definitions";
44
43
  import { PerformanceEvent } from "@fluidframework/telemetry-utils";
44
+ import { UsageError } from "@fluidframework/container-utils";
45
45
  import { Container } from "./container";
46
46
 
47
47
  const PackageNotFactoryError = "Code package does not implement IRuntimeFactory";
48
48
 
49
+ /**
50
+ * Events that {@link ContainerContext} can emit through its lifecycle.
51
+ *
52
+ * "runtimeInstantiated" - When an {@link @fluidframework/container-definitions#IRuntime} has been instantiated (by
53
+ * calling instantiateRuntime() on the runtime factory), and this._runtime is set.
54
+ *
55
+ * "disposed" - When its dispose() method is called. The {@link ContainerContext} is no longer usable at that point.
56
+ */
57
+ type ContextLifecycleEvents = "runtimeInstantiated" | "disposed";
58
+
49
59
  export class ContainerContext implements IContainerContext {
50
60
  public static async createOrLoad(
51
61
  container: Container,
@@ -57,8 +67,8 @@ export class ContainerContext implements IContainerContext {
57
67
  quorum: IQuorum,
58
68
  loader: ILoader,
59
69
  submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
60
- submitSummaryFn: (summaryOp: ISummaryContent) => number,
61
- submitBatchFn: (batch: IBatchMessage[]) => number,
70
+ submitSummaryFn: (summaryOp: ISummaryContent, referenceSequenceNumber?: number) => number,
71
+ submitBatchFn: (batch: IBatchMessage[], referenceSequenceNumber?: number) => number,
62
72
  submitSignalFn: (contents: any) => void,
63
73
  disposeFn: (error?: ICriticalContainerError) => void,
64
74
  closeFn: (error?: ICriticalContainerError) => void,
@@ -92,6 +102,7 @@ export class ContainerContext implements IContainerContext {
92
102
  }
93
103
 
94
104
  public readonly taggedLogger: ITelemetryLogger;
105
+ public readonly supportedFeatures: ReadonlyMap<string, unknown>;
95
106
 
96
107
  public get clientId(): string | undefined {
97
108
  return this.container.clientId;
@@ -170,12 +181,46 @@ export class ContainerContext implements IContainerContext {
170
181
 
171
182
  private readonly _fluidModuleP: Promise<IFluidModuleWithDetails>;
172
183
 
184
+ /**
185
+ * {@inheritDoc @fluidframework/container-definitions#IContainerContext.getEntryPoint}
186
+ */
187
+ public async getEntryPoint?(): Promise<FluidObject | undefined> {
188
+ if (this._disposed) {
189
+ throw new UsageError("The context is already disposed");
190
+ }
191
+ if (this._runtime !== undefined) {
192
+ return this._runtime?.getEntryPoint?.();
193
+ }
194
+ return new Promise<FluidObject | undefined>((resolve, reject) => {
195
+ const runtimeInstantiatedHandler = () => {
196
+ assert(
197
+ this._runtime !== undefined,
198
+ 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */,
199
+ );
200
+ resolve(this._runtime.getEntryPoint?.());
201
+ this.lifecycleEvents.off("disposed", disposedHandler);
202
+ };
203
+ const disposedHandler = () => {
204
+ reject(new Error("ContainerContext was disposed"));
205
+ this.lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
206
+ };
207
+ this.lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
208
+ this.lifecycleEvents.once("disposed", disposedHandler);
209
+ });
210
+ }
211
+
212
+ /**
213
+ * Emits events about the container context's lifecycle.
214
+ * Use it to coordinate things inside the ContainerContext class.
215
+ */
216
+ private readonly lifecycleEvents = new TypedEventEmitter<ContextLifecycleEvents>();
217
+
173
218
  constructor(
174
219
  private readonly container: Container,
175
220
  public readonly scope: FluidObject,
176
221
  private readonly codeLoader: ICodeDetailsLoader,
177
222
  private readonly _codeDetails: IFluidCodeDetails,
178
- private _baseSnapshot: ISnapshotTree | undefined,
223
+ private readonly _baseSnapshot: ISnapshotTree | undefined,
179
224
  public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
180
225
  quorum: IQuorum,
181
226
  public readonly loader: ILoader,
@@ -185,9 +230,15 @@ export class ContainerContext implements IContainerContext {
185
230
  batch: boolean,
186
231
  appData: any,
187
232
  ) => number,
188
- public readonly submitSummaryFn: (summaryOp: ISummaryContent) => number,
233
+ public readonly submitSummaryFn: (
234
+ summaryOp: ISummaryContent,
235
+ referenceSequenceNumber?: number,
236
+ ) => number,
189
237
  /** @returns clientSequenceNumber of last message in a batch */
190
- public readonly submitBatchFn: (batch: IBatchMessage[]) => number,
238
+ public readonly submitBatchFn: (
239
+ batch: IBatchMessage[],
240
+ referenceSequenceNumber?: number,
241
+ ) => number,
191
242
  public readonly submitSignalFn: (contents: any) => void,
192
243
  public readonly disposeFn: (error?: ICriticalContainerError) => void,
193
244
  public readonly closeFn: (error?: ICriticalContainerError) => void,
@@ -202,6 +253,15 @@ export class ContainerContext implements IContainerContext {
202
253
  this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(async () =>
203
254
  this.loadCodeModule(_codeDetails),
204
255
  );
256
+
257
+ this.supportedFeatures = new Map([
258
+ /**
259
+ * This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
260
+ * as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
261
+ * This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
262
+ */
263
+ ["referenceSequenceNumbers", true],
264
+ ]);
205
265
  this.attachListener();
206
266
  }
207
267
 
@@ -223,6 +283,7 @@ export class ContainerContext implements IContainerContext {
223
283
  }
224
284
  this._disposed = true;
225
285
 
286
+ this.lifecycleEvents.emit("disposed");
226
287
  this.runtime.dispose(error);
227
288
  this._quorum.dispose();
228
289
  this.deltaManager.dispose();
@@ -313,10 +374,8 @@ export class ContainerContext implements IContainerContext {
313
374
  return true;
314
375
  }
315
376
 
316
- public notifyAttaching(snapshot: ISnapshotTreeWithBlobContents) {
317
- this._baseSnapshot = snapshot;
318
- this.runtime.notifyAttaching?.(snapshot);
319
- this.runtime.setAttachState(AttachState.Attaching);
377
+ public async notifyOpReplay(message: ISequencedDocumentMessage): Promise<void> {
378
+ return this.runtime.notifyOpReplay?.(message);
320
379
  }
321
380
 
322
381
  // #region private
@@ -340,9 +399,13 @@ export class ContainerContext implements IContainerContext {
340
399
  { eventName: "InstantiateRuntime" },
341
400
  async () => runtimeFactory.instantiateRuntime(this, existing),
342
401
  );
402
+ this.lifecycleEvents.emit("runtimeInstantiated");
343
403
  }
344
404
 
345
405
  private attachListener() {
406
+ this.container.once("attaching", () => {
407
+ this.runtime.setAttachState(AttachState.Attaching);
408
+ });
346
409
  this.container.once("attached", () => {
347
410
  this.runtime.setAttachState(AttachState.Attached);
348
411
  });
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert } from "@fluidframework/common-utils";
7
+ import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
8
8
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
9
9
  import {
10
10
  FetchSource,
@@ -25,20 +25,51 @@ import { IDetachedBlobStorage } from "./loader";
25
25
  import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
26
26
  import { RetriableDocumentStorageService } from "./retriableDocumentStorageService";
27
27
 
28
+ /**
29
+ * Stringified blobs from a summary/snapshot tree.
30
+ * @deprecated this is an internal interface and will not longer be exported in future versions
31
+ * @internal
32
+ */
33
+ export interface ISerializableBlobContents {
34
+ [id: string]: string;
35
+ }
36
+
28
37
  /**
29
38
  * This class wraps the actual storage and make sure no wrong apis are called according to
30
39
  * container attach state.
31
40
  */
32
41
  export class ContainerStorageAdapter implements IDocumentStorageService, IDisposable {
33
- private readonly blobContents: { [id: string]: ArrayBufferLike } = {};
34
42
  private _storageService: IDocumentStorageService & Partial<IDisposable>;
35
43
 
36
- constructor(
44
+ private _summarizeProtocolTree: boolean | undefined;
45
+ /**
46
+ * Whether the adapter will enforce sending combined summary trees.
47
+ */
48
+ public get summarizeProtocolTree() {
49
+ return this._summarizeProtocolTree === true;
50
+ }
51
+
52
+ /**
53
+ * An adapter that ensures we're using detachedBlobStorage up until we connect to a real service, and then
54
+ * after connecting to a real service augments it with retry and combined summary tree enforcement.
55
+ * @param detachedBlobStorage - The detached blob storage to use up until we connect to a real service
56
+ * @param logger - Telemetry logger
57
+ * @param addProtocolSummaryIfMissing - a callback to permit the container to inspect the summary we're about to
58
+ * upload, and fix it up with a protocol tree if needed
59
+ * @param forceEnableSummarizeProtocolTree - Enforce uploading a protocol summary regardless of the service's policy
60
+ */
61
+ public constructor(
37
62
  detachedBlobStorage: IDetachedBlobStorage | undefined,
38
63
  private readonly logger: ITelemetryLogger,
39
- private readonly captureProtocolSummary?: () => ISummaryTree,
64
+ /**
65
+ * ArrayBufferLikes or utf8 encoded strings, containing blobs from a snapshot
66
+ */
67
+ private readonly blobContents: { [id: string]: ArrayBufferLike | string } = {},
68
+ private readonly addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree,
69
+ forceEnableSummarizeProtocolTree: boolean | undefined,
40
70
  ) {
41
71
  this._storageService = new BlobOnlyStorage(detachedBlobStorage, logger);
72
+ this._summarizeProtocolTree = forceEnableSummarizeProtocolTree;
42
73
  }
43
74
 
44
75
  disposed: boolean = false;
@@ -58,11 +89,13 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
58
89
  this.logger,
59
90
  ));
60
91
 
61
- if (this.captureProtocolSummary !== undefined) {
92
+ this._summarizeProtocolTree =
93
+ this._summarizeProtocolTree ?? service.policies?.summarizeProtocolTree;
94
+ if (this.summarizeProtocolTree) {
62
95
  this.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
63
96
  this._storageService = new ProtocolTreeStorageService(
64
97
  retriableStorage,
65
- this.captureProtocolSummary,
98
+ this.addProtocolSummaryIfMissing,
66
99
  );
67
100
  }
68
101
 
@@ -107,9 +140,13 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
107
140
  }
108
141
 
109
142
  public async readBlob(id: string): Promise<ArrayBufferLike> {
110
- const blob = this.blobContents[id];
111
- if (blob !== undefined) {
112
- return blob;
143
+ const maybeBlob = this.blobContents[id];
144
+ if (maybeBlob !== undefined) {
145
+ if (typeof maybeBlob === "string") {
146
+ const blob = stringToBuffer(maybeBlob, "utf8");
147
+ return blob;
148
+ }
149
+ return maybeBlob;
113
150
  }
114
151
  return this._storageService.readBlob(id);
115
152
  }
@@ -191,3 +228,70 @@ class BlobOnlyStorage implements IDocumentStorageService {
191
228
  }
192
229
  }
193
230
  }
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
+ /**
239
+ * Get blob contents of a snapshot tree from storage (or, ideally, cache)
240
+ */
241
+ export async function getBlobContentsFromTree(
242
+ snapshot: ISnapshotTree,
243
+ storage: IDocumentStorageService,
244
+ ): Promise<ISerializableBlobContents> {
245
+ const blobs = {};
246
+ await getBlobContentsFromTreeCore(snapshot, blobs, storage);
247
+ return blobs;
248
+ }
249
+
250
+ async function getBlobContentsFromTreeCore(
251
+ tree: ISnapshotTree,
252
+ blobs: ISerializableBlobContents,
253
+ storage: IDocumentStorageService,
254
+ root = true,
255
+ ) {
256
+ 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
+ }
261
+ }
262
+ for (const id of Object.values(tree.blobs)) {
263
+ const blob = await storage.readBlob(id);
264
+ // ArrayBufferLike will not survive JSON.stringify()
265
+ blobs[id] = bufferToString(blob, "utf8");
266
+ }
267
+ return Promise.all(treePs);
268
+ }
269
+
270
+ /**
271
+ * Extract blob contents from a snapshot tree with blob contents
272
+ */
273
+ export function getBlobContentsFromTreeWithBlobContents(
274
+ snapshot: ISnapshotTreeWithBlobContents,
275
+ ): ISerializableBlobContents {
276
+ const blobs = {};
277
+ getBlobContentsFromTreeWithBlobContentsCore(snapshot, blobs);
278
+ return blobs;
279
+ }
280
+
281
+ function getBlobContentsFromTreeWithBlobContentsCore(
282
+ tree: ISnapshotTreeWithBlobContents,
283
+ blobs: ISerializableBlobContents,
284
+ root = true,
285
+ ) {
286
+ for (const [key, subTree] of Object.entries(tree.trees)) {
287
+ if (!root || key !== blobsTreeName) {
288
+ getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
289
+ }
290
+ }
291
+ for (const id of Object.values(tree.blobs)) {
292
+ const blob = tree.blobsContents[id];
293
+ assert(blob !== undefined, 0x2ec /* "Blob must be present in blobsContents" */);
294
+ // ArrayBufferLike will not survive JSON.stringify()
295
+ blobs[id] = bufferToString(blob, "utf8");
296
+ }
297
+ }
package/src/contracts.ts CHANGED
@@ -7,7 +7,7 @@ import { ITelemetryProperties } from "@fluidframework/common-definitions";
7
7
  import {
8
8
  IDeltaQueue,
9
9
  ReadOnlyInfo,
10
- IConnectionDetails,
10
+ IConnectionDetailsInternal,
11
11
  ICriticalContainerError,
12
12
  IFluidCodeDetails,
13
13
  isFluidPackage,
@@ -144,7 +144,7 @@ export interface IConnectionManagerFactoryArgs {
144
144
  /**
145
145
  * Called whenever new connection to rely service is established
146
146
  */
147
- readonly connectHandler: (connection: IConnectionDetails) => void;
147
+ readonly connectHandler: (connection: IConnectionDetailsInternal) => void;
148
148
 
149
149
  /**
150
150
  * Called whenever ping/pong messages are roundtripped on connection.
@@ -18,7 +18,7 @@ import {
18
18
  IDeltaQueue,
19
19
  ICriticalContainerError,
20
20
  IThrottlingWarning,
21
- IConnectionDetails,
21
+ IConnectionDetailsInternal,
22
22
  } from "@fluidframework/container-definitions";
23
23
  import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
24
24
  import { normalizeError, logIfFalse, safeRaiseEvent } from "@fluidframework/telemetry-utils";
@@ -41,8 +41,9 @@ import {
41
41
  extractSafePropertiesFromMessage,
42
42
  DataProcessingError,
43
43
  } from "@fluidframework/container-utils";
44
- import { DeltaQueue } from "./deltaQueue";
45
44
  import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
45
+ import { DeltaQueue } from "./deltaQueue";
46
+ import { OnlyValidTermValue } from "./protocol";
46
47
 
47
48
  export interface IConnectionArgs {
48
49
  mode?: ConnectionMode;
@@ -57,6 +58,7 @@ export interface IConnectionArgs {
57
58
  export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
58
59
  (event: "throttled", listener: (error: IThrottlingWarning) => void);
59
60
  (event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
61
+ (event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
60
62
  }
61
63
 
62
64
  /**
@@ -122,7 +124,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
122
124
  private lastObservedSeqNumber: number = 0;
123
125
  private lastProcessedSequenceNumber: number = 0;
124
126
  private lastProcessedMessage: ISequencedDocumentMessage | undefined;
125
- private baseTerm: number = 0;
126
127
 
127
128
  /** count number of noops sent by the client which may not be acked */
128
129
  private noOpCount: number = 0;
@@ -186,10 +187,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
186
187
  return this.lastObservedSeqNumber;
187
188
  }
188
189
 
189
- public get referenceTerm(): number {
190
- return this.baseTerm;
191
- }
192
-
193
190
  public get minimumSequenceNumber(): number {
194
191
  return this.minSequenceNumber;
195
192
  }
@@ -230,11 +227,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
230
227
  batch = false,
231
228
  metadata?: any,
232
229
  compression?: string,
230
+ referenceSequenceNumber?: number,
233
231
  ) {
232
+ // Back-compat ADO:3455
233
+ const backCompatRefSeqNum = referenceSequenceNumber ?? this.lastProcessedSequenceNumber;
234
234
  const messagePartial: Omit<IDocumentMessage, "clientSequenceNumber"> = {
235
235
  contents,
236
236
  metadata,
237
- referenceSequenceNumber: this.lastProcessedSequenceNumber,
237
+ referenceSequenceNumber: backCompatRefSeqNum,
238
238
  type,
239
239
  compression,
240
240
  };
@@ -358,7 +358,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
358
358
  this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
359
359
  closeHandler: (error: any) => this.close(error),
360
360
  disconnectHandler: (reason: string) => this.disconnectHandler(reason),
361
- connectHandler: (connection: IConnectionDetails) => this.connectHandler(connection),
361
+ connectHandler: (connection: IConnectionDetailsInternal) =>
362
+ this.connectHandler(connection),
362
363
  pongHandler: (latency: number) => this.emit("pong", latency),
363
364
  readonlyChangeHandler: (readonly?: boolean) =>
364
365
  safeRaiseEvent(this, this.logger, "readonly", readonly),
@@ -401,7 +402,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
401
402
  // - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
402
403
  }
403
404
 
404
- private connectHandler(connection: IConnectionDetails) {
405
+ private connectHandler(connection: IConnectionDetailsInternal) {
405
406
  this.refreshDelayInfo(this.deltaStreamDelayId);
406
407
 
407
408
  const props = this.connectionManager.connectionVerboseProps;
@@ -460,13 +461,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
460
461
  public async attachOpHandler(
461
462
  minSequenceNumber: number,
462
463
  sequenceNumber: number,
463
- term: number,
464
464
  handler: IDeltaHandlerStrategy,
465
465
  prefetchType: "cached" | "all" | "none" = "none",
466
466
  ) {
467
467
  this.initSequenceNumber = sequenceNumber;
468
468
  this.lastProcessedSequenceNumber = sequenceNumber;
469
- this.baseTerm = term;
470
469
  this.minSequenceNumber = minSequenceNumber;
471
470
  this.lastQueuedSequenceNumber = sequenceNumber;
472
471
  this.lastObservedSeqNumber = sequenceNumber;
@@ -981,9 +980,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
981
980
 
982
981
  // Back-compat for older server with no term
983
982
  if (message.term === undefined) {
984
- message.term = 1;
983
+ message.term = OnlyValidTermValue;
985
984
  }
986
- this.baseTerm = message.term;
987
985
 
988
986
  if (this.handler === undefined) {
989
987
  throw new Error("Attempted to process an inbound message without a handler attached");
@@ -5,20 +5,16 @@
5
5
 
6
6
  import {
7
7
  IDeltaManager,
8
- IDeltaManagerEvents,
9
8
  IDeltaQueue,
10
- IDeltaSender,
11
9
  IDeltaQueueEvents,
12
- ReadOnlyInfo,
13
10
  } from "@fluidframework/container-definitions";
14
11
  import { EventForwarder } from "@fluidframework/common-utils";
15
12
  import {
16
- IClientConfiguration,
17
- IClientDetails,
18
13
  IDocumentMessage,
19
14
  ISequencedDocumentMessage,
20
15
  ISignalMessage,
21
16
  } from "@fluidframework/protocol-definitions";
17
+ import { DeltaManagerProxyBase } from "@fluidframework/container-utils";
22
18
 
23
19
  /**
24
20
  * Proxy to the real IDeltaQueue - used to restrict access
@@ -78,87 +74,36 @@ export class DeltaQueueProxy<T>
78
74
  * Proxy to the real IDeltaManager - used to restrict access
79
75
  */
80
76
  export class DeltaManagerProxy
81
- extends EventForwarder<IDeltaManagerEvents>
77
+ extends DeltaManagerProxyBase
82
78
  implements IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>
83
79
  {
84
- public readonly inbound: IDeltaQueue<ISequencedDocumentMessage>;
85
- public readonly outbound: IDeltaQueue<IDocumentMessage[]>;
86
- public readonly inboundSignal: IDeltaQueue<ISignalMessage>;
87
-
88
- public get IDeltaSender(): IDeltaSender {
89
- return this;
90
- }
91
-
92
- public get minimumSequenceNumber(): number {
93
- return this.deltaManager.minimumSequenceNumber;
94
- }
95
-
96
- public get lastSequenceNumber(): number {
97
- return this.deltaManager.lastSequenceNumber;
98
- }
99
-
100
- public get lastMessage() {
101
- return this.deltaManager.lastMessage;
102
- }
103
-
104
- public get lastKnownSeqNumber() {
105
- return this.deltaManager.lastKnownSeqNumber;
106
- }
107
-
108
- public get initialSequenceNumber(): number {
109
- return this.deltaManager.initialSequenceNumber;
110
- }
111
-
112
- public get hasCheckpointSequenceNumber() {
113
- return this.deltaManager.hasCheckpointSequenceNumber;
80
+ public get inbound(): IDeltaQueue<ISequencedDocumentMessage> {
81
+ return this._inbound;
114
82
  }
83
+ private readonly _inbound: IDeltaQueue<ISequencedDocumentMessage>;
115
84
 
116
- public get clientDetails(): IClientDetails {
117
- return this.deltaManager.clientDetails;
85
+ public get outbound(): IDeltaQueue<IDocumentMessage[]> {
86
+ return this._outbound;
118
87
  }
88
+ private readonly _outbound: IDeltaQueue<IDocumentMessage[]>;
119
89
 
120
- public get version(): string {
121
- return this.deltaManager.version;
90
+ public get inboundSignal(): IDeltaQueue<ISignalMessage> {
91
+ return this._inboundSignal;
122
92
  }
93
+ private readonly _inboundSignal: IDeltaQueue<ISignalMessage>;
123
94
 
124
- public get maxMessageSize(): number {
125
- return this.deltaManager.maxMessageSize;
126
- }
127
-
128
- public get serviceConfiguration(): IClientConfiguration | undefined {
129
- return this.deltaManager.serviceConfiguration;
130
- }
131
-
132
- public get active(): boolean {
133
- return this.deltaManager.active;
134
- }
135
-
136
- public get readOnlyInfo(): ReadOnlyInfo {
137
- return this.deltaManager.readOnlyInfo;
138
- }
139
-
140
- constructor(
141
- private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
142
- ) {
95
+ constructor(deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>) {
143
96
  super(deltaManager);
144
97
 
145
- this.inbound = new DeltaQueueProxy(deltaManager.inbound);
146
- this.outbound = new DeltaQueueProxy(deltaManager.outbound);
147
- this.inboundSignal = new DeltaQueueProxy(deltaManager.inboundSignal);
98
+ this._inbound = new DeltaQueueProxy(deltaManager.inbound);
99
+ this._outbound = new DeltaQueueProxy(deltaManager.outbound);
100
+ this._inboundSignal = new DeltaQueueProxy(deltaManager.inboundSignal);
148
101
  }
149
102
 
150
103
  public dispose(): void {
151
- this.inbound.dispose();
152
- this.outbound.dispose();
153
- this.inboundSignal.dispose();
104
+ this._inbound.dispose();
105
+ this._outbound.dispose();
106
+ this._inboundSignal.dispose();
154
107
  super.dispose();
155
108
  }
156
-
157
- public submitSignal(content: any): void {
158
- return this.deltaManager.submitSignal(content);
159
- }
160
-
161
- public flush(): void {
162
- return this.deltaManager.flush();
163
- }
164
109
  }
package/src/index.ts CHANGED
@@ -5,12 +5,12 @@
5
5
 
6
6
  export { ConnectionState } from "./connectionState";
7
7
  export {
8
- Container,
9
- IContainerLoadOptions,
10
8
  IContainerConfig,
9
+ IContainerLoadOptions,
11
10
  IPendingContainerState,
12
11
  waitContainerToCatchUp,
13
12
  } from "./container";
13
+ export { ISerializableBlobContents } from "./containerStorageAdapter";
14
14
  export {
15
15
  ICodeDetailsLoader,
16
16
  IDetachedBlobStorage,
@@ -19,6 +19,6 @@ export {
19
19
  ILoaderProps,
20
20
  ILoaderServices,
21
21
  Loader,
22
- RelativeLoader,
22
+ requestResolvedObjectFromContainer,
23
23
  } from "./loader";
24
24
  export { IProtocolHandler, ProtocolHandlerBuilder } from "./protocol";