@fluidframework/container-loader 2.0.0-internal.5.4.0 → 2.0.0-internal.6.1.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 (102) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/dist/connectionManager.d.ts +4 -4
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +57 -49
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/connectionStateHandler.d.ts +15 -14
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +26 -28
  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 +183 -134
  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 +20 -7
  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 +19 -6
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +67 -28
  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 +4 -2
  40. package/dist/protocol.d.ts.map +1 -1
  41. package/dist/protocol.js +25 -4
  42. package/dist/protocol.js.map +1 -1
  43. package/dist/utils.d.ts +8 -1
  44. package/dist/utils.d.ts.map +1 -1
  45. package/dist/utils.js +24 -6
  46. package/dist/utils.js.map +1 -1
  47. package/lib/connectionManager.d.ts +4 -4
  48. package/lib/connectionManager.d.ts.map +1 -1
  49. package/lib/connectionManager.js +58 -50
  50. package/lib/connectionManager.js.map +1 -1
  51. package/lib/connectionStateHandler.d.ts +15 -14
  52. package/lib/connectionStateHandler.d.ts.map +1 -1
  53. package/lib/connectionStateHandler.js +26 -28
  54. package/lib/connectionStateHandler.js.map +1 -1
  55. package/lib/container.d.ts +10 -5
  56. package/lib/container.d.ts.map +1 -1
  57. package/lib/container.js +182 -133
  58. package/lib/container.js.map +1 -1
  59. package/lib/containerContext.d.ts +2 -12
  60. package/lib/containerContext.d.ts.map +1 -1
  61. package/lib/containerContext.js +1 -20
  62. package/lib/containerContext.js.map +1 -1
  63. package/lib/containerStorageAdapter.js +3 -5
  64. package/lib/containerStorageAdapter.js.map +1 -1
  65. package/lib/contracts.d.ts +20 -7
  66. package/lib/contracts.d.ts.map +1 -1
  67. package/lib/contracts.js +3 -3
  68. package/lib/contracts.js.map +1 -1
  69. package/lib/debugLogger.js +2 -3
  70. package/lib/debugLogger.js.map +1 -1
  71. package/lib/deltaManager.d.ts +19 -6
  72. package/lib/deltaManager.d.ts.map +1 -1
  73. package/lib/deltaManager.js +67 -28
  74. package/lib/deltaManager.js.map +1 -1
  75. package/lib/deltaQueue.js +1 -2
  76. package/lib/deltaQueue.js.map +1 -1
  77. package/lib/loader.d.ts +12 -0
  78. package/lib/loader.d.ts.map +1 -1
  79. package/lib/loader.js +57 -42
  80. package/lib/loader.js.map +1 -1
  81. package/lib/packageVersion.d.ts +1 -1
  82. package/lib/packageVersion.js +1 -1
  83. package/lib/packageVersion.js.map +1 -1
  84. package/lib/protocol.d.ts +4 -2
  85. package/lib/protocol.d.ts.map +1 -1
  86. package/lib/protocol.js +25 -4
  87. package/lib/protocol.js.map +1 -1
  88. package/lib/utils.d.ts +8 -1
  89. package/lib/utils.d.ts.map +1 -1
  90. package/lib/utils.js +22 -5
  91. package/lib/utils.js.map +1 -1
  92. package/package.json +15 -19
  93. package/src/connectionManager.ts +53 -34
  94. package/src/connectionStateHandler.ts +30 -37
  95. package/src/container.ts +156 -76
  96. package/src/containerContext.ts +0 -24
  97. package/src/contracts.ts +27 -10
  98. package/src/deltaManager.ts +41 -18
  99. package/src/loader.ts +37 -23
  100. package/src/packageVersion.ts +1 -1
  101. package/src/protocol.ts +33 -2
  102. package/src/utils.ts +29 -0
package/lib/container.js CHANGED
@@ -8,18 +8,18 @@ import { v4 as uuid } from "uuid";
8
8
  import { TypedEventEmitter, assert, performance, unreachableCase, } from "@fluidframework/common-utils";
9
9
  import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
10
10
  import { GenericError, UsageError } from "@fluidframework/container-utils";
11
- import { readAndParse, OnlineStatus, isOnline, combineAppAndProtocolSummary, runWithRetry, isCombinedAppAndProtocolSummary, MessageType2, canBeCoalescedByService, } from "@fluidframework/driver-utils";
11
+ import { readAndParse, OnlineStatus, isOnline, runWithRetry, isCombinedAppAndProtocolSummary, MessageType2, } from "@fluidframework/driver-utils";
12
12
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
13
13
  import { createChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, connectedEventName, normalizeError, createChildMonitoringContext, wrapError, formatTick, } from "@fluidframework/telemetry-utils";
14
14
  import { Audience } from "./audience";
15
15
  import { ContainerContext } from "./containerContext";
16
- import { ReconnectMode, getPackageName } from "./contracts";
16
+ import { ReconnectMode, getPackageName, } from "./contracts";
17
17
  import { DeltaManager } from "./deltaManager";
