@fluidframework/container-loader 2.0.0-internal.5.4.2 → 2.0.0-internal.6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/connectionManager.d.ts +1 -1
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +24 -25
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/connectionStateHandler.d.ts +2 -1
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +9 -16
  9. package/dist/connectionStateHandler.js.map +1 -1
  10. package/dist/container.d.ts +10 -5
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +161 -99
  13. package/dist/container.js.map +1 -1
  14. package/dist/containerContext.d.ts +2 -7
  15. package/dist/containerContext.d.ts.map +1 -1
  16. package/dist/containerContext.js +2 -14
  17. package/dist/containerContext.js.map +1 -1
  18. package/dist/containerStorageAdapter.js +3 -5
  19. package/dist/containerStorageAdapter.js.map +1 -1
  20. package/dist/contracts.d.ts +11 -2
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js +3 -3
  23. package/dist/contracts.js.map +1 -1
  24. package/dist/debugLogger.js +2 -3
  25. package/dist/debugLogger.js.map +1 -1
  26. package/dist/deltaManager.d.ts +16 -3
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +62 -24
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.js +1 -2
  31. package/dist/deltaQueue.js.map +1 -1
  32. package/dist/loader.d.ts +12 -0
  33. package/dist/loader.d.ts.map +1 -1
  34. package/dist/loader.js +57 -42
  35. package/dist/loader.js.map +1 -1
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/protocol.d.ts.map +1 -1
  40. package/dist/protocol.js +2 -3
  41. package/dist/protocol.js.map +1 -1
  42. package/dist/utils.d.ts +8 -1
  43. package/dist/utils.d.ts.map +1 -1
  44. package/dist/utils.js +24 -6
  45. package/dist/utils.js.map +1 -1
  46. package/lib/connectionManager.d.ts +1 -1
  47. package/lib/connectionManager.d.ts.map +1 -1
  48. package/lib/connectionManager.js +25 -26
  49. package/lib/connectionManager.js.map +1 -1
  50. package/lib/connectionStateHandler.d.ts +2 -1
  51. package/lib/connectionStateHandler.d.ts.map +1 -1
  52. package/lib/connectionStateHandler.js +9 -16
  53. package/lib/connectionStateHandler.js.map +1 -1
  54. package/lib/container.d.ts +10 -5
  55. package/lib/container.d.ts.map +1 -1
  56. package/lib/container.js +160 -98
  57. package/lib/container.js.map +1 -1
  58. package/lib/containerContext.d.ts +2 -7
  59. package/lib/containerContext.d.ts.map +1 -1
  60. package/lib/containerContext.js +2 -14
  61. package/lib/containerContext.js.map +1 -1
  62. package/lib/containerStorageAdapter.js +3 -5
  63. package/lib/containerStorageAdapter.js.map +1 -1
  64. package/lib/contracts.d.ts +11 -2
  65. package/lib/contracts.d.ts.map +1 -1
  66. package/lib/contracts.js +3 -3
  67. package/lib/contracts.js.map +1 -1
  68. package/lib/debugLogger.js +2 -3
  69. package/lib/debugLogger.js.map +1 -1
  70. package/lib/deltaManager.d.ts +16 -3
  71. package/lib/deltaManager.d.ts.map +1 -1
  72. package/lib/deltaManager.js +62 -24
  73. package/lib/deltaManager.js.map +1 -1
  74. package/lib/deltaQueue.js +1 -2
  75. package/lib/deltaQueue.js.map +1 -1
  76. package/lib/loader.d.ts +12 -0
  77. package/lib/loader.d.ts.map +1 -1
  78. package/lib/loader.js +57 -42
  79. package/lib/loader.js.map +1 -1
  80. package/lib/packageVersion.d.ts +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/lib/protocol.d.ts.map +1 -1
  84. package/lib/protocol.js +2 -3
  85. package/lib/protocol.js.map +1 -1
  86. package/lib/utils.d.ts +8 -1
  87. package/lib/utils.d.ts.map +1 -1
  88. package/lib/utils.js +22 -5
  89. package/lib/utils.js.map +1 -1
  90. package/package.json +14 -14
  91. package/src/connectionManager.ts +7 -3
  92. package/src/connectionStateHandler.ts +3 -2
  93. package/src/container.ts +113 -27
  94. package/src/containerContext.ts +0 -16
  95. package/src/contracts.ts +16 -5
  96. package/src/deltaManager.ts +22 -5
  97. package/src/loader.ts +37 -23
  98. package/src/packageVersion.ts +1 -1
  99. package/src/protocol.ts +0 -1
  100. package/src/utils.ts +29 -0
