@fluidframework/container-loader 2.0.0-internal.5.1.1 → 2.0.0-internal.5.3.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 (145) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/catchUpMonitor.d.ts +1 -1
  3. package/dist/catchUpMonitor.d.ts.map +1 -1
  4. package/dist/catchUpMonitor.js.map +1 -1
  5. package/dist/connectionManager.d.ts +1 -1
  6. package/dist/connectionManager.d.ts.map +1 -1
  7. package/dist/connectionManager.js.map +1 -1
  8. package/dist/connectionStateHandler.d.ts +4 -1
  9. package/dist/connectionStateHandler.d.ts.map +1 -1
  10. package/dist/connectionStateHandler.js +10 -8
  11. package/dist/connectionStateHandler.js.map +1 -1
  12. package/dist/container.d.ts +30 -31
  13. package/dist/container.d.ts.map +1 -1
  14. package/dist/container.js +182 -109
  15. package/dist/container.js.map +1 -1
  16. package/dist/containerContext.d.ts +23 -66
  17. package/dist/containerContext.d.ts.map +1 -1
  18. package/dist/containerContext.js +28 -213
  19. package/dist/containerContext.js.map +1 -1
  20. package/dist/containerStorageAdapter.d.ts +1 -1
  21. package/dist/containerStorageAdapter.d.ts.map +1 -1
  22. package/dist/containerStorageAdapter.js +38 -6
  23. package/dist/containerStorageAdapter.js.map +1 -1
  24. package/dist/contracts.d.ts +1 -3
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/contracts.js.map +1 -1
  27. package/dist/deltaManager.d.ts +2 -1
  28. package/dist/deltaManager.d.ts.map +1 -1
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/disposal.d.ts +13 -0
  31. package/dist/disposal.d.ts.map +1 -0
  32. package/dist/disposal.js +25 -0
  33. package/dist/disposal.js.map +1 -0
  34. package/dist/loader.d.ts +1 -2
  35. package/dist/loader.d.ts.map +1 -1
  36. package/dist/loader.js.map +1 -1
  37. package/dist/noopHeuristic.d.ts +23 -0
  38. package/dist/noopHeuristic.d.ts.map +1 -0
  39. package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  40. package/dist/noopHeuristic.js.map +1 -0
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.js +1 -1
  43. package/dist/packageVersion.js.map +1 -1
  44. package/dist/protocol.d.ts +7 -12
  45. package/dist/protocol.d.ts.map +1 -1
  46. package/dist/protocol.js +17 -19
  47. package/dist/protocol.js.map +1 -1
  48. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  49. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  50. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  51. package/dist/quorum.d.ts +1 -17
  52. package/dist/quorum.d.ts.map +1 -1
  53. package/dist/quorum.js +1 -17
  54. package/dist/quorum.js.map +1 -1
  55. package/dist/retriableDocumentStorageService.d.ts +1 -1
  56. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  57. package/dist/retriableDocumentStorageService.js.map +1 -1
  58. package/lib/catchUpMonitor.d.ts +1 -1
  59. package/lib/catchUpMonitor.d.ts.map +1 -1
  60. package/lib/catchUpMonitor.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.map +1 -1
  64. package/lib/connectionStateHandler.d.ts +4 -1
  65. package/lib/connectionStateHandler.d.ts.map +1 -1
  66. package/lib/connectionStateHandler.js +10 -8
  67. package/lib/connectionStateHandler.js.map +1 -1
  68. package/lib/container.d.ts +30 -31
  69. package/lib/container.d.ts.map +1 -1
  70. package/lib/container.js +186 -113
  71. package/lib/container.js.map +1 -1
  72. package/lib/containerContext.d.ts +23 -66
  73. package/lib/containerContext.d.ts.map +1 -1
  74. package/lib/containerContext.js +28 -213
  75. package/lib/containerContext.js.map +1 -1
  76. package/lib/containerStorageAdapter.d.ts +1 -1
  77. package/lib/containerStorageAdapter.d.ts.map +1 -1
  78. package/lib/containerStorageAdapter.js +38 -6
  79. package/lib/containerStorageAdapter.js.map +1 -1
  80. package/lib/contracts.d.ts +1 -3
  81. package/lib/contracts.d.ts.map +1 -1
  82. package/lib/contracts.js.map +1 -1
  83. package/lib/deltaManager.d.ts +2 -1
  84. package/lib/deltaManager.d.ts.map +1 -1
  85. package/lib/deltaManager.js.map +1 -1
  86. package/lib/disposal.d.ts +13 -0
  87. package/lib/disposal.d.ts.map +1 -0
  88. package/lib/disposal.js +21 -0
  89. package/lib/disposal.js.map +1 -0
  90. package/lib/loader.d.ts +1 -2
  91. package/lib/loader.d.ts.map +1 -1
  92. package/lib/loader.js.map +1 -1
  93. package/lib/noopHeuristic.d.ts +23 -0
  94. package/lib/noopHeuristic.d.ts.map +1 -0
  95. package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  96. package/lib/noopHeuristic.js.map +1 -0
  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 +7 -12
  101. package/lib/protocol.d.ts.map +1 -1
  102. package/lib/protocol.js +15 -18
  103. package/lib/protocol.js.map +1 -1
  104. package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
  105. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  106. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  107. package/lib/quorum.d.ts +1 -17
  108. package/lib/quorum.d.ts.map +1 -1
  109. package/lib/quorum.js +1 -16
  110. package/lib/quorum.js.map +1 -1
  111. package/lib/retriableDocumentStorageService.d.ts +1 -1
  112. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  113. package/lib/retriableDocumentStorageService.js.map +1 -1
  114. package/package.json +18 -14
  115. package/src/catchUpMonitor.ts +1 -1
  116. package/src/connectionManager.ts +1 -1
  117. package/src/connectionStateHandler.ts +15 -10
  118. package/src/container.ts +284 -139
  119. package/src/containerContext.ts +33 -335
  120. package/src/containerStorageAdapter.ts +47 -5
  121. package/src/contracts.ts +1 -3
  122. package/src/deltaManager.ts +15 -8
  123. package/src/disposal.ts +25 -0
  124. package/src/loader.ts +1 -1
  125. package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
  126. package/src/packageVersion.ts +1 -1
  127. package/src/protocol.ts +18 -39
  128. package/src/protocolTreeDocumentStorageService.ts +1 -1
  129. package/src/quorum.ts +2 -31
  130. package/src/retriableDocumentStorageService.ts +2 -1
  131. package/dist/collabWindowTracker.d.ts +0 -19
  132. package/dist/collabWindowTracker.d.ts.map +0 -1
  133. package/dist/collabWindowTracker.js.map +0 -1
  134. package/dist/deltaManagerProxy.d.ts +0 -42
  135. package/dist/deltaManagerProxy.d.ts.map +0 -1
  136. package/dist/deltaManagerProxy.js +0 -79
  137. package/dist/deltaManagerProxy.js.map +0 -1
  138. package/lib/collabWindowTracker.d.ts +0 -19
  139. package/lib/collabWindowTracker.d.ts.map +0 -1
  140. package/lib/collabWindowTracker.js.map +0 -1
  141. package/lib/deltaManagerProxy.d.ts +0 -42
  142. package/lib/deltaManagerProxy.d.ts.map +0 -1
  143. package/lib/deltaManagerProxy.js +0 -74
  144. package/lib/deltaManagerProxy.js.map +0 -1
  145. package/src/deltaManagerProxy.ts +0 -109
