@fluidframework/container-loader 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.5.3.2.178189

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/catchUpMonitor.d.ts +1 -1
  3. package/dist/catchUpMonitor.d.ts.map +1 -1
  4. package/dist/catchUpMonitor.js.map +1 -1
  5. package/dist/connectionManager.d.ts +1 -1
  6. package/dist/connectionManager.d.ts.map +1 -1
  7. package/dist/connectionManager.js.map +1 -1
  8. package/dist/connectionStateHandler.d.ts +4 -1
  9. package/dist/connectionStateHandler.d.ts.map +1 -1
  10. package/dist/connectionStateHandler.js +10 -8
  11. package/dist/connectionStateHandler.js.map +1 -1
  12. package/dist/container.d.ts +30 -31
  13. package/dist/container.d.ts.map +1 -1
  14. package/dist/container.js +180 -108
  15. package/dist/container.js.map +1 -1
  16. package/dist/containerContext.d.ts +23 -66
  17. package/dist/containerContext.d.ts.map +1 -1
  18. package/dist/containerContext.js +28 -213
  19. package/dist/containerContext.js.map +1 -1
  20. package/dist/containerStorageAdapter.d.ts +1 -1
  21. package/dist/containerStorageAdapter.d.ts.map +1 -1
  22. package/dist/containerStorageAdapter.js +38 -6
  23. package/dist/containerStorageAdapter.js.map +1 -1
  24. package/dist/contracts.d.ts +1 -3
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/contracts.js.map +1 -1
  27. package/dist/deltaManager.d.ts +2 -1
  28. package/dist/deltaManager.d.ts.map +1 -1
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/disposal.d.ts +13 -0
  31. package/dist/disposal.d.ts.map +1 -0
  32. package/dist/disposal.js +25 -0
  33. package/dist/disposal.js.map +1 -0
  34. package/dist/loader.d.ts +1 -2
  35. package/dist/loader.d.ts.map +1 -1
  36. package/dist/loader.js.map +1 -1
  37. package/dist/noopHeuristic.d.ts +23 -0
  38. package/dist/noopHeuristic.d.ts.map +1 -0
  39. package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  40. package/dist/noopHeuristic.js.map +1 -0
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.js +1 -1
  43. package/dist/packageVersion.js.map +1 -1
  44. package/dist/protocol.d.ts +7 -12
  45. package/dist/protocol.d.ts.map +1 -1
  46. package/dist/protocol.js +17 -19
  47. package/dist/protocol.js.map +1 -1
  48. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  49. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  50. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  51. package/dist/quorum.d.ts +1 -17
  52. package/dist/quorum.d.ts.map +1 -1
  53. package/dist/quorum.js +1 -17
  54. package/dist/quorum.js.map +1 -1
  55. package/dist/retriableDocumentStorageService.d.ts +1 -1
  56. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  57. package/dist/retriableDocumentStorageService.js.map +1 -1
  58. package/lib/catchUpMonitor.d.ts +1 -1
  59. package/lib/catchUpMonitor.d.ts.map +1 -1
  60. package/lib/catchUpMonitor.js.map +1 -1
  61. package/lib/connectionManager.d.ts +1 -1
  62. package/lib/connectionManager.d.ts.map +1 -1
  63. package/lib/connectionManager.js.map +1 -1
  64. package/lib/connectionStateHandler.d.ts +4 -1
  65. package/lib/connectionStateHandler.d.ts.map +1 -1
  66. package/lib/connectionStateHandler.js +10 -8
  67. package/lib/connectionStateHandler.js.map +1 -1
  68. package/lib/container.d.ts +30 -31
  69. package/lib/container.d.ts.map +1 -1
  70. package/lib/container.js +184 -112
  71. package/lib/container.js.map +1 -1
  72. package/lib/containerContext.d.ts +23 -66
  73. package/lib/containerContext.d.ts.map +1 -1
  74. package/lib/containerContext.js +28 -213
  75. package/lib/containerContext.js.map +1 -1
  76. package/lib/containerStorageAdapter.d.ts +1 -1
  77. package/lib/containerStorageAdapter.d.ts.map +1 -1
  78. package/lib/containerStorageAdapter.js +38 -6
  79. package/lib/containerStorageAdapter.js.map +1 -1
  80. package/lib/contracts.d.ts +1 -3
  81. package/lib/contracts.d.ts.map +1 -1
  82. package/lib/contracts.js.map +1 -1
  83. package/lib/deltaManager.d.ts +2 -1
  84. package/lib/deltaManager.d.ts.map +1 -1
  85. package/lib/deltaManager.js.map +1 -1
  86. package/lib/disposal.d.ts +13 -0
  87. package/lib/disposal.d.ts.map +1 -0
  88. package/lib/disposal.js +21 -0
  89. package/lib/disposal.js.map +1 -0
  90. package/lib/loader.d.ts +1 -2
  91. package/lib/loader.d.ts.map +1 -1
  92. package/lib/loader.js.map +1 -1
  93. package/lib/noopHeuristic.d.ts +23 -0
  94. package/lib/noopHeuristic.d.ts.map +1 -0
  95. package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  96. package/lib/noopHeuristic.js.map +1 -0
  97. package/lib/packageVersion.d.ts +1 -1
  98. package/lib/packageVersion.js +1 -1
  99. package/lib/packageVersion.js.map +1 -1
  100. package/lib/protocol.d.ts +7 -12
  101. package/lib/protocol.d.ts.map +1 -1
  102. package/lib/protocol.js +15 -18
  103. package/lib/protocol.js.map +1 -1
  104. package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
  105. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  106. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  107. package/lib/quorum.d.ts +1 -17
  108. package/lib/quorum.d.ts.map +1 -1
  109. package/lib/quorum.js +1 -16
  110. package/lib/quorum.js.map +1 -1
  111. package/lib/retriableDocumentStorageService.d.ts +1 -1
  112. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  113. package/lib/retriableDocumentStorageService.js.map +1 -1
  114. package/package.json +18 -14
  115. package/src/catchUpMonitor.ts +1 -1
  116. package/src/connectionManager.ts +1 -1
  117. package/src/connectionStateHandler.ts +15 -10
  118. package/src/container.ts +279 -139
  119. package/src/containerContext.ts +33 -335
  120. package/src/containerStorageAdapter.ts +47 -5
  121. package/src/contracts.ts +1 -3
  122. package/src/deltaManager.ts +15 -8
  123. package/src/disposal.ts +25 -0
  124. package/src/loader.ts +1 -1
  125. package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
  126. package/src/packageVersion.ts +1 -1
  127. package/src/protocol.ts +18 -39
  128. package/src/protocolTreeDocumentStorageService.ts +1 -1
  129. package/src/quorum.ts +2 -31
  130. package/src/retriableDocumentStorageService.ts +2 -1
  131. package/dist/collabWindowTracker.d.ts +0 -19
  132. package/dist/collabWindowTracker.d.ts.map +0 -1
  133. package/dist/collabWindowTracker.js.map +0 -1
  134. package/dist/deltaManagerProxy.d.ts +0 -42
  135. package/dist/deltaManagerProxy.d.ts.map +0 -1
  136. package/dist/deltaManagerProxy.js +0 -79
  137. package/dist/deltaManagerProxy.js.map +0 -1
  138. package/lib/collabWindowTracker.d.ts +0 -19
  139. package/lib/collabWindowTracker.d.ts.map +0 -1
  140. package/lib/collabWindowTracker.js.map +0 -1
  141. package/lib/deltaManagerProxy.d.ts +0 -42
  142. package/lib/deltaManagerProxy.d.ts.map +0 -1
  143. package/lib/deltaManagerProxy.js +0 -74
  144. package/lib/deltaManagerProxy.js.map +0 -1
  145. package/src/deltaManagerProxy.ts +0 -109