package/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, } 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 : ((attributes, quorumSnapshot, sendProposal) => new ProtocolHandler(attributes, quorumSnapshot, sendProposal, new Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
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
  },
@@ -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
@@ -279,7 +284,7 @@ export class Container extends EventEmitterWithErrorHandling {
279
284
  clientShouldHaveLeft: (clientId) => {
280
285
  this.clientsWhoShouldHaveLeft.add(clientId);
281
286
  },
282
- }, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
287
+ }, this.deltaManager, pendingLocalState?.clientId);
283
288
  this.on(savedContainerEvent, () => {
284
289
  this.connectionStateHandler.containerSaved();
285
290
  });
@@ -291,8 +296,9 @@ export class Container extends EventEmitterWithErrorHandling {
291
296
  : combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
292
297
  // Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
293
298
  // 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);
299
+ const forceEnableSummarizeProtocolTree = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
300
+ options.summarizeProtocolTree;
301
+ this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState?.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
296
302
  const isDomAvailable = typeof document === "object" &&
297
303
  document !== null &&
298
304
  typeof document.addEventListener === "function" &&
@@ -319,7 +325,7 @@ export class Container extends EventEmitterWithErrorHandling {
319
325
  * @internal
320
326
  */
321
327
  static async load(loadProps, createProps) {
322
- const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
328
+ const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } = loadProps;
323
329
  const container = new Container(createProps, loadProps);
324
330
  const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
325
331
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
@@ -327,19 +333,20 @@ export class Container extends EventEmitterWithErrorHandling {
327
333
  // if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
328
334
  // to return container, so ignore this value and use undefined for opsBeforeReturn
329
335
  const mode = pendingLocalState
330
- ? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
336
+ ? { ...(loadMode ?? defaultMode), opsBeforeReturn: undefined }
337
+ : loadMode ?? defaultMode;
331
338
  const onClosed = (err) => {
332
339
  // pre-0.58 error message: containerClosedWithoutErrorDuringLoad
333
- reject(err !== null && err !== void 0 ? err : new GenericError("Container closed without error during load"));
340
+ reject(err ?? new GenericError("Container closed without error during load"));
334
341
  };
335
342
  container.on("closed", onClosed);
336
343
  container
337
- .load(version, mode, resolvedUrl, pendingLocalState)
344
+ .load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
338
345
  .finally(() => {
339
346
  container.removeListener("closed", onClosed);
340
347
  })
341
348
  .then((props) => {
342
- event.end(Object.assign(Object.assign({}, props), loadMode));
349
+ event.end({ ...props, ...loadMode });
343
350
  resolve(container);
344
351
  }, (error) => {
345
352
  const err = normalizeError(error);
@@ -407,7 +414,6 @@ export class Container extends EventEmitterWithErrorHandling {
407
414
  return this;
408
415
  }
409
416
  get resolvedUrl() {
410
- var _a;
411
417
  /**
412
418
  * All attached containers will have a document service,
413
419
  * this is required, as attached containers are attached to
@@ -419,7 +425,7 @@ export class Container extends EventEmitterWithErrorHandling {
419
425
  * is always the same as the containers, as we had to
420
426
  * obtain the resolved url, and then create the service from it.
421
427
  */
422
- return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
428
+ return this.service?.resolvedUrl;
423
429
  }
424
430
  get readOnlyInfo() {
425
431
  return this._deltaManager.readOnlyInfo;
@@ -447,8 +453,8 @@ export class Container extends EventEmitterWithErrorHandling {
447
453
  return this._clientId;
448
454
  }
449
455
  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;
456
+ const enabled = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ??
457
+ this.options?.enableOfflineLoad === true;
452
458
  // summarizer will not have any pending state we want to save
453
459
  return enabled && this.deltaManager.clientDetails.capabilities.interactive;
454
460
  }
@@ -485,18 +491,16 @@ export class Container extends EventEmitterWithErrorHandling {
485
491
  * {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
486
492
  */
487
493
  async getEntryPoint() {
488
- var _a, _b;
489
494
  if (this._disposed) {
490
495
  throw new UsageError("The context is already disposed");
491
496
  }
492
497
  if (this._runtime !== undefined) {
493
- return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
498
+ return this._runtime.getEntryPoint?.();
494
499
  }
495
500
  return new Promise((resolve, reject) => {
496
501
  const runtimeInstantiatedHandler = () => {
497
- var _a, _b;
498
502
  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));
503
+ resolve(this._runtime.getEntryPoint?.());
500
504
  this._lifecycleEvents.off("disposed", disposedHandler);
501
505
  };
502
506
  const disposedHandler = () => {
@@ -530,7 +534,6 @@ export class Container extends EventEmitterWithErrorHandling {
530
534
  assert(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
531
535
  }
532
536
  closeCore(error) {
533
- var _a;
534
537
  assert(!this.closed, 0x315 /* re-entrancy */);
535
538
  try {
536
539
  // Ensure that we raise all key events even if one of these throws
@@ -546,7 +549,7 @@ export class Container extends EventEmitterWithErrorHandling {
546
549
  : "generic",
547
550
  }, error);
548
551
  this._lifecycleState = "closing";
549
- (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
552
+ this._protocolHandler?.close();
550
553
  this.connectionStateHandler.dispose();
551
554
  }
552
555
  catch (exception) {
@@ -566,7 +569,6 @@ export class Container extends EventEmitterWithErrorHandling {
566
569
  }
567
570
  }
568
571
  disposeCore(error) {
569
- var _a, _b, _c;
570
572
  assert(!this._disposed, 0x54c /* Container already disposed */);
571
573
  this._disposed = true;
572
574
  try {
@@ -583,15 +585,15 @@ export class Container extends EventEmitterWithErrorHandling {
583
585
  if (this._lifecycleState !== "closed") {
584
586
  this._lifecycleState = "disposing";
585
587
  }
586
- (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
588
+ this._protocolHandler?.close();
587
589
  this.connectionStateHandler.dispose();
588
590
  const maybeError = error !== undefined ? new Error(error.message) : undefined;
589
- (_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
591
+ this._runtime?.dispose(maybeError);
590
592
  this.storageAdapter.dispose();
591
593
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
592
594
  // about file, like file being overwritten in storage, but client having stale local cache.
593
595
  // Driver need to ensure all caches are cleared on critical errors
594
- (_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
596
+ this.service?.dispose(error);
595
597
  }
596
598
  catch (exception) {
597
599
  this.mc.logger.sendErrorEvent({ eventName: "ContainerDisposeException" }, exception);
@@ -607,15 +609,19 @@ export class Container extends EventEmitterWithErrorHandling {
607
609
  this._lifecycleEvents.emit("disposed");
608
610
  }
609
611
  }
610
- closeAndGetPendingLocalState() {
612
+ async closeAndGetPendingLocalState() {
611
613
  // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
612
614
  // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
613
615
  // a new clientId and a future container using stale pending state without the new clientId would resubmit them
614
- const pendingState = this.getPendingLocalState();
616
+ this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
617
+ const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
615
618
  this.close();
616
619
  return pendingState;
617
620
  }
618
- getPendingLocalState() {
621
+ async getPendingLocalState() {
622
+ return this.getPendingLocalStateCore({ notifyImminentClosure: false });
623
+ }
624
+ async getPendingLocalStateCore(props) {
619
625
  if (!this.offlineLoadEnabled) {
620
626
  throw new UsageError("Can't get pending local state unless offline load is enabled");
621
627
  }
@@ -626,8 +632,9 @@ export class Container extends EventEmitterWithErrorHandling {
626
632
  assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
627
633
  assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
628
634
  assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
635
+ const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
629
636
  const pendingState = {
630
- pendingRuntimeState: this.runtime.getPendingLocalState(),
637
+ pendingRuntimeState,
631
638
  baseSnapshot: this.baseSnapshot,
632
639
  snapshotBlobs: this.baseSnapshotBlobs,
633
640
  savedOps: this.savedOps,
@@ -656,7 +663,6 @@ export class Container extends EventEmitterWithErrorHandling {
656
663
  }
657
664
  async attach(request) {
658
665
  await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
659
- var _a;
660
666
  if (this._lifecycleState !== "loaded") {
661
667
  // pre-0.58 error message: containerNotValidForAttach
662
668
  throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
@@ -748,7 +754,7 @@ export class Container extends EventEmitterWithErrorHandling {
748
754
  catch (error) {
749
755
  // add resolved URL on error object so that host has the ability to find this document and delete it
750
756
  const newError = normalizeError(error);
751
- newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
757
+ newError.addTelemetryProperties({ resolvedUrl: this.resolvedUrl?.url });
752
758
  this.close(newError);
753
759
  throw newError;
754
760
  }
@@ -855,7 +861,6 @@ export class Container extends EventEmitterWithErrorHandling {
855
861
  * Determines if the currently loaded module satisfies the incoming constraint code details
856
862
  */
857
863
  async satisfies(constraintCodeDetails) {
858
- var _a, _b;
859
864
  // If we have no module, it can't satisfy anything.
860
865
  if (this._loadedModule === undefined) {
861
866
  return false;
@@ -865,8 +870,8 @@ export class Container extends EventEmitterWithErrorHandling {
865
870
  if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
866
871
  comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
867
872
  }
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) {
873
+ const maybeCompareExport = this._loadedModule?.module.fluidExport;
874
+ if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
870
875
  comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
871
876
  }
872
877
  // If there are no comparers, then it's impossible to know if the currently loaded package satisfies
@@ -876,7 +881,7 @@ export class Container extends EventEmitterWithErrorHandling {
876
881
  return false;
877
882
  }
878
883
  for (const comparer of comparers) {
879
- const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
884
+ const satisfies = await comparer.satisfies(this._loadedModule?.details, constraintCodeDetails);
880
885
  if (satisfies === false) {
881
886
  return false;
882
887
  }
@@ -899,8 +904,7 @@ export class Container extends EventEmitterWithErrorHandling {
899
904
  *
900
905
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
901
906
  */
902
- async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
903
- var _a, _b, _c;
907
+ async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState, loadToSequenceNumber) {
904
908
  this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
905
909
  // Ideally we always connect as "read" by default.
906
910
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
@@ -949,9 +953,51 @@ export class Container extends EventEmitterWithErrorHandling {
949
953
  }
950
954
  const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
951
955
  // 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;
956
+ const sequenceNumber = pendingLocalState?.savedOps[pendingLocalState.savedOps.length - 1]?.sequenceNumber;
957
+ const dmAttributes = sequenceNumber !== undefined ? { ...attributes, sequenceNumber } : attributes;
954
958
  let opsBeforeReturnP;
959
+ if (loadMode.pauseAfterLoad === true) {
960
+ // If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
961
+ if (loadMode.opsBeforeReturn === "sequenceNumber") {
962
+ assert(loadToSequenceNumber !== undefined, 0x727 /* sequenceNumber should be defined */);
963
+ // Note: It is possible that we think the latest snapshot is newer than the specified sequence number
964
+ // due to saved ops that may be replayed after the snapshot.
965
+ // https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
966
+ if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
967
+ 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.");
968
+ }
969
+ }
970
+ // Force readonly mode - this will ensure we don't receive an error for the lack of join op
971
+ this.forceReadonly(true);
972
+ // We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
973
+ const opHandler = () => {
974
+ if (loadToSequenceNumber === undefined) {
975
+ // If there is no specified sequence number, pause after the inbound queue is empty.
976
+ if (this.deltaManager.inbound.length !== 0) {
977
+ return;
978
+ }
979
+ }
980
+ else {
981
+ // If there is a specified sequence number, keep processing until we reach it.
982
+ if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
983
+ return;
984
+ }
985
+ }
986
+ // Pause op processing once we have processed the desired number of ops.
987
+ void this.deltaManager.inbound.pause();
988
+ void this.deltaManager.outbound.pause();
989
+ this.off("op", opHandler);
990
+ };
991
+ if ((loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
992
+ this.deltaManager.lastSequenceNumber === loadToSequenceNumber) {
993
+ // If we have already reached the desired sequence number, call opHandler() to pause immediately.
994
+ opHandler();
995
+ }
996
+ else {
997
+ // If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
998
+ this.on("op", opHandler);
999
+ }
1000
+ }
955
1001
  // Attach op handlers to finish initialization and be able to start processing ops
956
1002
  // Kick off any ops fetching if required.
957
1003
  switch (loadMode.opsBeforeReturn) {
@@ -960,6 +1006,9 @@ export class Container extends EventEmitterWithErrorHandling {
960
1006
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
961
1007
  this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
962
1008
  break;
1009
+ case "sequenceNumber":
1010
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
1011
+ break;
963
1012
  case "cached":
964
1013
  opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
965
1014
  break;
@@ -973,18 +1022,18 @@ export class Container extends EventEmitterWithErrorHandling {
973
1022
  // Initialize the protocol handler
974
1023
  await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
975
1024
  const codeDetails = this.getCodeDetailsFromQuorum();
976
- await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
1025
+ await this.instantiateRuntime(codeDetails, snapshot, pendingLocalState?.pendingRuntimeState);
977
1026
  // replay saved ops
978
1027
  if (pendingLocalState) {
979
1028
  for (const message of pendingLocalState.savedOps) {
980
1029
  this.processRemoteMessage(message);
981
1030
  // 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));
1031
+ await this.runtime.notifyOpReplay?.(message);
983
1032
  }
984
1033
  pendingLocalState.savedOps = [];
985
1034
  // now set clientId to stashed clientId so live ops are correctly processed as local
986
1035
  assert(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
987
- this._clientId = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId;
1036
+ this._clientId = pendingLocalState?.clientId;
988
1037
  }
989
1038
  // We might have hit some failure that did not manifest itself in exception in this flow,
990
1039
  // do not start op processing in such case - static version of Container.load() will handle it correctly.
@@ -1015,6 +1064,19 @@ export class Container extends EventEmitterWithErrorHandling {
1015
1064
  unreachableCase(loadMode.deltaConnection);
1016
1065
  }
1017
1066
  }
1067
+ // If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
1068
+ if (loadToSequenceNumber !== undefined &&
1069
+ this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
1070
+ await new Promise((resolve, reject) => {
1071
+ const opHandler = (message) => {
1072
+ if (message.sequenceNumber >= loadToSequenceNumber) {
1073
+ resolve();
1074
+ this.off("op", opHandler);
1075
+ }
1076
+ };
1077
+ this.on("op", opHandler);
1078
+ });
1079
+ }
1018
1080
  // Safety net: static version of Container.load() should have learned about it through "closed" handler.
1019
1081
  // But if that did not happen for some reason, fail load for sure.
1020
1082
  // Otherwise we can get into situations where container is closed and does not try to connect to ordering
@@ -1169,8 +1231,7 @@ export class Container extends EventEmitterWithErrorHandling {
1169
1231
  return pkg;
1170
1232
  }
1171
1233
  get client() {
1172
- var _a;
1173
- const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
1234
+ const client = this.options?.client !== undefined
1174
1235
  ? this.options.client
1175
1236
  : {
1176
1237
  details: {
@@ -1218,8 +1279,7 @@ export class Container extends EventEmitterWithErrorHandling {
1218
1279
  this.connectionStateHandler.cancelEstablishingConnection(reason);
1219
1280
  });
1220
1281
  deltaManager.on("disconnect", (reason, error) => {
1221
- var _a;
1222
- (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
1282
+ this.noopHeuristic?.notifyDisconnect();
1223
1283
  if (!this.closed) {
1224
1284
  this.connectionStateHandler.receivedDisconnectEvent(reason, error);
1225
1285
  }
@@ -1254,7 +1314,6 @@ export class Container extends EventEmitterWithErrorHandling {
1254
1314
  }, prefetchType);
1255
1315
  }
1256
1316
  logConnectionStateChangeTelemetry(value, oldState, reason, error) {
1257
- var _a;
1258
1317
  // Log actual event
1259
1318
  const time = performance.now();
1260
1319
  this.connectionTransitionTimes[value] = time;
@@ -1284,19 +1343,31 @@ export class Container extends EventEmitterWithErrorHandling {
1284
1343
  }
1285
1344
  connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
1286
1345
  }
1287
- this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
1346
+ this.mc.logger.sendPerformanceEvent({
1347
+ eventName: `ConnectionStateChange_${ConnectionState[value]}`,
1348
+ from: ConnectionState[oldState],
1349
+ duration,
1288
1350
  durationFromDisconnected,
1289
1351
  reason,
1290
- connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
1291
- opsBehind, online: OnlineStatus[isOnline()], lastVisible: this.lastVisible !== undefined
1352
+ connectionInitiationReason,
1353
+ pendingClientId: this.connectionStateHandler.pendingClientId,
1354
+ clientId: this.clientId,
1355
+ autoReconnect,
1356
+ opsBehind,
1357
+ online: OnlineStatus[isOnline()],
1358
+ lastVisible: this.lastVisible !== undefined
1292
1359
  ? 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);
1360
+ : undefined,
1361
+ checkpointSequenceNumber,
1362
+ quorumSize: this._protocolHandler?.quorum.getMembers().size,
1363
+ isDirty: this.isDirty,
1364
+ ...this._deltaManager.connectionProps,
1365
+ }, error);
1294
1366
  if (value === ConnectionState.Connected) {
1295
1367
  this.firstConnection = false;
1296
1368
  }
1297
1369
  }
1298
1370
  propagateConnectionState(initialTransition, disconnectedReason) {
1299
- var _a;
1300
1371
  // When container loaded, we want to propagate initial connection state.
1301
1372
  // After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
1302
1373
  // This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
@@ -1307,7 +1378,7 @@ export class Container extends EventEmitterWithErrorHandling {
1307
1378
  }
1308
1379
  const state = this.connectionState === ConnectionState.Connected;
1309
1380
  // 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);
1381
+ this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
1311
1382
  this.protocolHandler.setConnectionState(state, this.clientId);
1312
1383
  raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
1313
1384
  }
@@ -1347,12 +1418,11 @@ export class Container extends EventEmitterWithErrorHandling {
1347
1418
  return this.submitMessage(MessageType.Summarize, JSON.stringify(summary), false /* batch */, undefined /* metadata */, undefined /* compression */, referenceSequenceNumber);
1348
1419
  }
1349
1420
  submitMessage(type, contents, batch, metadata, compression, referenceSequenceNumber) {
1350
- var _a;
1351
1421
  if (this.connectionState !== ConnectionState.Connected) {
1352
1422
  this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
1353
1423
  return -1;
1354
1424
  }
1355
- (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
1425
+ this.noopHeuristic?.notifyMessageSent();
1356
1426
  return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
1357
1427
  }
1358
1428
  processRemoteMessage(message) {
@@ -1410,8 +1480,7 @@ export class Container extends EventEmitterWithErrorHandling {
1410
1480
  * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
1411
1481
  */
1412
1482
  async fetchSnapshotTree(specifiedVersion) {
1413
- var _a;
1414
- const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
1483
+ const version = await this.getVersion(specifiedVersion ?? null);
1415
1484
  if (version === undefined && specifiedVersion !== undefined) {
1416
1485
  // We should have a defined version to load from if specified version requested
1417
1486
  this.mc.logger.sendErrorEvent({
@@ -1420,15 +1489,14 @@ export class Container extends EventEmitterWithErrorHandling {
1420
1489
  });
1421
1490
  }
1422
1491
  this._loadedFromVersion = version;
1423
- const snapshot = (_a = (await this.storageAdapter.getSnapshotTree(version))) !== null && _a !== void 0 ? _a : undefined;
1492
+ const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
1424
1493
  if (snapshot === undefined && version !== undefined) {
1425
1494
  this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
1426
1495
  }
1427
- return { snapshot, versionId: version === null || version === void 0 ? void 0 : version.id };
1496
+ return { snapshot, versionId: version?.id };
1428
1497
  }
1429
1498
  async instantiateRuntime(codeDetails, snapshot, pendingLocalState) {
1430
- var _a, _b;
1431
- assert(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
1499
+ assert(this._runtime?.disposed !== false, 0x0dd /* "Existing runtime not disposed" */);
1432
1500
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1433
1501
  // are set. Global requests will still go directly to the loader
1434
1502
  const maybeLoader = this.scope;
@@ -1439,22 +1507,17 @@ export class Container extends EventEmitterWithErrorHandling {
1439
1507
  // An older interface ICodeLoader could return an IFluidModule which didn't have details.
1440
1508
  // If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
1441
1509
  // TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
1442
- details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
1510
+ details: loadCodeResult.details ?? codeDetails,
1443
1511
  };
1444
1512
  const fluidExport = this._loadedModule.module.fluidExport;
1445
- const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
1513
+ const runtimeFactory = fluidExport?.IRuntimeFactory;
1446
1514
  if (runtimeFactory === undefined) {
1447
1515
  throw new Error(packageNotFactoryError);
1448
1516
  }
1449
- const getSpecifiedCodeDetails = () => {
1450
- var _a;
1451
- return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
1452
- };
1517
+ const getSpecifiedCodeDetails = () => (this.protocolHandler.quorum.get("code") ??
1518
+ this.protocolHandler.quorum.get("code2"));
1453
1519
  const existing = snapshot !== undefined;
1454
- 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);
1455
- this._lifecycleEvents.once("disposed", () => {
1456
- context.dispose();
1457
- });
1520
+ 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.resolvedUrl?.id, () => this.clientId, () => this.attachState, () => this.connected, getSpecifiedCodeDetails, this._deltaManager.clientDetails, existing, this.subLogger, pendingLocalState);
1458
1521
  this._runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
1459
1522
  this._lifecycleEvents.emit("runtimeInstantiated");
1460
1523
  this._loadedCodeDetails = codeDetails;
@@ -1466,8 +1529,7 @@ export class Container extends EventEmitterWithErrorHandling {
1466
1529
  * @param readonly - Is the container in readonly mode?
1467
1530
  */
1468
1531
  setContextConnectedState(state, readonly) {
1469
- var _a;
1470
- if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1532
+ if (this._runtime?.disposed === false) {
1471
1533
  /**
1472
1534
  * We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
1473
1535
  * ops getting through to the DeltaManager.