@@ -3,159 +3,66 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryLoggerExt, PerformanceEvent } from "@fluidframework/telemetry-utils";
7
- import { assert, LazyPromise, TypedEventEmitter } from "@fluidframework/common-utils";
6
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
8
7
  import {
9
8
  IAudience,
10
9
  IContainerContext,
11
10
  IDeltaManager,
12
11
  ILoader,
13
- IRuntime,
14
12
  ICriticalContainerError,
15
13
  AttachState,
16
14
  ILoaderOptions,
17
- IRuntimeFactory,
18
- IProvideRuntimeFactory,
19
15
  IFluidCodeDetails,
20
- IFluidCodeDetailsComparer,
21
- IProvideFluidCodeDetailsComparer,
22
- ICodeDetailsLoader,
23
- IFluidModuleWithDetails,
24
16
  IBatchMessage,
25
17
  } from "@fluidframework/container-definitions";
26
- import { IRequest, IResponse, FluidObject } from "@fluidframework/core-interfaces";
18
+ import { FluidObject } from "@fluidframework/core-interfaces";
27
19
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
28
20
  import {
29
21
  IClientConfiguration,
30
22
  IClientDetails,
31
23
  IDocumentMessage,
32
- IQuorum,
33
24
  IQuorumClients,
34
25
  ISequencedDocumentMessage,
35
- ISignalMessage,
36
26
  ISnapshotTree,
37
- ISummaryTree,
38
27
  IVersion,
39
28
  MessageType,
40
29
  ISummaryContent,
41
30
  } from "@fluidframework/protocol-definitions";