package/dist/container.js CHANGED
@@ -21,20 +21,20 @@ const audience_1 = require("./audience");
21
21
  const containerContext_1 = require("./containerContext");
22
22
  const contracts_1 = require("./contracts");
23
23
  const deltaManager_1 = require("./deltaManager");
24
- const deltaManagerProxy_1 = require("./deltaManagerProxy");
25
24
  const loader_1 = require("./loader");
26
25
  const packageVersion_1 = require("./packageVersion");
27
26
  const containerStorageAdapter_1 = require("./containerStorageAdapter");
28
27
  const connectionStateHandler_1 = require("./connectionStateHandler");
29
28
  const utils_1 = require("./utils");
30
29
  const quorum_1 = require("./quorum");
31
- const collabWindowTracker_1 = require("./collabWindowTracker");
30
+ const noopHeuristic_1 = require("./noopHeuristic");
32
31
  const connectionManager_1 = require("./connectionManager");
33
32
  const connectionState_1 = require("./connectionState");
34
33
  const protocol_1 = require("./protocol");
35
34
  const detachedContainerRefSeqNumber = 0;
36
35
  const dirtyContainerEvent = "dirty";
37
36
  const savedContainerEvent = "saved";
37
+ const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
38
38
  /**
39
39
  * Waits until container connects to delta storage and gets up-to-date.
40
40
  *
@@ -164,8 +164,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
164
164
  this.attachStarted = false;
165
165
  this._dirtyContainer = false;
166
166
  this.savedOps = [];
167
+ this.clientsWhoShouldHaveLeft = new Set();
167
168
  this.setAutoReconnectTime = common_utils_1.performance.now();
169
+ this._lifecycleEvents = new common_utils_1.TypedEventEmitter();
168
170
  this._disposed = false;
171
+ this.getAbsoluteUrl = async (relativeUrl) => {
172
+ if (this.resolvedUrl === undefined) {
173
+ return undefined;
174
+ }
175
+ return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)(this._loadedCodeDetails));
176
+ };
177
+ this.updateDirtyContainerState = (dirty) => {
178
+ if (this._dirtyContainer === dirty) {
179
+ return;
180
+ }
181
+ this._dirtyContainer = dirty;
182
+ this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
183
+ };
169
184
  const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
170
185
  this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
171
186
  const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
@@ -210,13 +225,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
210
225
  dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
211
226
  dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
212
227
  dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
213
- containerLoadedFromVersionId: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
214
- containerLoadedFromVersionDate: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
228
+ containerLoadedFromVersionId: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
229
+ containerLoadedFromVersionDate: () => { var _a; return (_a = this._loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
215
230
  // message information to associate errors with the specific execution state
216
231
  // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
217
232
  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; },
218
233
  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; },
219
- dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
234
+ dmLastMsqSeqClientId: () => {
235
+ var _a, _b, _c, _d;
236
+ return ((_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId) === null
237
+ ? "null"
238
+ : (_d = (_c = this.deltaManager) === null || _c === void 0 ? void 0 : _c.lastMessage) === null || _d === void 0 ? void 0 : _d.clientId;
239
+ },
220
240
  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; },
221
241
  connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
222
242
  },
@@ -262,6 +282,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
262
282
  this.connect();
263
283
  }
264
284
  },
285
+ clientShouldHaveLeft: (clientId) => {
286
+ this.clientsWhoShouldHaveLeft.add(clientId);
287
+ },
265
288
  }, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
266
289
  this.on(savedContainerEvent, () => {
267
290
  this.connectionStateHandler.containerSaved();
@@ -371,14 +394,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
371
394
  this._lifecycleState === "disposing" ||
372
395
  this._lifecycleState === "disposed");
373
396
  }
374
- get storage() {
375
- return this.storageAdapter;
376
- }
377
- get context() {
378
- if (this._context === undefined) {
379
- throw new container_utils_1.GenericError("Attempted to access context before it was defined");
397
+ get runtime() {
398
+ if (this._runtime === undefined) {
399
+ throw new Error("Attempted to access runtime before it was defined");
380
400
  }
381
- return this._context;
401
+ return this._runtime;
382
402
  }
383
403
  get protocolHandler() {
384
404
  if (this._protocolHandler === undefined) {
@@ -407,15 +427,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
407
427
  */