18
18
  import { RelativeLoader } from "./loader";
19
19
  import { pkgVersion } from "./packageVersion";
20
20
  import { ContainerStorageAdapter, getBlobContentsFromTree, getBlobContentsFromTreeWithBlobContents, } from "./containerStorageAdapter";
21
21
  import { createConnectionStateHandler } from "./connectionStateHandler";
22
- import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
22
+ import { combineAppAndProtocolSummary, getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer, } from "./utils";
23
23
  import { initQuorumValuesFromCodeDetails } from "./quorum";
24
24
  import { NoopHeuristic } from "./noopHeuristic";
25
25
  import { ConnectionManager } from "./connectionManager";
@@ -104,7 +104,7 @@ export async function waitContainerToCatchUp(container) {
104
104
  }
105
105
  const getCodeProposal =
106
106
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
107
- (quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
107
+ (quorum) => quorum.get("code") ?? quorum.get("code2");
108
108
  /**
109
109
  * Helper function to report to telemetry cases where operation takes longer than expected (200ms)
110
110
  * @param logger - logger to use
@@ -124,7 +124,6 @@ export class Container extends EventEmitterWithErrorHandling {
124
124
  * @internal
125
125
  */
126
126
  constructor(createProps, loadProps) {
127
- var _a;
128
127
  super((name, error) => {
129
128
  this.mc.logger.sendErrorEvent({
130
129
  eventName: "ContainerEventHandlerException",
@@ -174,8 +173,8 @@ export class Container extends EventEmitterWithErrorHandling {
174
173
  };
175
174
  const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
176
175
  this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
177
- const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
178
- this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
176
+ const pendingLocalState = loadProps?.pendingLocalState;
177
+ this._canReconnect = canReconnect ?? true;
179
178
  this.clientDetailsOverride = clientDetailsOverride;
180
179
  this.urlResolver = urlResolver;
181
180
  this.serviceFactory = documentServiceFactory;
@@ -183,14 +182,18 @@ export class Container extends EventEmitterWithErrorHandling {
183
182
  // Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
184
183
  // all clients that were loaded from the same loader (including summarizer clients).
185
184
  // Tracking alternative ways to handle this in AB#4129.
186
- this.options = Object.assign({}, options);
185
+ this.options = { ...options };
187
186
  this.scope = scope;
188
187
  this.detachedBlobStorage = detachedBlobStorage;
189
188
  this.protocolHandlerBuilder =
190
- protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new ProtocolHandler(...args, new Audience()));
189
+ protocolHandlerBuilder ??
190
+ ((attributes, quorumSnapshot, sendProposal) => new ProtocolHandler(attributes, quorumSnapshot, sendProposal, new Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
191
191
  // Note that we capture the createProps here so we can replicate the creation call when we want to clone.
192
192
  this.clone = async (_loadProps, createParamOverrides) => {
193
- return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
193
+ return Container.load(_loadProps, {
194
+ ...createProps,
195
+ ...createParamOverrides,
196
+ });
194
197
  };
195
198
  // Create logger for data stores to use
196
199
  const type = this.client.details.type;
@@ -204,7 +207,7 @@ export class Container extends EventEmitterWithErrorHandling {
204
207
  all: {
205
208
  clientType,
206
209
  containerId: uuid(),
207
- docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
210
+ docId: () => this.resolvedUrl?.id,
208
211
  containerAttachState: () => this._attachState,
209
212
  containerLifecycleState: () => this._lifecycleState,
210
213
  containerConnectionState: () => ConnectionState[this.connectionState],
@@ -215,22 +218,19 @@ export class Container extends EventEmitterWithErrorHandling {
215
218
  // specific error or class of errors
216
219
  error: {
217
220
  // load information to associate errors with the specific load point
218
- dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
219
- dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
220
- dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
221
- containerLoadedFromVersionId: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
222
- containerLoadedFromVersionDate: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
221
+ dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
222
+ dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
223
+ dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
224
+ containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
225
+ containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
223
226
  // message information to associate errors with the specific execution state
224
227
  // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
225
- dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
226
- dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
227
- dmLastMsqSeqClientId: () => {
228
- var _a, _b, _c, _d;
229
- return ((_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId) === null
230
- ? "null"
231
- : (_d = (_c = this.deltaManager) === null || _c === void 0 ? void 0 : _c.lastMessage) === null || _d === void 0 ? void 0 : _d.clientId;
232
- },
233
- dmLastMsgClientSeq: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientSequenceNumber; },
228
+ dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
229
+ dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
230
+ dmLastMsqSeqClientId: () => this.deltaManager?.lastMessage?.clientId === null
231
+ ? "null"
232
+ : this.deltaManager?.lastMessage?.clientId,
233
+ dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
234
234
  connectionStateDuration: () => performance.now() - this.connectionTransitionTimes[this.connectionState],
235
235
  },
236
236
  },
@@ -240,11 +240,11 @@ export class Container extends EventEmitterWithErrorHandling {
240
240
  this._deltaManager = this.createDeltaManager();
241
241
  this.connectionStateHandler = createConnectionStateHandler({
242
242
  logger: this.mc.logger,
243
- connectionStateChanged: (value, oldState, reason, error) => {
243
+ connectionStateChanged: (value, oldState, reason) => {
244
244
  if (value === ConnectionState.Connected) {
245
245
  this._clientId = this.connectionStateHandler.pendingClientId;
246
246
  }
247
- this.logConnectionStateChangeTelemetry(value, oldState, reason, error);
247
+ this.logConnectionStateChangeTelemetry(value, oldState, reason);
248
248
  if (this._lifecycleState === "loaded") {
249
249
  this.propagateConnectionState(false /* initial transition */, value === ConnectionState.Disconnected
250
250
  ? reason
@@ -260,9 +260,14 @@ export class Container extends EventEmitterWithErrorHandling {
260
260
  // Report issues only if we already loaded container - op processing is paused while container is loading,
261
261
  // so we always time-out processing of join op in cases where fetching snapshot takes a minute.
262
262
  // It's not a problem with op processing itself - such issues should be tracked as part of boot perf monitoring instead.
263
- this._deltaManager.logConnectionIssue(Object.assign({ eventName,
264
- mode, category: this._lifecycleState === "loading" ? "generic" : category, duration: performance.now() -
265
- this.connectionTransitionTimes[ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
263
+ this._deltaManager.logConnectionIssue({
264
+ eventName,
265
+ mode,
266
+ category: this._lifecycleState === "loading" ? "generic" : category,
267
+ duration: performance.now() -
268
+ this.connectionTransitionTimes[ConnectionState.CatchingUp],
269
+ ...(details === undefined ? {} : { details: JSON.stringify(details) }),
270
+ });
266
271
  // If this is "write" connection, it took too long to receive join op. But in most cases that's due
267
272
  // to very slow op fetches and we will eventually get there.
268
273
  // For "read" connections, we get here due to self join signal not arriving on time. We will need to
@@ -272,14 +277,15 @@ export class Container extends EventEmitterWithErrorHandling {
272
277
  // Other possible recovery path - move to connected state (i.e. ConnectionStateHandler.joinOpTimer
273
278
  // to call this.applyForConnectedState("addMemberEvent") for "read" connections)
274
279
  if (mode === "read") {
275
- this.disconnect();
276
- this.connect();
280
+ const reason = { text: "NoJoinSignal" };
281
+ this.disconnectInternal(reason);
282
+ this.connectInternal({ reason, fetchOpsFromStorage: false });
277
283
  }
278
284
  },
279
285
  clientShouldHaveLeft: (clientId) => {
280
286
  this.clientsWhoShouldHaveLeft.add(clientId);
281
287
  },
282
- }, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
288
+ }, this.deltaManager, pendingLocalState?.clientId);
283
289
  this.on(savedContainerEvent, () => {
284
290
  this.connectionStateHandler.containerSaved();
285
291
  });
@@ -291,8 +297,9 @@ export class Container extends EventEmitterWithErrorHandling {
291
297
  : combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
292
298
  // Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
293
299
  // Even if not forced on via this flag, combined summaries may still be enabled by service policy.
294
- const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
295
- this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
300
+ const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
301
+ options.summarizeProtocolTree;
302
+ this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
296
303
  const isDomAvailable = typeof document === "object" &&
297
304
  document !== null &&
298
305
  typeof document.addEventListener === "function" &&
@@ -319,7 +326,7 @@ export class Container extends EventEmitterWithErrorHandling {
319
326
  * @internal
320
327
  */
321
328
  static async load(loadProps, createProps) {
322
- const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
329
+ const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
323
330
  const container = new Container(createProps, loadProps);
324
331
  const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
325
332
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
@@ -327,19 +334,20 @@ export class Container extends EventEmitterWithErrorHandling {
327
334
  // if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
328
335
  // to return container, so ignore this value and use undefined for opsBeforeReturn
329
336
  const mode = pendingLocalState
330
- ? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
337
+ ? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
338
+ : loadMode ?? defaultMode;
331
339
  const onClosed = (err) => {
332
340
  // pre-0.58 error message: containerClosedWithoutErrorDuringLoad
333
- reject(err !== null && err !== void 0 ? err : new GenericError("Container closed without error during load"));
341
+ reject(err ?? new GenericError("Container closed without error during load"));
334
342
  };
335
343
  container.on("closed", onClosed);
336
344
  container
337
- .load(version, mode, resolvedUrl, pendingLocalState)
345
+ .load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
338
346
  .finally(() => {
339
347
  container.removeListener("closed", onClosed);
340
348
  })
341
349
  .then((props) => {
342
- event.end(Object.assign(Object.assign({}, props), loadMode));
350
+ event.end({ ...props, ...loadMode });
343
351
  resolve(container);
344
352
  }, (error) => {
345
353
  const err = normalizeError(error);
@@ -407,7 +415,6 @@ export class Container extends EventEmitterWithErrorHandling {
407
415
  return this;
408
416
  }
409
417
  get resolvedUrl() {
410
- var _a;
411
418
  /**
412
419
  * All attached containers will have a document service,
413
420
  * this is required, as attached containers are attached to
@@ -419,7 +426,7 @@ export class Container extends EventEmitterWithErrorHandling {
419
426
  * is always the same as the containers, as we had to
420
427
  * obtain the resolved url, and then create the service from it.
421
428
  */
422
- return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
429
+ return this.service?.resolvedUrl;
423
430
  }
424
431
  get readOnlyInfo() {
425
432
  return this._deltaManager.readOnlyInfo;
@@ -447,8 +454,8 @@ export class Container extends EventEmitterWithErrorHandling {
447
454
  return this._clientId;
448
455
  }
449
456
  get offlineLoadEnabled() {
450
- var _a, _b;
451
- const enabled = (_a = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) !== null && _a !== void 0 ? _a : ((_b = this.options) === null || _b === void 0 ? void 0 : _b.enableOfflineLoad) === true;
457
+ const enabled = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
458
+ this.options?.enableOfflineLoad === true;
452
459
  // summarizer will not have any pending state we want to save
453
460
  return enabled && this.deltaManager.clientDetails.capabilities.interactive;
454
461
  }
@@ -485,18 +492,16 @@ export class Container extends EventEmitterWithErrorHandling {
485
492
  * {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
486
493
  */
487
494
  async getEntryPoint() {
488
- var _a, _b;
489
495
  if (this._disposed) {
490
496
  throw new UsageError("The context is already disposed");
491
497
  }
492
498
  if (this._runtime !== undefined) {
493
- return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
499
+ return this._runtime.getEntryPoint?.();
494
500
  }
495
501
  return new Promise((resolve, reject) => {
496
502
  const runtimeInstantiatedHandler = () => {
497
- var _a, _b;
498
503
  assert(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
499
- resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
504
+ resolve(this._runtime.getEntryPoint?.());
500
505
  this._lifecycleEvents.off("disposed", disposedHandler);
501
506
  };
502
507
  const disposedHandler = () => {
@@ -530,7 +535,6 @@ export class Container extends EventEmitterWithErrorHandling {
530
535
  assert(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
531
536
  }
532
537
  closeCore(error) {
533
- var _a;
534
538
  assert(!this.closed, 0x315 /* re-entrancy */);
535
539
  try {
536
540
  // Ensure that we raise all key events even if one of these throws
@@ -546,7 +550,7 @@ export class Container extends EventEmitterWithErrorHandling {
546
550
  : "generic",
547
551
  }, error);
548
552
  this._lifecycleState = "closing";
549
- (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
553
+ this._protocolHandler?.close();
550
554
  this.connectionStateHandler.dispose();
551
555
  }
552
556
  catch (exception) {
@@ -566,7 +570,6 @@ export class Container extends EventEmitterWithErrorHandling {
566
570
  }
567
571
  }
568
572
  disposeCore(error) {
569
- var _a, _b, _c;
570
573
  assert(!this._disposed, 0x54c /* Container already disposed */);
571
574
  this._disposed = true;
572
575
  try {
@@ -583,15 +586,15 @@ export class Container extends EventEmitterWithErrorHandling {
583
586
  if (this._lifecycleState !== "closed") {
584
587
  this._lifecycleState = "disposing";
585
588
  }
586
- (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
589
+ this._protocolHandler?.close();
587
590
  this.connectionStateHandler.dispose();
588
591
  const maybeError = error !== undefined ? new Error(error.message) : undefined;
589
- (_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
592
+ this._runtime?.dispose(maybeError);
590
593
  this.storageAdapter.dispose();
591
594
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
592
595
  // about file, like file being overwritten in storage, but client having stale local cache.
593
596
  // Driver need to ensure all caches are cleared on critical errors
594
- (_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
597
+ this.service?.dispose(error);
595
598
  }
596
599
  catch (exception) {
597
600
  this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
@@ -607,15 +610,19 @@ export class Container extends EventEmitterWithErrorHandling {
607
610
  this._lifecycleEvents.emit("disposed");
608
611
  }
609
612
  }
610
- closeAndGetPendingLocalState() {
613
+ async closeAndGetPendingLocalState() {
611
614
  // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
612
615
  // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
613
616
  // a new clientId and a future container using stale pending state without the new clientId would resubmit them
614
- const pendingState = this.getPendingLocalState();
617
+ this.disconnectInternal({ text: "closeAndGetPendingLocalState" }); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
618
+ const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
615
619
  this.close();
616
620
  return pendingState;
617
621
  }
618
- getPendingLocalState() {
622
+ async getPendingLocalState() {
623
+ return this.getPendingLocalStateCore({ notifyImminentClosure: false });
624
+ }
625
+ async getPendingLocalStateCore(props) {
619
626
  if (!this.offlineLoadEnabled) {
620
627
  throw new UsageError("Can't get pending local state unless offline load is enabled");
621
628
  }
@@ -626,8 +633,9 @@ export class Container extends EventEmitterWithErrorHandling {
626
633
  assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
627
634
  assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
628
635
  assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
636
+ const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
629
637
  const pendingState = {
630
- pendingRuntimeState: this.runtime.getPendingLocalState(),
638
+ pendingRuntimeState,
631
639
  baseSnapshot: this.baseSnapshot,
632
640
  snapshotBlobs: this.baseSnapshotBlobs,
633
641
  savedOps: this.savedOps,
@@ -656,7 +664,6 @@ export class Container extends EventEmitterWithErrorHandling {
656
664
  }
657
665
  async attach(request) {
658
666
  await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
659
- var _a;
660
667
  if (this._lifecycleState !== "loaded") {
661
668
  // pre-0.58 error message: containerNotValidForAttach
662
669
  throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
@@ -741,14 +748,14 @@ export class Container extends EventEmitterWithErrorHandling {
741
748
  if (!this.closed) {
742
749
  this.resumeInternal({
743
750
  fetchOpsFromStorage: false,
744
- reason: "createDetached",
751
+ reason: { text: "createDetached" },
745
752
  });
746
753
  }
747
754
  }
748
755
  catch (error) {
749
756
  // add resolved URL on error object so that host has the ability to find this document and delete it
750
757
  const newError = normalizeError(error);
751
- newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
758
+ newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
752
759
  this.close(newError);
753
760
  throw newError;
754
761
  }
@@ -757,7 +764,7 @@ export class Container extends EventEmitterWithErrorHandling {
757
764
  async request(path) {
758
765
  return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
759
766
  }
760
- setAutoReconnectInternal(mode) {
767
+ setAutoReconnectInternal(mode, reason) {
761
768
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
762
769
  if (currentMode === mode) {
763
770
  return;
@@ -771,7 +778,7 @@ export class Container extends EventEmitterWithErrorHandling {
771
778
  connectionState: ConnectionState[this.connectionState],
772
779
  duration,
773
780
  });
774
- this._deltaManager.connectionManager.setAutoReconnect(mode);
781
+ this._deltaManager.connectionManager.setAutoReconnect(mode, reason);
775
782
  }
776
783
  connect() {
777
784
  if (this.closed) {
@@ -784,7 +791,10 @@ export class Container extends EventEmitterWithErrorHandling {
784
791
  // Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
785
792
  // If there is gap, we will learn about it once connected, but the gap should be small (if any),
786
793
  // assuming that connect() is called quickly after initial container boot.
787
- this.connectInternal({ reason: "DocumentConnect", fetchOpsFromStorage: false });
794
+ this.connectInternal({
795
+ reason: { text: "DocumentConnect" },
796
+ fetchOpsFromStorage: false,
797
+ });
788
798
  }
789
799
  }
790
800
  connectInternal(args) {
@@ -794,21 +804,21 @@ export class Container extends EventEmitterWithErrorHandling {
794
804
  this.resumeInternal(args);
795
805
  // Set Auto Reconnect Mode
796
806
  const mode = ReconnectMode.Enabled;
797
- this.setAutoReconnectInternal(mode);
807
+ this.setAutoReconnectInternal(mode, args.reason);
798
808
  }
799
809
  disconnect() {
800
810
  if (this.closed) {
801
811
  throw new UsageError(`The Container is closed and cannot be disconnected`);
802
812
  }
803
813
  else {
804
- this.disconnectInternal();
814
+ this.disconnectInternal({ text: "DocumentDisconnect" });
805
815
  }
806
816
  }
807
- disconnectInternal() {
817
+ disconnectInternal(reason) {
808
818
  assert(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
809
819
  // Set Auto Reconnect Mode
810
820
  const mode = ReconnectMode.Disabled;
811
- this.setAutoReconnectInternal(mode);
821
+ this.setAutoReconnectInternal(mode, reason);
812
822
  }
813
823
  resumeInternal(args) {
814
824
  assert(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
@@ -855,7 +865,6 @@ export class Container extends EventEmitterWithErrorHandling {
855
865
  * Determines if the currently loaded module satisfies the incoming constraint code details
856
866
  */
857
867
  async satisfies(constraintCodeDetails) {
858
- var _a, _b;
859
868
  // If we have no module, it can't satisfy anything.
860
869
  if (this._loadedModule === undefined) {
861
870
  return false;
@@ -865,8 +874,8 @@ export class Container extends EventEmitterWithErrorHandling {
865
874
  if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
866
875
  comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
867
876
  }
868
- const maybeCompareExport = (_a = this._loadedModule) === null || _a === void 0 ? void 0 : _a.module.fluidExport;
869
- if ((maybeCompareExport === null || maybeCompareExport === void 0 ? void 0 : maybeCompareExport.IFluidCodeDetailsComparer) !== undefined) {
877
+ const maybeCompareExport = this._loadedModule?.module.fluidExport;
878
+ if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
870
879
  comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
871
880
  }
872
881
  // If there are no comparers, then it's impossible to know if the currently loaded package satisfies
@@ -876,7 +885,7 @@ export class Container extends EventEmitterWithErrorHandling {
876
885
  return false;
877
886
  }
878
887
  for (const comparer of comparers) {
879
- const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
888
+ const satisfies = await comparer.satisfies(this._loadedModule?.details, constraintCodeDetails);
880
889
  if (satisfies === false) {
881
890
  return false;
882
891
  }
@@ -899,8 +908,7 @@ export class Container extends EventEmitterWithErrorHandling {
899
908
  *
900
909
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
901
910
  */
902
- async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
903
- var _a, _b, _c;
911
+ async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
904
912
  this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
905
913
  // Ideally we always connect as "read" by default.
906
914
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
@@ -912,7 +920,7 @@ export class Container extends EventEmitterWithErrorHandling {
912
920
  // A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
913
921
  // B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
914
922
  const connectionArgs = {
915
- reason: "DocumentOpen",
923
+ reason: { text: "DocumentOpen" },
916
924
  mode: "write",
917
925
  fetchOpsFromStorage: false,
918
926
  };
@@ -949,9 +957,51 @@ export class Container extends EventEmitterWithErrorHandling {
949
957
  }
950
958
  const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
951
959
  // If we saved ops, we will replay them and don't need DeltaManager to fetch them
952
- const sequenceNumber = (_a = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.savedOps[pendingLocalState.savedOps.length - 1]) === null || _a === void 0 ? void 0 : _a.sequenceNumber;
953
- const dmAttributes = sequenceNumber !== undefined ? Object.assign(Object.assign({}, attributes), { sequenceNumber }) : attributes;
960
+ const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
961
+ const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
954
962
  let opsBeforeReturnP;
963
+ if (loadMode.pauseAfterLoad === true) {
964
+ // If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
965
+ if (loadMode.opsBeforeReturn === "sequenceNumber") {
966
+ assert(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
967
+ // Note: It is possible that we think the latest snapshot is newer than the specified sequence number
968
+ // due to saved ops that may be replayed after the snapshot.
969
+ // https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
970
+ if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
971
+ throw new Error("Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.");
972
+ }
973
+ }
974
+ // Force readonly mode - this will ensure we don't receive an error for the lack of join op
975
+ this.forceReadonly(true);
976
+ // We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
977
+ const opHandler = () => {
978
+ if (loadToSequenceNumber === undefined) {
979
+ // If there is no specified sequence number, pause after the inbound queue is empty.
980
+ if (this.deltaManager.inbound.length !== 0) {
981
+ return;
982
+ }
983
+ }
984
+ else {
985
+ // If there is a specified sequence number, keep processing until we reach it.
986
+ if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
987
+ return;
988
+ }
989
+ }
990
+ // Pause op processing once we have processed the desired number of ops.
991
+ void this.deltaManager.inbound.pause();
992
+ void this.deltaManager.outbound.pause();
993
+ this.off("op", opHandler);
994
+ };
995
+ if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
996
+ this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
997
+ // If we have already reached the desired sequence number, call opHandler() to pause immediately.
998
+ opHandler();
999
+ }
1000
+ else {
1001
+ // If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
1002
+ this.on("op", opHandler);
1003
+ }
1004
+ }
955
1005
  // Attach op handlers to finish initialization and be able to start processing ops
956
1006
  // Kick off any ops fetching if required.
957
1007
  switch (loadMode.opsBeforeReturn) {
@@ -960,6 +1010,9 @@ export class Container extends EventEmitterWithErrorHandling {
960
1010
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
961
1011
  this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
962
1012
  break;
1013
+ case "sequenceNumber":
1014
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
1015
+ break;
963
1016
  case "cached":
964
1017
  opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
965
1018
  break;
@@ -973,18 +1026,18 @@ export class Container extends EventEmitterWithErrorHandling {
973
1026
  // Initialize the protocol handler
974
1027
  await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
975
1028
  const codeDetails = this.getCodeDetailsFromQuorum();
976
- await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
1029
+ await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState?.pendingRuntimeState);
977
1030
  // replay saved ops
978
1031
  if (pendingLocalState) {
979
1032
  for (const message of pendingLocalState.savedOps) {
980
1033
  this.processRemoteMessage(message);
981
1034
  // allow runtime to apply stashed ops at this op's sequence number
982
- await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
1035
+ await this.runtime.notifyOpReplay?.(message);
983
1036
  }
984
1037
  pendingLocalState.savedOps = [];
985
1038
  // now set clientId to stashed clientId so live ops are correctly processed as local
986
1039
  assert(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
987
- this._clientId = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId;
1040
+ this._clientId = pendingLocalState?.clientId;
988
1041
  }
989
1042
  // We might have hit some failure that did not manifest itself in exception in this flow,
990
1043
  // do not start op processing in such case - static version of Container.load() will handle it correctly.
@@ -1015,6 +1068,19 @@ export class Container extends EventEmitterWithErrorHandling {
1015
1068
  unreachableCase(loadMode.deltaConnection);
1016
1069
  }
1017
1070
  }
1071
+ // If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
1072
+ if (loadToSequenceNumber !== undefined &&
1073
+ this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
1074
+ await new Promise((resolve, reject) => {
1075
+ const opHandler = (message) => {
1076
+ if (message.sequenceNumber >= loadToSequenceNumber) {
1077
+ resolve();
1078
+ this.off("op", opHandler);
1079
+ }
1080
+ };
1081
+ this.on("op", opHandler);
1082
+ });
1083
+ }
1018
1084
  // Safety net: static version of Container.load() should have learned about it through "closed" handler.
1019
1085
  // But if that did not happen for some reason, fail load for sure.
1020
1086
  // Otherwise we can get into situations where container is closed and does not try to connect to ordering
@@ -1169,8 +1235,7 @@ export class Container extends EventEmitterWithErrorHandling {
1169
1235
  return pkg;
1170
1236
  }
1171
1237
  get client() {
1172
- var _a;
1173
- const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
1238
+ const client = this.options?.client !== undefined
1174
1239
  ? this.options.client
1175
1240
  : {
1176
1241
  details: {
@@ -1217,11 +1282,10 @@ export class Container extends EventEmitterWithErrorHandling {
1217
1282
  deltaManager.on("cancelEstablishingConnection", (reason) => {
1218
1283
  this.connectionStateHandler.cancelEstablishingConnection(reason);
1219
1284
  });
1220
- deltaManager.on("disconnect", (reason, error) => {
1221
- var _a;
1222
- (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
1285
+ deltaManager.on("disconnect", (reason) => {
1286
+ this.noopHeuristic?.notifyDisconnect();
1223
1287
  if (!this.closed) {
1224
- this.connectionStateHandler.receivedDisconnectEvent(reason, error);
1288
+ this.connectionStateHandler.receivedDisconnectEvent(reason);
1225
1289
  }
1226
1290
  });
1227
1291
  deltaManager.on("throttled", (warning) => {
@@ -1253,8 +1317,7 @@ export class Container extends EventEmitterWithErrorHandling {
1253
1317
  },
1254
1318
  }, prefetchType);
1255
1319
  }
1256
- logConnectionStateChangeTelemetry(value, oldState, reason, error) {
1257
- var _a;
1320
+ logConnectionStateChangeTelemetry(value, oldState, reason) {
1258
1321
  // Log actual event
1259
1322
  const time = performance.now();
1260
1323
  this.connectionTransitionTimes[value] = time;
@@ -1284,19 +1347,31 @@ export class Container extends EventEmitterWithErrorHandling {
1284
1347
  }
1285
1348
  connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
1286
1349
  }
1287
- this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
1350
+ this.mc.logger.sendPerformanceEvent({
1351
+ eventName: `ConnectionStateChange_${ConnectionState[value]}`,
1352
+ from: ConnectionState[oldState],
1353
+ duration,
1288
1354
  durationFromDisconnected,
1289
- reason,
1290
- connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
1291
- opsBehind, online: OnlineStatus[isOnline()], lastVisible: this.lastVisible !== undefined
1355
+ reason: reason?.text,
1356
+ connectionInitiationReason,
1357
+ pendingClientId: this.connectionStateHandler.pendingClientId,
1358
+ clientId: this.clientId,
1359
+ autoReconnect,
1360
+ opsBehind,
1361
+ online: OnlineStatus[isOnline()],
1362
+ lastVisible: this.lastVisible !== undefined
1292
1363
  ? performance.now() - this.lastVisible
1293
- : undefined, checkpointSequenceNumber, quorumSize: (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.quorum.getMembers().size, isDirty: this.isDirty }, this._deltaManager.connectionProps), error);
1364
+ : undefined,
1365
+ checkpointSequenceNumber,
1366
+ quorumSize: this._protocolHandler?.quorum.getMembers().size,
1367
+ isDirty: this.isDirty,
1368
+ ...this._deltaManager.connectionProps,
1369
+ }, reason?.error);
1294
1370
  if (value === ConnectionState.Connected) {
1295
1371
  this.firstConnection = false;
1296
1372
  }
1297
1373
  }
1298
1374
  propagateConnectionState(initialTransition, disconnectedReason) {
1299
- var _a;
1300
1375
  // When container loaded, we want to propagate initial connection state.
1301
1376
  // After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
1302
1377
  // This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
@@ -1307,9 +1382,9 @@ export class Container extends EventEmitterWithErrorHandling {
1307
1382
  }
1308
1383
  const state = this.connectionState === ConnectionState.Connected;
1309
1384
  // Both protocol and context should not be undefined if we got so far.
1310
- this.setContextConnectedState(state, (_a = this.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
1385
+ this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
1311
1386
  this.protocolHandler.setConnectionState(state, this.clientId);
1312
- raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
1387
+ raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason?.text);
1313
1388
  }
1314
1389
  // back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
1315
1390
  submitContainerMessage(type, contents, batch, metadata) {
@@ -1347,12 +1422,11 @@ export class Container extends EventEmitterWithErrorHandling {
1347
1422
  return this.submitMessage(MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
1348
1423
  }
1349
1424
  submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
1350
- var _a;
1351
1425
  if (this.connectionState !== ConnectionState.Connected) {
1352
1426
  this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
1353
1427
  return -1;
1354
1428
  }
1355
- (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
1429
+ this.noopHeuristic?.notifyMessageSent();
1356
1430
  return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
1357
1431
  }
1358
1432
  processRemoteMessage(message) {
@@ -1360,23 +1434,6 @@ export class Container extends EventEmitterWithErrorHandling {
1360
1434
  this.savedOps.push(message);
1361
1435
  }
1362
1436
  const local = this.clientId === message.clientId;
1363
- // Check and report if we're getting messages from a clientId that we previously
1364
- // flagged should have left, or from a client that's not in the quorum but should be
1365
- if (message.clientId != null) {
1366
- const client = this.protocolHandler.quorum.getMember(message.clientId);
1367
- if (client === undefined && message.type !== MessageType.ClientJoin) {
1368
- // pre-0.58 error message: messageClientIdMissingFromQuorum
1369
- throw new Error("Remote message's clientId is missing from the quorum");
1370
- }
1371
- // Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
1372
- // It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
1373
- // document we don't need to blow up aggressively.
1374
- if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
1375
- !canBeCoalescedByService(message)) {
1376
- // pre-0.58 error message: messageClientIdShouldHaveLeft
1377
- throw new Error("Remote message's clientId already should have left");
1378
- }
1379
- }
1380
1437
  // Allow the protocol handler to process the message
1381
1438
  const result = this.protocolHandler.processMessage(message, local);
1382
1439
  // Forward messages to the loaded runtime for processing
@@ -1427,8 +1484,7 @@ export class Container extends EventEmitterWithErrorHandling {
1427
1484
  * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
1428
1485
  */
1429
1486
  async fetchSnapshotTree(specifiedVersion) {
1430
- var _a;
1431
- const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
1487
+ const version = await this.getVersion(specifiedVersion ?? null);
1432
1488
  if (version === undefined && specifiedVersion !== undefined) {
1433
1489
  // We should have a defined version to load from if specified version requested
1434
1490
  this.mc.logger.sendErrorEvent({
@@ -1437,15 +1493,14 @@ export class Container extends EventEmitterWithErrorHandling {
1437
1493
  });
1438
1494
  }
1439
1495
  this._loadedFromVersion = version;
1440
- const snapshot = (_a = (await this.storageAdapter.getSnapshotTree(version))) !== null && _a !== void 0 ? _a : undefined;
1496
+ const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
1441
1497
  if (snapshot === undefined && version !== undefined) {
1442
1498
  this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
1443
1499
  }
1444
- return { snapshot, versionId: version === null || version === void 0 ? void 0 : version.id };
1500
+ return { snapshot, versionId: version?.id };
1445
1501
  }
1446
1502
  async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
1447
- var _a, _b;
1448
- assert(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
1503
+ assert(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
1449
1504
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1450
1505
  // are set. Global requests will still go directly to the loader
1451
1506
  const maybeLoader = this.scope;
@@ -1456,22 +1511,17 @@ export class Container extends EventEmitterWithErrorHandling {
1456
1511
  // An older interface ICodeLoader could return an IFluidModule which didn't have details.
1457
1512
  // If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
1458
1513
  // TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
1459
- details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
1514
+ details: loadCodeResult.details ?? codeDetails,
1460
1515
  };
1461
1516
  const fluidExport = this._loadedModule.module.fluidExport;
1462
- const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
1517
+ const runtimeFactory = fluidExport?.IRuntimeFactory;
1463
1518
  if (runtimeFactory === undefined) {
1464
1519
  throw new Error(packageNotFactoryError);
1465
1520
  }
1466
- const getSpecifiedCodeDetails = () => {
1467
- var _a;
1468
- return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
1469
- };
1521
+ const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
1522
+ this.protocolHandler.quorum.get("code2"));
1470
1523
  const existing = snapshot !== undefined;
1471
- const context = new ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; }, () => this.clientId, () => this._deltaManager.serviceConfiguration, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
1472
- this._lifecycleEvents.once("disposed", () => {
1473
- context.dispose();
1474
- });
1524
+ const context = new ContainerContext(this.options, this.scope, snapshot, this._loadedFromVersion, this._deltaManager, this.storageAdapter, this.protocolHandler.quorum, this.protocolHandler.audience, loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), this.updateDirtyContainerState, this.getAbsoluteUrl, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
1475
1525
  this._runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
1476
1526
  this._lifecycleEvents.emit("runtimeInstantiated");
1477
1527
  this._loadedCodeDetails = codeDetails;
@@ -1483,8 +1533,7 @@ export class Container extends EventEmitterWithErrorHandling {
1483
1533
  * @param readonly - Is the container in readonly mode?
1484
1534
  */
1485
1535
  setContextConnectedState(state, readonly) {
1486
- var _a;
1487
- if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1536
+ if (this._runtime?.disposed === false) {
1488
1537
  /**
1489
1538
  * We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
1490
1539
  * ops getting through to the DeltaManager.