42
- import { UsageError } from "@fluidframework/container-utils";
43
- import { Container } from "./container";
44
-
45
- const PackageNotFactoryError = "Code package does not implement IRuntimeFactory";
46
31
 
47
32
  /**
48
- * Events that {@link ContainerContext} can emit through its lifecycle.
49
- *
50
- * "runtimeInstantiated" - When an {@link @fluidframework/container-definitions#IRuntime} has been instantiated (by
51
- * calling instantiateRuntime() on the runtime factory), and this._runtime is set.
52
- *
53
- * "disposed" - When its dispose() method is called. The {@link ContainerContext} is no longer usable at that point.
33
+ * {@inheritDoc @fluidframework/container-definitions#IContainerContext}
54
34
  */
55
- type ContextLifecycleEvents = "runtimeInstantiated" | "disposed";
56
-
57
35
  export class ContainerContext implements IContainerContext {
58
- public static async createOrLoad(
59
- container: Container,
60
- scope: FluidObject,
61
- codeLoader: ICodeDetailsLoader,
62
- codeDetails: IFluidCodeDetails,
63
- baseSnapshot: ISnapshotTree | undefined,
64
- deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
65
- quorum: IQuorum,
66
- loader: ILoader,
67
- submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
68
- submitSummaryFn: (summaryOp: ISummaryContent, referenceSequenceNumber?: number) => number,
69
- submitBatchFn: (batch: IBatchMessage[], referenceSequenceNumber?: number) => number,
70
- submitSignalFn: (contents: any) => void,
71
- disposeFn: (error?: ICriticalContainerError) => void,
72
- closeFn: (error?: ICriticalContainerError) => void,
73
- version: string,
74
- updateDirtyContainerState: (dirty: boolean) => void,
75
- existing: boolean,
76
- pendingLocalState?: unknown,
77
- ): Promise<ContainerContext> {
78
- const context = new ContainerContext(
79
- container,
80
- scope,
81
- codeLoader,
82
- codeDetails,
83
- baseSnapshot,
84
- deltaManager,
85
- quorum,
86
- loader,
87
- submitFn,
88
- submitSummaryFn,
89
- submitBatchFn,
90
- submitSignalFn,
91
- disposeFn,
92
- closeFn,
93
- version,
94
- updateDirtyContainerState,
95
- existing,
96
- pendingLocalState,
97
- );
98
- await context.instantiateRuntime(existing);
99
- return context;
100
- }
101
-
102
- public readonly taggedLogger: ITelemetryLoggerExt;
103
- public readonly supportedFeatures: ReadonlyMap<string, unknown>;
36
+ public readonly supportedFeatures: ReadonlyMap<string, unknown> = new Map([
37
+ /**
38
+ * This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
39
+ * as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
40
+ * This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
41
+ */
42
+ ["referenceSequenceNumbers", true],
43
+ ]);
104
44
 
105
45
  public get clientId(): string | undefined {
106
- return this.container.clientId;
46
+ return this._getClientId();
107
47
  }
108
48
 
109
49
  /**
110
50
  * DISCLAIMER: this id is only for telemetry purposes. Not suitable for any other usages.
111
51
  */
112
52
  public get id(): string {
113
- return this.container.resolvedUrl?.id ?? "";
114
- }
115
-
116
- public get clientDetails(): IClientDetails {
117
- return this.container.clientDetails;
53
+ return this._getContainerDiagnosticId() ?? "";
118
54
  }
119
55
 
120
- private _connected: boolean;
121
56
  /**
122
57
  * When true, ops are free to flow
123
58
  * When false, ops should be kept as pending or rejected
124
59
  */
125
60
  public get connected(): boolean {
126
- return this._connected;
127
- }
128
-
129
- public get canSummarize(): boolean {
130
- return "summarize" in this.runtime;
61
+ return this._getConnected();
131
62
  }
132
63
 
133
64
  public get serviceConfiguration(): IClientConfiguration | undefined {
134
- return this.container.serviceConfiguration;
135
- }
136
-
137
- public get audience(): IAudience {
138
- return this.container.audience;
139
- }
140
-
141
- public get options(): ILoaderOptions {
142
- return this.container.options;
143
- }
144
-
145
- public get baseSnapshot() {
146
- return this._baseSnapshot;
147
- }
148
-
149
- public get storage(): IDocumentStorageService {
150
- return this.container.storage;
151
- }
152
-
153
- private _runtime: IRuntime | undefined;
154
- private get runtime() {
155
- if (this._runtime === undefined) {
156
- throw new Error("Attempted to access runtime before it was defined");
157
- }
158
- return this._runtime;
65
+ return this._getServiceConfiguration();
159
66
  }
160
67
 
161
68
  private _disposed = false;
@@ -164,59 +71,15 @@ export class ContainerContext implements IContainerContext {
164
71
  return this._disposed;
165
72
  }
166
73
 
167
- public get codeDetails() {
168
- return this._codeDetails;
169
- }
170
-
171
- private readonly _quorum: IQuorum;
172
- public get quorum(): IQuorumClients {
173
- return this._quorum;
174
- }
175
-
176
- private readonly _fluidModuleP: Promise<IFluidModuleWithDetails>;
177
-
178
- /**
179
- * {@inheritDoc @fluidframework/container-definitions#IContainerContext.getEntryPoint}
180
- */
181
- public async getEntryPoint?(): Promise<FluidObject | undefined> {
182
- if (this._disposed) {
183
- throw new UsageError("The context is already disposed");
184
- }
185
- if (this._runtime !== undefined) {
186
- return this._runtime?.getEntryPoint?.();
187
- }
188
- return new Promise<FluidObject | undefined>((resolve, reject) => {
189
- const runtimeInstantiatedHandler = () => {
190
- assert(
191
- this._runtime !== undefined,
192
- 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */,
193
- );
194
- resolve(this._runtime.getEntryPoint?.());
195
- this.lifecycleEvents.off("disposed", disposedHandler);
196
- };
197
- const disposedHandler = () => {
198
- reject(new Error("ContainerContext was disposed"));
199
- this.lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
200
- };
201
- this.lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
202
- this.lifecycleEvents.once("disposed", disposedHandler);
203
- });
204
- }
205
-
206
- /**
207
- * Emits events about the container context's lifecycle.
208
- * Use it to coordinate things inside the ContainerContext class.
209
- */
210
- private readonly lifecycleEvents = new TypedEventEmitter<ContextLifecycleEvents>();
211
-
212
74
  constructor(
213
- private readonly container: Container,
75
+ public readonly options: ILoaderOptions,
214
76
  public readonly scope: FluidObject,
215
- private readonly codeLoader: ICodeDetailsLoader,
216
- private readonly _codeDetails: IFluidCodeDetails,
217
- private readonly _baseSnapshot: ISnapshotTree | undefined,
77
+ public readonly baseSnapshot: ISnapshotTree | undefined,
78
+ private readonly _version: IVersion | undefined,
218
79
  public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
219
- quorum: IQuorum,
80
+ public readonly storage: IDocumentStorageService,
81
+ public readonly quorum: IQuorumClients,
82
+ public readonly audience: IAudience,
220
83
  public readonly loader: ILoader,
221
84
  public readonly submitFn: (
222
85
  type: MessageType,
@@ -236,194 +99,29 @@ export class ContainerContext implements IContainerContext {
236
99
  public readonly submitSignalFn: (contents: any) => void,
237
100
  public readonly disposeFn: (error?: ICriticalContainerError) => void,
238
101
  public readonly closeFn: (error?: ICriticalContainerError) => void,
239
- public readonly version: string,
240
102
  public readonly updateDirtyContainerState: (dirty: boolean) => void,
103
+ public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
104
+ private readonly _getContainerDiagnosticId: () => string | undefined,
105
+ private readonly _getClientId: () => string | undefined,
106
+ private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
107
+ private readonly _getAttachState: () => AttachState,
108
+ private readonly _getConnected: () => boolean,
109
+ public readonly getSpecifiedCodeDetails: () => IFluidCodeDetails | undefined,
110
+ public readonly clientDetails: IClientDetails,
241
111
  public readonly existing: boolean,
112
+ public readonly taggedLogger: ITelemetryLoggerExt,
242
113
  public readonly pendingLocalState?: unknown,
243
- ) {
244
- this._connected = this.container.connected;
245
- this._quorum = quorum;
246
- this.taggedLogger = container.subLogger;
247
- this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(async () =>
248
- this.loadCodeModule(_codeDetails),
249
- );
250
-
251
- this.supportedFeatures = new Map([
252
- /**
253
- * This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
254
- * as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
255
- * This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
256
- */
257
- ["referenceSequenceNumbers", true],
258
- ]);
259
- this.attachListener();
260
- }
261
-
262
- /**
263
- * @deprecated Temporary migratory API, to be removed when customers no longer need it.
264
- * When removed, `ContainerContext` should only take an {@link @fluidframework/container-definitions#IQuorumClients}
265
- * rather than an {@link @fluidframework/protocol-definitions#IQuorum}.
266
- * See {@link @fluidframework/container-definitions#IContainerContext} for more details.
267
- */
268
- public getSpecifiedCodeDetails(): IFluidCodeDetails | undefined {
269
- return (this._quorum.get("code") ?? this._quorum.get("code2")) as
270
- | IFluidCodeDetails
271
- | undefined;
272
- }
114
+ ) {}
273
115
 
274
116
  public dispose(error?: Error): void {
275
- if (this._disposed) {
276
- return;
277
- }
278
117
  this._disposed = true;
279
-
280
- this.lifecycleEvents.emit("disposed");
281
- this.runtime.dispose(error);
282
- this._quorum.dispose();
283
- this.deltaManager.dispose();
284
118
  }
285
119
 
286
120
  public getLoadedFromVersion(): IVersion | undefined {
287
- return this.container.loadedFromVersion;
121
+ return this._version;
288
122
  }
289
123
 
290
124
  public get attachState(): AttachState {
291
- return this.container.attachState;
292
- }
293
-
294
- /**
295
- * Create a summary. Used when attaching or serializing a detached container.
296
- *
297
- * @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
298
- * using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
299
- * new storage IDs so requests can be redirected.
300
- */
301
- public createSummary(blobRedirectTable?: Map<string, string>): ISummaryTree {
302
- return this.runtime.createSummary(blobRedirectTable);
303
- }
304
-
305
- public setConnectionState(connected: boolean, clientId?: string) {
306
- const runtime = this.runtime;
307
- this._connected = connected;
308
- runtime.setConnectionState(connected, clientId);
309
- }
310
-
311
- public process(message: ISequencedDocumentMessage, local: boolean) {
312
- this.runtime.process(message, local);
313
- }
314
-
315
- public processSignal(message: ISignalMessage, local: boolean) {
316
- this.runtime.processSignal(message, local);
317
- }
318
-
319
- public async request(path: IRequest): Promise<IResponse> {
320
- return this.runtime.request(path);
321
- }
322
-
323
- public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
324
- return this.container.getAbsoluteUrl(relativeUrl);
325
- }
326
-
327
- public getPendingLocalState(): unknown {
328
- return this.runtime.getPendingLocalState();
329
- }
330
-
331
- /**
332
- * Determines if the current code details of the context
333
- * satisfy the incoming constraint code details
334
- */
335
- public async satisfies(constraintCodeDetails: IFluidCodeDetails) {
336
- const comparers: IFluidCodeDetailsComparer[] = [];
337
-
338
- const maybeCompareCodeLoader = this.codeLoader;
339
- if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
340
- comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
341
- }
342
-
343
- const moduleWithDetails = await this._fluidModuleP;
344
- const maybeCompareExport: Partial<IProvideFluidCodeDetailsComparer> | undefined =
345
- moduleWithDetails.module?.fluidExport;
346
- if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
347
- comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
348
- }
349
-
350
- // if there are not comparers it is not possible to know
351
- // if the current satisfy the incoming, so return false,
352
- // as assuming they do not satisfy is safer .e.g we will
353
- // reload, rather than potentially running with
354
- // incompatible code
355
- if (comparers.length === 0) {
356
- return false;
357
- }
358
-
359
- for (const comparer of comparers) {
360
- const satisfies = await comparer.satisfies(
361
- moduleWithDetails.details,
362
- constraintCodeDetails,
363
- );
364
- if (satisfies === false) {
365
- return false;
366
- }
367
- }
368
- return true;
369
- }
370
-
371
- public async notifyOpReplay(message: ISequencedDocumentMessage): Promise<void> {
372
- return this.runtime.notifyOpReplay?.(message);
373
- }
374
-
375
- // #region private
376
-
377
- private async getRuntimeFactory(): Promise<IRuntimeFactory> {
378
- const fluidExport: FluidObject<IProvideRuntimeFactory> | undefined = (
379
- await this._fluidModuleP
380
- ).module?.fluidExport;
381
- const runtimeFactory = fluidExport?.IRuntimeFactory;
382
- if (runtimeFactory === undefined) {
383
- throw new Error(PackageNotFactoryError);
384
- }
385
-
386
- return runtimeFactory;
387
- }
388
-
389
- private async instantiateRuntime(existing: boolean) {
390
- const runtimeFactory = await this.getRuntimeFactory();
391
- this._runtime = await PerformanceEvent.timedExecAsync(
392
- this.taggedLogger,
393
- { eventName: "InstantiateRuntime" },
394
- async () => runtimeFactory.instantiateRuntime(this, existing),
395
- );
396
- this.lifecycleEvents.emit("runtimeInstantiated");
397
- }
398
-
399
- private attachListener() {
400
- this.container.once("attaching", () => {
401
- this.runtime.setAttachState(AttachState.Attaching);
402
- });
403
- this.container.once("attached", () => {
404
- this.runtime.setAttachState(AttachState.Attached);
405
- });
406
- }
407
-
408
- private async loadCodeModule(codeDetails: IFluidCodeDetails): Promise<IFluidModuleWithDetails> {
409
- const loadCodeResult = await PerformanceEvent.timedExecAsync(
410
- this.taggedLogger,
411
- { eventName: "CodeLoad" },
412
- async () => this.codeLoader.load(codeDetails),
413
- );
414
-
415
- if ("module" in loadCodeResult) {
416
- const { module, details } = loadCodeResult;
417
- return {
418
- module,
419
- details: details ?? codeDetails,
420
- };
421
- } else {
422
- // If "module" is not in the result, we are using a legacy ICodeLoader. Fix the result up with details.
423
- // Once usage drops to 0 we can remove this compat path.
424
- this.taggedLogger.sendTelemetryEvent({ eventName: "LegacyCodeLoader" });
425
- return loadCodeResult;
426
- }
125
+ return this._getAttachState();
427
126
  }