408
428
  return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
409
429
  }
410
- get loadedFromVersion() {
411
- return this._loadedFromVersion;
412
- }
413
430
  get readOnlyInfo() {
414
431
  return this._deltaManager.readOnlyInfo;
415
432
  }
416
- get closeSignal() {
417
- return this._deltaManager.closeAbortController.signal;
418
- }
419
433
  /**
420
434
  * Tracks host requiring read-only mode.
421
435
  */
@@ -431,13 +445,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
431
445
  get connected() {
432
446
  return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
433
447
  }
434
- /**
435
- * Service configuration details. If running in offline mode will be undefined otherwise will contain service
436
- * configuration details returned as part of the initial connection.
437
- */
438
- get serviceConfiguration() {
439
- return this._deltaManager.serviceConfiguration;
440
- }
441
448
  /**
442
449
  * The server provided id of the client.
443
450
  * Set once this.connected is true, otherwise undefined
@@ -445,21 +452,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
445
452
  get clientId() {
446
453
  return this._clientId;
447
454
  }
448
- /**
449
- * The server provided claims of the client.
450
- * Set once this.connected is true, otherwise undefined
451
- */
452
- get scopes() {
453
- return this._deltaManager.connectionManager.scopes;
454
- }
455
- get clientDetails() {
456
- return this._deltaManager.clientDetails;
457
- }
458
455
  get offlineLoadEnabled() {
459
456
  var _a, _b;
460
457
  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;
461
458
  // summarizer will not have any pending state we want to save
462
- return enabled && this.clientDetails.capabilities.interactive;
459
+ return enabled && this.deltaManager.clientDetails.capabilities.interactive;
463
460
  }