428
- // #endregion
429
127
  }
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IDisposable } from "@fluidframework/common-definitions";
6
+ import { IDisposable } from "@fluidframework/core-interfaces";
7
7
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
8
8
  import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
9
9
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
@@ -229,6 +229,14 @@ class BlobOnlyStorage implements IDocumentStorageService {
229
229
  }
230
230
  }
231
231
 
232
+ // runtime will write a tree to the summary containing "attachment" type entries
233
+ // which reference attachment blobs by ID, along with a blob containing the blob redirect table.
234
+ // However, some drivers do not support the "attachment" type and will convert them to "blob" type
235
+ // entries. We want to avoid saving these to reduce the size of stashed change blobs, but we
236
+ // need to make sure the blob redirect table is saved.
237
+ const blobsTreeName = ".blobs";
238
+ const redirectTableBlobName = ".redirectTable";
239
+
232
240
  /**
233
241
  * Get blob contents of a snapshot tree from storage (or, ideally, cache)
234
242
  */
@@ -245,10 +253,15 @@ async function getBlobContentsFromTreeCore(
245
253
  tree: ISnapshotTree,
246
254
  blobs: ISerializableBlobContents,
247
255
  storage: IDocumentStorageService,
256
+ root = true,
248
257
  ) {
249
258
  const treePs: Promise<any>[] = [];
250
- for (const subTree of Object.values(tree.trees)) {
251
- treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage));
259
+ for (const [key, subTree] of Object.entries(tree.trees)) {
260
+ if (root && key === blobsTreeName) {
261
+ treePs.push(getBlobManagerTreeFromTree(subTree, blobs, storage));
262
+ } else {
263
+ treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage, false));
264
+ }
252
265
  }