464
461
  /**
465
462
  * Get the code details that are currently specified for the container.
@@ -474,8 +471,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
474
471
  * loaded.
475
472
  */
476
473
  getLoadedCodeDetails() {
477
- var _a;
478
- return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
474
+ return this._loadedCodeDetails;
479
475
  }
480
476
  /**
481
477
  * Retrieves the audience associated with the document
@@ -496,32 +492,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
496
492
  */
497
493
  async getEntryPoint() {
498
494
  var _a, _b;
499
- // Only the disposing/disposed lifecycle states should prevent access to the entryPoint; closing/closed should still
500
- // allow it since they mean a kind of read-only state for the Container.
501
- // Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
502
- if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
503
- throw new container_utils_1.UsageError("The container is disposing or disposed");
504
- }
505
- while (this._context === undefined) {
506
- await new Promise((resolve, reject) => {
507
- const contextChangedHandler = () => {
508
- resolve();
509
- this.off("disposed", disposedHandler);
510
- };
511
- const disposedHandler = (error) => {
512
- reject(error !== null && error !== void 0 ? error : "The Container is disposed");
513
- this.off("contextChanged", contextChangedHandler);
514
- };
515
- this.once("contextChanged", contextChangedHandler);
516
- this.once("disposed", disposedHandler);
517
- });
518
- // The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
519
- // should have set this._context; making sure.
520
- (0, common_utils_1.assert)(this._context !== undefined, 0x5a2 /* Context still not defined after contextChanged event */);
495
+ if (this._disposed) {
496
+ throw new container_utils_1.UsageError("The context is already disposed");
497
+ }
498
+ if (this._runtime !== undefined) {
499
+ return (_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a);
521
500
  }
522
- // Disable lint rule for the sake of more complete stack traces
523
- // eslint-disable-next-line no-return-await
524
- return await ((_b = (_a = this._context).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
501
+ return new Promise((resolve, reject) => {
502
+ const runtimeInstantiatedHandler = () => {
503
+ var _a, _b;
504
+ (0, common_utils_1.assert)(this._runtime !== undefined, 0x5a3 /* runtimeInstantiated fired but runtime is still undefined */);
505
+ resolve((_b = (_a = this._runtime).getEntryPoint) === null || _b === void 0 ? void 0 : _b.call(_a));
506
+ this._lifecycleEvents.off("disposed", disposedHandler);
507
+ };
508
+ const disposedHandler = () => {
509
+ reject(new Error("ContainerContext was disposed"));
510
+ this._lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
511
+ };
512
+ this._lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
513
+ this._lifecycleEvents.once("disposed", disposedHandler);
514
+ });
525
515
  }
526
516
  /**
527
517
  * Retrieves the quorum associated with the document
@@ -575,6 +565,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
575
565
  }
576
566
  finally {
577
567
  this._lifecycleState = "closed";
568
+ // There is no user for summarizer, so we need to ensure dispose is called
569
+ if (this.client.details.type === summarizerClientType) {
570
+ this.dispose(error);
571
+ }
578
572
  }
579
573
  }
580
574
  disposeCore(error) {
@@ -588,7 +582,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
588
582
  // This gives us a chance to know what errors happened on open vs. on fully loaded container.
589
583
  this.mc.logger.sendTelemetryEvent({
590
584
  eventName: "ContainerDispose",
591
- category: "generic",
585
+ // Only log error if container isn't closed
586
+ category: !this.closed && error !== undefined ? "error" : "generic",
592
587
  }, error);
593
588
  // ! Progressing from "closed" to "disposing" is not allowed
594
589
  if (this._lifecycleState !== "closed") {
@@ -596,7 +591,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
596
591
  }
597
592
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
598
593
  this.connectionStateHandler.dispose();
599
- (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
594
+ const maybeError = error !== undefined ? new Error(error.message) : undefined;
595
+ (_b = this._runtime) === null || _b === void 0 ? void 0 : _b.dispose(maybeError);
600
596
  this.storageAdapter.dispose();
601
597
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
602
598
  // about file, like file being overwritten in storage, but client having stale local cache.
@@ -614,6 +610,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
614
610
  }
615
611
  finally {
616
612
  this._lifecycleState = "disposed";
613
+ this._lifecycleEvents.emit("disposed");
617
614
  }
618
615
  }
619
616
  closeAndGetPendingLocalState() {
@@ -628,12 +625,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
628
625
  if (!this.offlineLoadEnabled) {
629
626
  throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
630
627
  }
628
+ if (this.closed || this._disposed) {
629
+ throw new container_utils_1.UsageError("Pending state cannot be retried if the container is closed or disposed");
630
+ }
631
631
  (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
632
632
  (0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
633
633
  (0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
634
634
  (0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
635
635
  const pendingState = {
636
- pendingRuntimeState: this.context.getPendingLocalState(),
636
+ pendingRuntimeState: this.runtime.getPendingLocalState(),
637
637
  baseSnapshot: this.baseSnapshot,
638
638
  snapshotBlobs: this.baseSnapshotBlobs,
639
639
  savedOps: this.savedOps,
@@ -649,7 +649,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
649
649
  }
650
650
  serialize() {
651
651
  (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
652
- const appSummary = this.context.createSummary();
652
+ const appSummary = this.runtime.createSummary();
653
653
  const protocolSummary = this.captureProtocolSummary();
654
654
  const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
655
655
  if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
@@ -678,7 +678,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
678
678
  if (!hasAttachmentBlobs) {
679
679
  // Get the document state post attach - possibly can just call attach but we need to change the
680
680
  // semantics around what the attach means as far as async code goes.
681
- const appSummary = this.context.createSummary();
681
+ const appSummary = this.runtime.createSummary();
682
682
  const protocolSummary = this.captureProtocolSummary();
683
683
  summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
684
684
  // Set the state as attaching as we are starting the process of attaching container.
@@ -686,6 +686,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
686
686
  // starting to attach the container to storage.
687
687
  // Also, this should only be fired in detached container.
688
688
  this._attachState = container_definitions_1.AttachState.Attaching;
689
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
689
690
  this.emit("attaching");
690
691
  if (this.offlineLoadEnabled) {
691
692
  const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
@@ -700,7 +701,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
700
701
  (0, common_utils_1.assert)(this.client.details.type !== summarizerClientType &&
701
702
  createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
702
703
  this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
703
- cancel: this.closeSignal,
704
+ cancel: this._deltaManager.closeAbortController.signal,
704
705
  });
705
706
  }
706
707
  await this.storageAdapter.connectToService(this.service);
@@ -722,10 +723,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
722
723
  }
723
724
  }
724
725
  // take summary and upload
725
- const appSummary = this.context.createSummary(redirectTable);
726
+ const appSummary = this.runtime.createSummary(redirectTable);
726
727
  const protocolSummary = this.captureProtocolSummary();
727
728
  summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
728
729
  this._attachState = container_definitions_1.AttachState.Attaching;
730
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
729
731
  this.emit("attaching");
730
732
  if (this.offlineLoadEnabled) {
731
733
  const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
@@ -740,6 +742,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
740
742
  });
741
743
  }
742
744
  this._attachState = container_definitions_1.AttachState.Attached;
745
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
743
746
  this.emit("attached");
744
747
  if (!this.closed) {
745
748
  this.resumeInternal({
@@ -758,7 +761,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
758
761
  }, { start: true, end: true, cancel: "generic" });
759
762
  }
760
763
  async request(path) {
761
- return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
764
+ return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.runtime.request(path), { end: true, cancel: "error" });
762
765
  }
763
766
  setAutoReconnectInternal(mode) {
764
767
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
@@ -824,13 +827,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
824
827
  // Ensure connection to web socket
825
828
  this.connectToDeltaStream(args);
826
829
  }
827
- async getAbsoluteUrl(relativeUrl) {
828
- var _a;
829
- if (this.resolvedUrl === undefined) {
830
- return undefined;
831
- }
832
- return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
833
- }
834
830
  async proposeCodeDetails(codeDetails) {
835
831
  if (!(0, container_definitions_1.isFluidCodeDetails)(codeDetails)) {
836
832
  throw new Error("Provided codeDetails are not IFluidCodeDetails");
@@ -852,7 +848,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
852
848
  this.deltaManager.inbound.pause(),
853
849
  this.deltaManager.inboundSignal.pause(),
854
850
  ]);
855
- if ((await this.context.satisfies(codeDetails)) === true) {
851
+ if ((await this.satisfies(codeDetails)) === true) {
856
852
  this.deltaManager.inbound.resume();
857
853
  this.deltaManager.inboundSignal.resume();
858
854
  return;
@@ -861,6 +857,38 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
861
857
  const error = new container_utils_1.GenericError("Existing context does not satisfy incoming proposal");
862
858
  this.close(error);
863
859
  }
860
+ /**
861
+ * Determines if the currently loaded module satisfies the incoming constraint code details
862
+ */
863
+ async satisfies(constraintCodeDetails) {
864
+ var _a, _b;
865
+ // If we have no module, it can't satisfy anything.
866
+ if (this._loadedModule === undefined) {
867
+ return false;
868
+ }
869
+ const comparers = [];
870
+ const maybeCompareCodeLoader = this.codeLoader;
871
+ if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
872
+ comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
873
+ }
874
+ const maybeCompareExport = (_a = this._loadedModule) === null || _a === void 0 ? void 0 : _a.module.fluidExport;
875
+ if ((maybeCompareExport === null || maybeCompareExport === void 0 ? void 0 : maybeCompareExport.IFluidCodeDetailsComparer) !== undefined) {
876
+ comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
877
+ }
878
+ // If there are no comparers, then it's impossible to know if the currently loaded package satisfies
879
+ // the incoming constraint, so we return false. Assuming it does not satisfy is safer, to force a reload
880
+ // rather than potentially running with incompatible code.
881
+ if (comparers.length === 0) {
882
+ return false;
883
+ }
884
+ for (const comparer of comparers) {
885
+ const satisfies = await comparer.satisfies((_b = this._loadedModule) === null || _b === void 0 ? void 0 : _b.details, constraintCodeDetails);
886
+ if (satisfies === false) {
887
+ return false;
888
+ }
889
+ }
890
+ return true;
891
+ }
864
892
  async getVersion(version) {
865
893
  const versions = await this.storageAdapter.getVersions(version, 1);
866
894
  return versions[0];
@@ -878,7 +906,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
878
906
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
879
907
  */
880
908
  async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
881
- var _a;
909
+ var _a, _b, _c;
882
910
  this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
883
911
  // Ideally we always connect as "read" by default.
884
912
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
@@ -922,7 +950,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
922
950
  if (this.offlineLoadEnabled) {
923
951
  this.baseSnapshot = snapshot;
924
952
  // Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
925
- this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storage);
953
+ this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storageAdapter);
926
954
  }