253
266
  for (const id of Object.values(tree.blobs)) {
254
267
  const blob = await storage.readBlob(id);
@@ -258,6 +271,18 @@ async function getBlobContentsFromTreeCore(
258
271
  return Promise.all(treePs);
259
272
  }
260
273
 
274
+ // save redirect table from .blobs tree but nothing else
275
+ async function getBlobManagerTreeFromTree(
276
+ tree: ISnapshotTree,
277
+ blobs: ISerializableBlobContents,
278
+ storage: IDocumentStorageService,
279
+ ) {
280
+ const id = tree.blobs[redirectTableBlobName];
281
+ const blob = await storage.readBlob(id);
282
+ // ArrayBufferLike will not survive JSON.stringify()
283
+ blobs[id] = bufferToString(blob, "utf8");
284
+ }
285
+
261
286
  /**
262
287
  * Extract blob contents from a snapshot tree with blob contents
263
288
  */
@@ -272,9 +297,14 @@ export function getBlobContentsFromTreeWithBlobContents(
272
297
  function getBlobContentsFromTreeWithBlobContentsCore(
273
298
  tree: ISnapshotTreeWithBlobContents,
274
299
  blobs: ISerializableBlobContents,
300
+ root = true,
275
301
  ) {
276
- for (const subTree of Object.values(tree.trees)) {
277
- getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs);
302
+ for (const [key, subTree] of Object.entries(tree.trees)) {
303
+ if (root && key === blobsTreeName) {
304
+ getBlobManagerTreeFromTreeWithBlobContents(subTree, blobs);
305
+ } else {
306
+ getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
307
+ }
278
308
  }
279
309
  for (const id of Object.values(tree.blobs)) {
280
310
  const blob = tree.blobsContents[id];
@@ -283,3 +313,15 @@ function getBlobContentsFromTreeWithBlobContentsCore(
283
313
  blobs[id] = bufferToString(blob, "utf8");
284
314
  }
285
315
  }
316
+
317
+ // save redirect table from .blobs tree but nothing else
318
+ function getBlobManagerTreeFromTreeWithBlobContents(
319
+ tree: ISnapshotTreeWithBlobContents,
320
+ blobs: ISerializableBlobContents,
321
+ ) {
322
+ const id = tree.blobs[redirectTableBlobName];
323
+ const blob = tree.blobsContents[id];
324
+ assert(blob !== undefined, 0x70f /* Blob must be present in blobsContents */);
325
+ // ArrayBufferLike will not survive JSON.stringify()
326
+ blobs[id] = bufferToString(blob, "utf8");
327
+ }
package/src/contracts.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryProperties } from "@fluidframework/common-definitions";
6
+ import { ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import {
8
8
  IDeltaQueue,
9
9
  ReadOnlyInfo,
@@ -148,8 +148,6 @@ export interface IConnectionManagerFactoryArgs {
148
148
 
149
149
  /**
150
150
  * Called whenever ping/pong messages are roundtripped on connection.
151
- *
152
- * @deprecated No replacement API intended.
153
151
  */
154
152
  readonly pongHandler: (latency: number) => void;
155
153
 
@@ -5,11 +5,8 @@
5
5
 
6
6
  import { default as AbortController } from "abort-controller";
7
7
  import { v4 as uuid } from "uuid";
8
- import {
9
- IEventProvider,
10
- ITelemetryProperties,
11
- ITelemetryErrorEvent,
12
- } from "@fluidframework/common-definitions";
8
+ import { IEventProvider } from "@fluidframework/common-definitions";
9
+ import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
13
10
  import {
14
11
  IDeltaHandlerStrategy,
15
12
  IDeltaManager,
@@ -70,6 +67,13 @@ export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
70
67
  (event: "cancelEstablishingConnection", listener: (reason: string) => void);
71
68
  }
72
69
 
70
+ /**
71
+ * Batching makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.
72
+ */
73
+ interface IBatchMetadata {
74
+ batch?: boolean;
75
+ }
76
+
73
77
  /**
74
78
  * Determines if message was sent by client, not service
75
79
  */
@@ -293,13 +297,16 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
293
297
 
294
298
  if (batch.length === 1) {
295
299
  assert(
296
- batch[0].metadata?.batch === undefined,
300
+ (batch[0].metadata as IBatchMetadata)?.batch === undefined,
297
301
  0x3c9 /* no batch markup on single message */,
298
302
  );
299
303
  } else {
300
- assert(batch[0].metadata?.batch === true, 0x3ca /* no start batch markup */);
301
304
  assert(
302
- batch[batch.length - 1].metadata?.batch === false,
305
+ (batch[0].metadata as IBatchMetadata)?.batch === true,
306
+ 0x3ca /* no start batch markup */,
307
+ );
308
+ assert(
309
+ (batch[batch.length - 1].metadata as IBatchMetadata)?.batch === false,
303
310
  0x3cb /* no end batch markup */,
304
311
  );
305
312
  }
@@ -0,0 +1,25 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IDisposable } from "@fluidframework/common-definitions";
7
+
8
+ /**
9
+ * Returns a wrapper around the provided function, which will only invoke the inner function if the provided
10
+ * {@link @fluidframework/common-definitions#IDisposable | disposable} object has not yet been disposed.
11
+ *
12
+ * @throws Will throw an error if the item has already been disposed.
13
+ */
14
+ export function doIfNotDisposed<T>(
15
+ disposable: IDisposable,
16
+ f: (...args: any[]) => T,
17
+ ): (...args: any[]) => T {
18
+ return (...args: any[]): T => {
19
+ if (disposable.disposed) {
20
+ throw new Error("Already disposed");
21
+ } else {
22
+ return f(...args);
23
+ }
24
+ };
25
+ }
package/src/loader.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import { v4 as uuid } from "uuid";
7
- import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
8
7
  import {
9
8
  ITelemetryLoggerExt,
10
9
  ChildLogger,
@@ -17,6 +16,7 @@ import {
17
16
  sessionStorageConfigProvider,
18
17
  } from "@fluidframework/telemetry-utils";
19
18
  import {
19
+ ITelemetryBaseLogger,
20
20
  FluidObject,
21
21
  IFluidRouter,
22
22
  IRequest,