927
955
  }
928
956
  const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
@@ -958,7 +986,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
958
986
  for (const message of pendingLocalState.savedOps) {
959
987
  this.processRemoteMessage(message);
960
988
  // allow runtime to apply stashed ops at this op's sequence number
961
- await this.context.notifyOpReplay(message);
989
+ await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
962
990
  }
963
991
  pendingLocalState.savedOps = [];
964
992
  // now set clientId to stashed clientId so live ops are correctly processed as local
@@ -1197,7 +1225,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1197
1225
  });
1198
1226
  deltaManager.on("disconnect", (reason, error) => {
1199
1227
  var _a;
1200
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1228
+ (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
1201
1229
  if (!this.closed) {
1202
1230
  this.connectionStateHandler.receivedDisconnectEvent(reason, error);
1203
1231
  }
@@ -1254,7 +1282,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1254
1282
  else if (value === connectionState_1.ConnectionState.CatchingUp) {
1255
1283
  // This info is of most interesting while Catching Up.
1256
1284
  checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
1257
- if (this.deltaManager.hasCheckpointSequenceNumber) {
1285
+ // Need to check that we have already loaded and fetched the snapshot.
1286
+ if (this.deltaManager.hasCheckpointSequenceNumber &&
1287
+ this._lifecycleState === "loaded") {
1258
1288
  opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
1259
1289
  }
1260
1290
  }
@@ -1341,7 +1371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1341
1371
  return -1;
1342
1372
  }
1343
1373
  this.messageCountAfterDisconnection += 1;
1344
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1374
+ (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
1345
1375
  return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
1346
1376
  }
1347
1377
  processRemoteMessage(message) {
@@ -1349,24 +1379,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1349
1379
  this.savedOps.push(message);
1350
1380
  }
1351
1381
  const local = this.clientId === message.clientId;
1382
+ // Check and report if we're getting messages from a clientId that we previously
1383
+ // flagged should have left, or from a client that's not in the quorum but should be
1384
+ if (message.clientId != null) {
1385
+ const client = this.protocolHandler.quorum.getMember(message.clientId);
1386
+ if (client === undefined && message.type !== protocol_definitions_1.MessageType.ClientJoin) {
1387
+ // pre-0.58 error message: messageClientIdMissingFromQuorum
1388
+ throw new Error("Remote message's clientId is missing from the quorum");
1389
+ }
1390
+ // Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
1391
+ // It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
1392
+ // document we don't need to blow up aggressively.
1393
+ if (this.clientsWhoShouldHaveLeft.has(message.clientId) &&
1394
+ !(0, driver_utils_1.canBeCoalescedByService)(message)) {
1395
+ // pre-0.58 error message: messageClientIdShouldHaveLeft
1396
+ throw new Error("Remote message's clientId already should have left");
1397
+ }
1398
+ }
1352
1399
  // Allow the protocol handler to process the message
1353
1400
  const result = this.protocolHandler.processMessage(message, local);
1354
1401
  // Forward messages to the loaded runtime for processing
1355
- this.context.process(message, local);
1402
+ this.runtime.process(message, local);
1356
1403
  // Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
1357
1404
  if (this.activeConnection()) {
1358
- if (this.collabWindowTracker === undefined) {
1405
+ if (this.noopHeuristic === undefined) {
1406
+ const serviceConfiguration = this.deltaManager.serviceConfiguration;
1359
1407
  // Note that config from first connection will be used for this container's lifetime.
1360
1408
  // That means that if relay service changes settings, such changes will impact only newly booted
1361
1409
  // clients.
1362
1410
  // All existing will continue to use settings they got earlier.
1363
- (0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
1364
- this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type) => {
1365
- (0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
1366
- this.submitMessage(type);
1367
- }, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
1411
+ (0, common_utils_1.assert)(serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
1412
+ this.noopHeuristic = new noopHeuristic_1.NoopHeuristic(serviceConfiguration.noopTimeFrequency, serviceConfiguration.noopCountFrequency);
1413
+ this.noopHeuristic.on("wantsNoop", () => {
1414
+ // On disconnect we notify the heuristic which should prevent it from wanting a noop.
1415
+ // Hitting this assert would imply we lost activeConnection between notifying the heuristic of a processed message and
1416
+ // running the microtask that the heuristic queued in response.
1417
+ (0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "Trying to send noop without active connection" */);
1418
+ this.submitMessage(protocol_definitions_1.MessageType.NoOp);
1419
+ });
1420
+ }
1421
+ this.noopHeuristic.notifyMessageProcessed(message);
1422
+ // The contract with the protocolHandler is that returning "immediateNoOp" is equivalent to "please immediately accept the proposal I just processed".
1423
+ if (result.immediateNoOp === true) {
1424
+ // ADO:1385: Remove cast and use MessageType once definition changes propagate
1425
+ this.submitMessage(driver_utils_1.MessageType2.Accept);
1368
1426
  }
1369
- this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
1370
1427
  }
1371
1428
  this.emit("op", message);
1372
1429
  }
@@ -1375,12 +1432,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1375
1432
  }
1376
1433
  processSignal(message) {
1377
1434
  // No clientId indicates a system signal message.
1378
- if (message.clientId === null) {
1435
+ if ((0, protocol_1.protocolHandlerShouldProcessSignal)(message)) {
1379
1436
  this.protocolHandler.processSignal(message);
1380
1437
  }
1381
1438
  else {
1382
1439
  const local = this.clientId === message.clientId;
1383
- this.context.processSignal(message, local);
1440
+ this.runtime.processSignal(message, local);
1384
1441
  }
1385
1442
  }
1386
1443
  /**
@@ -1413,21 +1470,37 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1413
1470
  await this.instantiateContext(existing, codeDetails, snapshot);
1414
1471
  }
1415
1472
  async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
1416
- var _a;
1417
- (0, common_utils_1.assert)(((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing context not disposed" */);
1473
+ var _a, _b;
1474
+ (0, common_utils_1.assert)(((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing runtime not disposed" */);
1418
1475
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1419
1476
  // are set. Global requests will still go directly to the loader
1420
1477
  const maybeLoader = this.scope;
1421
1478
  const loader = new loader_1.RelativeLoader(this, maybeLoader.ILoader);
1422
- this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), 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), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1423
- this.emit("contextChanged", codeDetails);
1424
- }
1425
- updateDirtyContainerState(dirty) {
1426
- if (this._dirtyContainer === dirty) {
1427
- return;
1479
+ const loadCodeResult = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "CodeLoad" }, async () => this.codeLoader.load(codeDetails));
1480
+ this._loadedModule = {
1481
+ module: loadCodeResult.module,
1482
+ // An older interface ICodeLoader could return an IFluidModule which didn't have details.
1483
+ // If we're using one of those older ICodeLoaders, then we fix up the module with the specified details here.
1484
+ // TODO: Determine if this is still a realistic scenario or if this fixup could be removed.
1485
+ details: (_b = loadCodeResult.details) !== null && _b !== void 0 ? _b : codeDetails,
1486
+ };
1487
+ const fluidExport = this._loadedModule.module.fluidExport;
1488
+ const runtimeFactory = fluidExport === null || fluidExport === void 0 ? void 0 : fluidExport.IRuntimeFactory;
1489
+ if (runtimeFactory === undefined) {
1490
+ throw new Error(packageNotFactoryError);
1428
1491
  }
1429
- this._dirtyContainer = dirty;
1430
- this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
1492
+ const getSpecifiedCodeDetails = () => {
1493
+ var _a;
1494
+ return ((_a = this.protocolHandler.quorum.get("code")) !== null && _a !== void 0 ? _a : this.protocolHandler.quorum.get("code2"));
1495
+ };
1496
+ const context = new containerContext_1.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);
1497
+ this._lifecycleEvents.once("disposed", () => {
1498
+ context.dispose();
1499
+ });
1500
+ this._runtime = await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
1501
+ this._lifecycleEvents.emit("runtimeInstantiated");
1502
+ this._loadedCodeDetails = codeDetails;
1503
+ this.emit("contextChanged", codeDetails);
1431
1504
  }
1432
1505
  /**
1433
1506
  * Set the connected state of the ContainerContext
@@ -1437,17 +1510,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1437
1510
  */
1438
1511
  setContextConnectedState(state, readonly) {
1439
1512
  var _a;
1440
- if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1513
+ if (((_a = this._runtime) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1441
1514
  /**
1442
1515
  * We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
1443
1516
  * ops getting through to the DeltaManager.
1444
1517
  * The ContainerRuntime's "connected" state simply means it is ok to send ops
1445
1518
  * See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
1446
1519
  */
1447
- this.context.setConnectionState(state && !readonly, this.clientId);
1520
+ this.runtime.setConnectionState(state && !readonly, this.clientId);
1448
1521
  }
1449
1522
  }
1450
1523
  }
1451
1524
  exports.Container = Container;
1452
- Container.version = "^0.1.0";
1453
1525
  //# sourceMappingURL=container.js.map