@fluidframework/container-loader 2.0.0-dev.4.4.0.162574 → 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 (179) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +27 -3
  3. package/dist/catchUpMonitor.d.ts +1 -1
  4. package/dist/catchUpMonitor.d.ts.map +1 -1
  5. package/dist/catchUpMonitor.js.map +1 -1
  6. package/dist/connectionManager.d.ts +3 -2
  7. package/dist/connectionManager.d.ts.map +1 -1
  8. package/dist/connectionManager.js +32 -13
  9. package/dist/connectionManager.js.map +1 -1
  10. package/dist/connectionStateHandler.d.ts +18 -3
  11. package/dist/connectionStateHandler.d.ts.map +1 -1
  12. package/dist/connectionStateHandler.js +34 -9
  13. package/dist/connectionStateHandler.js.map +1 -1
  14. package/dist/container.d.ts +99 -70
  15. package/dist/container.d.ts.map +1 -1
  16. package/dist/container.js +260 -218
  17. package/dist/container.js.map +1 -1
  18. package/dist/containerContext.d.ts +24 -67
  19. package/dist/containerContext.d.ts.map +1 -1
  20. package/dist/containerContext.js +28 -217
  21. package/dist/containerContext.js.map +1 -1
  22. package/dist/containerStorageAdapter.d.ts +3 -3
  23. package/dist/containerStorageAdapter.d.ts.map +1 -1
  24. package/dist/containerStorageAdapter.js +29 -6
  25. package/dist/containerStorageAdapter.js.map +1 -1
  26. package/dist/contracts.d.ts +9 -3
  27. package/dist/contracts.d.ts.map +1 -1
  28. package/dist/contracts.js.map +1 -1
  29. package/dist/deltaManager.d.ts +22 -9
  30. package/dist/deltaManager.d.ts.map +1 -1
  31. package/dist/deltaManager.js +42 -31
  32. package/dist/deltaManager.js.map +1 -1
  33. package/dist/deltaQueue.d.ts +2 -3
  34. package/dist/deltaQueue.d.ts.map +1 -1
  35. package/dist/deltaQueue.js +2 -3
  36. package/dist/deltaQueue.js.map +1 -1
  37. package/dist/disposal.d.ts +13 -0
  38. package/dist/disposal.d.ts.map +1 -0
  39. package/dist/disposal.js +25 -0
  40. package/dist/disposal.js.map +1 -0
  41. package/dist/index.d.ts +1 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/loader.d.ts +9 -8
  45. package/dist/loader.d.ts.map +1 -1
  46. package/dist/loader.js +47 -61
  47. package/dist/loader.js.map +1 -1
  48. package/dist/noopHeuristic.d.ts +23 -0
  49. package/dist/noopHeuristic.d.ts.map +1 -0
  50. package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  51. package/dist/noopHeuristic.js.map +1 -0
  52. package/dist/packageVersion.d.ts +1 -1
  53. package/dist/packageVersion.js +1 -1
  54. package/dist/packageVersion.js.map +1 -1
  55. package/dist/protocol.d.ts +7 -12
  56. package/dist/protocol.d.ts.map +1 -1
  57. package/dist/protocol.js +17 -19
  58. package/dist/protocol.js.map +1 -1
  59. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  60. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  61. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  62. package/dist/quorum.d.ts +1 -17
  63. package/dist/quorum.d.ts.map +1 -1
  64. package/dist/quorum.js +1 -17
  65. package/dist/quorum.js.map +1 -1
  66. package/dist/retriableDocumentStorageService.d.ts +3 -2
  67. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  68. package/dist/retriableDocumentStorageService.js.map +1 -1
  69. package/dist/tsdoc-metadata.json +11 -0
  70. package/dist/utils.d.ts +2 -0
  71. package/dist/utils.d.ts.map +1 -1
  72. package/dist/utils.js +8 -1
  73. package/dist/utils.js.map +1 -1
  74. package/lib/catchUpMonitor.d.ts +1 -1
  75. package/lib/catchUpMonitor.d.ts.map +1 -1
  76. package/lib/catchUpMonitor.js.map +1 -1
  77. package/lib/connectionManager.d.ts +3 -2
  78. package/lib/connectionManager.d.ts.map +1 -1
  79. package/lib/connectionManager.js +33 -14
  80. package/lib/connectionManager.js.map +1 -1
  81. package/lib/connectionStateHandler.d.ts +18 -3
  82. package/lib/connectionStateHandler.d.ts.map +1 -1
  83. package/lib/connectionStateHandler.js +35 -10
  84. package/lib/connectionStateHandler.js.map +1 -1
  85. package/lib/container.d.ts +99 -70
  86. package/lib/container.d.ts.map +1 -1
  87. package/lib/container.js +264 -222
  88. package/lib/container.js.map +1 -1
  89. package/lib/containerContext.d.ts +24 -67
  90. package/lib/containerContext.d.ts.map +1 -1
  91. package/lib/containerContext.js +28 -217
  92. package/lib/containerContext.js.map +1 -1
  93. package/lib/containerStorageAdapter.d.ts +3 -3
  94. package/lib/containerStorageAdapter.d.ts.map +1 -1
  95. package/lib/containerStorageAdapter.js +29 -6
  96. package/lib/containerStorageAdapter.js.map +1 -1
  97. package/lib/contracts.d.ts +9 -3
  98. package/lib/contracts.d.ts.map +1 -1
  99. package/lib/contracts.js.map +1 -1
  100. package/lib/deltaManager.d.ts +22 -9
  101. package/lib/deltaManager.d.ts.map +1 -1
  102. package/lib/deltaManager.js +44 -33
  103. package/lib/deltaManager.js.map +1 -1
  104. package/lib/deltaQueue.d.ts +2 -3
  105. package/lib/deltaQueue.d.ts.map +1 -1
  106. package/lib/deltaQueue.js +2 -3
  107. package/lib/deltaQueue.js.map +1 -1
  108. package/lib/disposal.d.ts +13 -0
  109. package/lib/disposal.d.ts.map +1 -0
  110. package/lib/disposal.js +21 -0
  111. package/lib/disposal.js.map +1 -0
  112. package/lib/index.d.ts +1 -2
  113. package/lib/index.d.ts.map +1 -1
  114. package/lib/index.js +1 -1
  115. package/lib/index.js.map +1 -1
  116. package/lib/loader.d.ts +9 -8
  117. package/lib/loader.d.ts.map +1 -1
  118. package/lib/loader.js +47 -61
  119. package/lib/loader.js.map +1 -1
  120. package/lib/noopHeuristic.d.ts +23 -0
  121. package/lib/noopHeuristic.d.ts.map +1 -0
  122. package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
  123. package/lib/noopHeuristic.js.map +1 -0
  124. package/lib/packageVersion.d.ts +1 -1
  125. package/lib/packageVersion.js +1 -1
  126. package/lib/packageVersion.js.map +1 -1
  127. package/lib/protocol.d.ts +7 -12
  128. package/lib/protocol.d.ts.map +1 -1
  129. package/lib/protocol.js +15 -18
  130. package/lib/protocol.js.map +1 -1
  131. package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
  132. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  133. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  134. package/lib/quorum.d.ts +1 -17
  135. package/lib/quorum.d.ts.map +1 -1
  136. package/lib/quorum.js +1 -16
  137. package/lib/quorum.js.map +1 -1
  138. package/lib/retriableDocumentStorageService.d.ts +3 -2
  139. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  140. package/lib/retriableDocumentStorageService.js.map +1 -1
  141. package/lib/utils.d.ts +2 -0
  142. package/lib/utils.d.ts.map +1 -1
  143. package/lib/utils.js +7 -1
  144. package/lib/utils.js.map +1 -1
  145. package/package.json +22 -20
  146. package/src/catchUpMonitor.ts +1 -1
  147. package/src/connectionManager.ts +40 -22
  148. package/src/connectionStateHandler.ts +66 -17
  149. package/src/container.ts +464 -292
  150. package/src/containerContext.ts +33 -341
  151. package/src/containerStorageAdapter.ts +40 -10
  152. package/src/contracts.ts +11 -3
  153. package/src/deltaManager.ts +74 -45
  154. package/src/deltaQueue.ts +2 -3
  155. package/src/disposal.ts +25 -0
  156. package/src/index.ts +1 -8
  157. package/src/loader.ts +85 -83
  158. package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
  159. package/src/packageVersion.ts +1 -1
  160. package/src/protocol.ts +18 -39
  161. package/src/protocolTreeDocumentStorageService.ts +1 -1
  162. package/src/quorum.ts +2 -31
  163. package/src/retriableDocumentStorageService.ts +4 -2
  164. package/src/utils.ts +15 -1
  165. package/dist/collabWindowTracker.d.ts +0 -19
  166. package/dist/collabWindowTracker.d.ts.map +0 -1
  167. package/dist/collabWindowTracker.js.map +0 -1
  168. package/dist/deltaManagerProxy.d.ts +0 -42
  169. package/dist/deltaManagerProxy.d.ts.map +0 -1
  170. package/dist/deltaManagerProxy.js +0 -79
  171. package/dist/deltaManagerProxy.js.map +0 -1
  172. package/lib/collabWindowTracker.d.ts +0 -19
  173. package/lib/collabWindowTracker.d.ts.map +0 -1
  174. package/lib/collabWindowTracker.js.map +0 -1
  175. package/lib/deltaManagerProxy.d.ts +0 -42
  176. package/lib/deltaManagerProxy.d.ts.map +0 -1
  177. package/lib/deltaManagerProxy.js +0 -74
  178. package/lib/deltaManagerProxy.js.map +0 -1
  179. 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
  *
@@ -127,26 +127,18 @@ async function ReportIfTooLong(logger, eventName, action) {
127
127
  }
128
128
  exports.ReportIfTooLong = ReportIfTooLong;
129
129
  const summarizerClientType = "summarizer";
130
- /**
131
- * @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
132
- */
133
130
  class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
134
131
  /**
135
132
  * @internal
136
133
  */
137
- constructor(loader, config, protocolHandlerBuilder) {
138
- var _a, _b, _c;
134
+ constructor(createProps, loadProps) {
135
+ var _a;
139
136
  super((name, error) => {
140
137
  this.mc.logger.sendErrorEvent({
141
138
  eventName: "ContainerEventHandlerException",
142
139
  name: typeof name === "string" ? name : undefined,
143
140
  }, error);
144
141
  });
145
- this.loader = loader;
146
- this.protocolHandlerBuilder = protocolHandlerBuilder;
147
- // Tells if container can reconnect on losing fist connection
148
- // If false, container gets closed on loss of connection.
149
- this._canReconnect = true;
150
142
  /**
151
143
  * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
152
144
  *
@@ -172,28 +164,58 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
172
164
  this.attachStarted = false;
173
165
  this._dirtyContainer = false;
174
166
  this.savedOps = [];
167
+ this.clientsWhoShouldHaveLeft = new Set();
175
168
  this.setAutoReconnectTime = common_utils_1.performance.now();
169
+ this._lifecycleEvents = new common_utils_1.TypedEventEmitter();
176
170
  this._disposed = false;
177
- this.clientDetailsOverride = config.clientDetailsOverride;
178
- this._resolvedUrl = config.resolvedUrl;
179
- if (config.canReconnect !== undefined) {
180
- this._canReconnect = config.canReconnect;
181
- }
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
+ };
184
+ const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
185
+ this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
186
+ const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
187
+ this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
188
+ this.clientDetailsOverride = clientDetailsOverride;
189
+ this.urlResolver = urlResolver;
190
+ this.serviceFactory = documentServiceFactory;
191
+ this.codeLoader = codeLoader;
192
+ // Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
193
+ // all clients that were loaded from the same loader (including summarizer clients).
194
+ // Tracking alternative ways to handle this in AB#4129.
195
+ this.options = Object.assign({}, options);
196
+ this.scope = scope;
197
+ this.detachedBlobStorage = detachedBlobStorage;
198
+ this.protocolHandlerBuilder =
199
+ protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
200
+ // Note that we capture the createProps here so we can replicate the creation call when we want to clone.
201
+ this.clone = async (_loadProps, createParamOverrides) => {
202
+ return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
203
+ };
182
204
  // Create logger for data stores to use
183
205
  const type = this.client.details.type;
184
206
  const interactive = this.client.details.capabilities.interactive;
185
207
  const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
186
208
  // Need to use the property getter for docId because for detached flow we don't have the docId initially.
187
209
  // We assign the id later so property getter is used.
188
- this.subLogger = telemetry_utils_1.ChildLogger.create(loader.services.subLogger, undefined, {
210
+ this.subLogger = telemetry_utils_1.ChildLogger.create(subLogger, undefined, {
189
211
  all: {
190
212
  clientType,
191
213
  containerId: (0, uuid_1.v4)(),
192
- docId: () => { var _a, _b; return (_b = (_a = this._resolvedUrl) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined; },
214
+ docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
193
215
  containerAttachState: () => this._attachState,
194
216
  containerLifecycleState: () => this._lifecycleState,
195
217
  containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
196
- serializedContainer: config.serializedContainerState !== undefined,
218
+ serializedContainer: pendingLocalState !== undefined,
197
219
  },
198
220
  // we need to be judicious with our logging here to avoid generating too much data
199
221
  // all data logged here should be broadly applicable, and not specific to a
@@ -203,23 +225,24 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
203
225
  dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
204
226
  dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
205
227
  dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
206
- containerLoadedFromVersionId: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
207
- 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; },
208
230
  // message information to associate errors with the specific execution state
209
231
  // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
210
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; },
211
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; },
212
- 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
+ },
213
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; },
214
241
  connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
215
242
  },
216
243
  });
217
244
  // Prefix all events in this file with container-loader
218
245
  this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
219
- // Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
220
- // all clients that were loaded from the same loader (including summarizer clients).
221
- // Tracking alternative ways to handle this in AB#4129.
222
- this.options = Object.assign({}, this.loader.services.options);
223
246
  this._deltaManager = this.createDeltaManager();
224
247
  this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
225
248
  logger: this.mc.logger,
@@ -235,7 +258,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
235
258
  }
236
259
  },
237
260
  shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
238
- maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
261
+ maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
239
262
  logConnectionIssue: (eventName, category, details) => {
240
263
  const mode = this.connectionMode;
241
264
  // We get here when socket does not receive any ops on "write" connection, including
@@ -259,7 +282,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
259
282
  this.connect();
260
283
  }
261
284
  },
262
- }, this.deltaManager, (_a = config.serializedContainerState) === null || _a === void 0 ? void 0 : _a.clientId);
285
+ clientShouldHaveLeft: (clientId) => {
286
+ this.clientsWhoShouldHaveLeft.add(clientId);
287
+ },
288
+ }, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
263
289
  this.on(savedContainerEvent, () => {
264
290
  this.connectionStateHandler.containerSaved();
265
291
  });
@@ -271,8 +297,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
271
297
  : (0, driver_utils_1.combineAppAndProtocolSummary)(summaryTree, this.captureProtocolSummary());
272
298
  // Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
273
299
  // Even if not forced on via this flag, combined summaries may still be enabled by service policy.
274
- const forceEnableSummarizeProtocolTree = (_b = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _b !== void 0 ? _b : this.loader.services.options.summarizeProtocolTree;
275
- this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, (_c = config.serializedContainerState) === null || _c === void 0 ? void 0 : _c.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
300
+ const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
301
+ this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
276
302
  const isDomAvailable = typeof document === "object" &&
277
303
  document !== null &&
278
304
  typeof document.addEventListener === "function" &&
@@ -298,33 +324,28 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
298
324
  * Load an existing container.
299
325
  * @internal
300
326
  */
301
- static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
302
- const container = new Container(loader, {
303
- clientDetailsOverride: loadOptions.clientDetailsOverride,
304
- resolvedUrl: loadOptions.resolvedUrl,
305
- canReconnect: loadOptions.canReconnect,
306
- serializedContainerState: pendingLocalState,
307
- }, protocolHandlerBuilder);
327
+ static async load(loadProps, createProps) {
328
+ const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
329
+ const container = new Container(createProps, loadProps);
330
+ const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
308
331
  return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
309
- var _a, _b;
310
- const version = loadOptions.version;
311
332
  const defaultMode = { opsBeforeReturn: "cached" };
312
333
  // if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
313
334
  // to return container, so ignore this value and use undefined for opsBeforeReturn
314
335
  const mode = pendingLocalState
315
- ? Object.assign(Object.assign({}, ((_a = loadOptions.loadMode) !== null && _a !== void 0 ? _a : defaultMode)), { opsBeforeReturn: undefined }) : (_b = loadOptions.loadMode) !== null && _b !== void 0 ? _b : defaultMode;
336
+ ? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
316
337
  const onClosed = (err) => {
317
338
  // pre-0.58 error message: containerClosedWithoutErrorDuringLoad
318
339
  reject(err !== null && err !== void 0 ? err : new container_utils_1.GenericError("Container closed without error during load"));
319
340
  };
320
341
  container.on("closed", onClosed);
321
342
  container
322
- .load(version, mode, pendingLocalState)
343
+ .load(version, mode, resolvedUrl, pendingLocalState)
323
344
  .finally(() => {
324
345
  container.removeListener("closed", onClosed);
325
346
  })
326
347
  .then((props) => {
327
- event.end(Object.assign(Object.assign({}, props), loadOptions.loadMode));
348
+ event.end(Object.assign(Object.assign({}, props), loadMode));
328
349
  resolve(container);
329
350
  }, (error) => {
330
351
  const err = (0, telemetry_utils_1.normalizeError)(error);
@@ -334,13 +355,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
334
355
  container.close(err);
335
356
  onClosed(err);
336
357
  });
337
- }), { start: true, end: true, cancel: "generic" });
358
+ }), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
338
359
  }
339
360
  /**
340
361
  * Create a new container in a detached state.
341
362
  */
342
- static async createDetached(loader, codeDetails, protocolHandlerBuilder) {
343
- const container = new Container(loader, {}, protocolHandlerBuilder);
363
+ static async createDetached(createProps, codeDetails) {
364
+ const container = new Container(createProps);
344
365
  return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
345
366
  await container.createDetached(codeDetails);
346
367
  return container;
@@ -350,8 +371,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
350
371
  * Create a new container in a detached state that is initialized with a
351
372
  * snapshot from a previous detached container.
352
373
  */
353
- static async rehydrateDetachedFromSnapshot(loader, snapshot, protocolHandlerBuilder) {
354
- const container = new Container(loader, {}, protocolHandlerBuilder);
374
+ static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
375
+ const container = new Container(createProps);
355
376
  return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
356
377
  const deserializedSummary = JSON.parse(snapshot);
357
378
  await container.rehydrateDetachedFromSnapshot(deserializedSummary);
@@ -373,14 +394,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
373
394
  this._lifecycleState === "disposing" ||
374
395
  this._lifecycleState === "disposed");
375
396
  }
376
- get storage() {
377
- return this.storageAdapter;
378
- }
379
- get context() {
380
- if (this._context === undefined) {
381
- 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");
382
400
  }
383
- return this._context;
401
+ return this._runtime;
384
402
  }
385
403
  get protocolHandler() {
386
404
  if (this._protocolHandler === undefined) {
@@ -395,17 +413,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
395
413
  return this;
396
414
  }
397
415
  get resolvedUrl() {
398
- return this._resolvedUrl;
399
- }
400
- get loadedFromVersion() {
401
- return this._loadedFromVersion;
416
+ var _a;
417
+ /**
418
+ * All attached containers will have a document service,
419
+ * this is required, as attached containers are attached to
420
+ * a service. Detached containers will neither have a document
421
+ * service or a resolved url as they only exist locally.
422
+ * in order to create a document service a resolved url must
423
+ * first be obtained, this is how the container is identified.
424
+ * Because of this, the document service's resolved url
425
+ * is always the same as the containers, as we had to
426
+ * obtain the resolved url, and then create the service from it.
427
+ */
428
+ return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
402
429
  }
403
430
  get readOnlyInfo() {
404
431
  return this._deltaManager.readOnlyInfo;
405
432
  }
406
- get closeSignal() {
407
- return this._deltaManager.closeAbortController.signal;
408
- }
409
433
  /**
410
434
  * Tracks host requiring read-only mode.
411
435
  */
@@ -421,13 +445,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
421
445
  get connected() {
422
446
  return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
423
447
  }
424
- /**
425
- * Service configuration details. If running in offline mode will be undefined otherwise will contain service
426
- * configuration details returned as part of the initial connection.
427
- */
428
- get serviceConfiguration() {
429
- return this._deltaManager.serviceConfiguration;
430
- }
431
448
  /**
432
449
  * The server provided id of the client.
433
450
  * Set once this.connected is true, otherwise undefined
@@ -435,21 +452,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
435
452
  get clientId() {
436
453
  return this._clientId;
437
454
  }
438
- /**
439
- * The server provided claims of the client.
440
- * Set once this.connected is true, otherwise undefined
441
- */
442
- get scopes() {
443
- return this._deltaManager.connectionManager.scopes;
444
- }
445
- get clientDetails() {
446
- return this._deltaManager.clientDetails;
447
- }
448
455
  get offlineLoadEnabled() {
449
456
  var _a, _b;
450
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;
451
458
  // summarizer will not have any pending state we want to save
452
- return enabled && this.clientDetails.capabilities.interactive;
459
+ return enabled && this.deltaManager.clientDetails.capabilities.interactive;
453
460
  }
454
461
  /**
455
462
  * Get the code details that are currently specified for the container.
@@ -464,8 +471,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
464
471
  * loaded.
465
472
  */
466
473
  getLoadedCodeDetails() {
467
- var _a;
468
- return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
474
+ return this._loadedCodeDetails;
469
475
  }
470
476
  /**
471
477
  * Retrieves the audience associated with the document
@@ -481,49 +487,31 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
481
487
  get isDirty() {
482
488
  return this._dirtyContainer;
483
489
  }
484
- get serviceFactory() {
485
- return this.loader.services.documentServiceFactory;
486
- }
487
- get urlResolver() {
488
- return this.loader.services.urlResolver;
489
- }
490
- get scope() {
491
- return this.loader.services.scope;
492
- }
493
- get codeLoader() {
494
- return this.loader.services.codeLoader;
495
- }
496
490
  /**
497
491
  * {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
498
492
  */
499
493
  async getEntryPoint() {
500
494
  var _a, _b;
501
- // Only the disposing/disposed lifecycle states should prevent access to the entryPoint; closing/closed should still
502
- // allow it since they mean a kind of read-only state for the Container.
503
- // Note that all 4 are lifecycle states but only 'closed' and 'disposed' are emitted as events.
504
- if (this._lifecycleState === "disposing" || this._lifecycleState === "disposed") {
505
- throw new container_utils_1.UsageError("The container is disposing or disposed");
506
- }
507
- while (this._context === undefined) {
508
- await new Promise((resolve, reject) => {
509
- const contextChangedHandler = () => {
510
- resolve();
511
- this.off("disposed", disposedHandler);
512
- };
513
- const disposedHandler = (error) => {
514
- reject(error !== null && error !== void 0 ? error : "The Container is disposed");
515
- this.off("contextChanged", contextChangedHandler);
516
- };
517
- this.once("contextChanged", contextChangedHandler);
518
- this.once("disposed", disposedHandler);
519
- });
520
- // The Promise above should only resolve (vs reject) if the 'contextChanged' event was emitted and that
521
- // should have set this._context; making sure.
522
- (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);
523
500
  }
524
- // Disable lint rule for the sake of more complete stack traces
525
- // eslint-disable-next-line no-return-await
526
- 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
+ });
527
515
  }
528
516
  /**
529
517
  * Retrieves the quorum associated with the document
@@ -532,7 +520,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
532
520
  return this.protocolHandler.quorum;
533
521
  }
534
522
  dispose(error) {
535
- this._deltaManager.close(error, true /* doDispose */);
523
+ this._deltaManager.dispose(error);
536
524
  this.verifyClosed();
537
525
  }
538
526
  close(error) {
@@ -548,7 +536,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
548
536
  (0, common_utils_1.assert)(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
549
537
  }
550
538
  closeCore(error) {
551
- var _a, _b, _c;
539
+ var _a;
552
540
  (0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
553
541
  try {
554
542
  // Ensure that we raise all key events even if one of these throws
@@ -566,12 +554,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
566
554
  this._lifecycleState = "closing";
567
555
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
568
556
  this.connectionStateHandler.dispose();
569
- (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
570
- this.storageAdapter.dispose();
571
- // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
572
- // about file, like file being overwritten in storage, but client having stale local cache.
573
- // Driver need to ensure all caches are cleared on critical errors
574
- (_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
575
557
  }
576
558
  catch (exception) {
577
559
  this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
@@ -583,6 +565,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
583
565
  }
584
566
  finally {
585
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
+ }
586
572
  }
587
573
  }
588
574
  disposeCore(error) {
@@ -596,7 +582,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
596
582
  // This gives us a chance to know what errors happened on open vs. on fully loaded container.
597
583
  this.mc.logger.sendTelemetryEvent({
598
584
  eventName: "ContainerDispose",
599
- category: "generic",
585
+ // Only log error if container isn't closed
586
+ category: !this.closed && error !== undefined ? "error" : "generic",
600
587
  }, error);
601
588
  // ! Progressing from "closed" to "disposing" is not allowed
602
589
  if (this._lifecycleState !== "closed") {
@@ -604,7 +591,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
604
591
  }
605
592
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
606
593
  this.connectionStateHandler.dispose();
607
- (_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);
608
596
  this.storageAdapter.dispose();
609
597
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
610
598
  // about file, like file being overwritten in storage, but client having stale local cache.
@@ -622,6 +610,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
622
610
  }
623
611
  finally {
624
612
  this._lifecycleState = "disposed";
613
+ this._lifecycleEvents.emit("disposed");
625
614
  }
626
615
  }
627
616
  closeAndGetPendingLocalState() {
@@ -636,12 +625,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
636
625
  if (!this.offlineLoadEnabled) {
637
626
  throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
638
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
+ }
639
631
  (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
640
632
  (0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
641
633
  (0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
642
634
  (0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
643
635
  const pendingState = {
644
- pendingRuntimeState: this.context.getPendingLocalState(),
636
+ pendingRuntimeState: this.runtime.getPendingLocalState(),
645
637
  baseSnapshot: this.baseSnapshot,
646
638
  snapshotBlobs: this.baseSnapshotBlobs,
647
639
  savedOps: this.savedOps,
@@ -657,11 +649,10 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
657
649
  }
658
650
  serialize() {
659
651
  (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
660
- const appSummary = this.context.createSummary();
652
+ const appSummary = this.runtime.createSummary();
661
653
  const protocolSummary = this.captureProtocolSummary();
662
654
  const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
663
- if (this.loader.services.detachedBlobStorage &&
664
- this.loader.services.detachedBlobStorage.size > 0) {
655
+ if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
665
656
  combinedSummary.tree[".hasAttachmentBlobs"] = {
666
657
  type: protocol_definitions_1.SummaryType.Blob,
667
658
  content: "true",
@@ -680,15 +671,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
680
671
  (0, common_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
681
672
  this.attachStarted = true;
682
673
  // If attachment blobs were uploaded in detached state we will go through a different attach flow
683
- const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined &&
684
- this.loader.services.detachedBlobStorage.size > 0;
674
+ const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
685
675
  try {
686
676
  (0, common_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
687
677
  let summary;
688
678
  if (!hasAttachmentBlobs) {
689
679
  // Get the document state post attach - possibly can just call attach but we need to change the
690
680
  // semantics around what the attach means as far as async code goes.
691
- const appSummary = this.context.createSummary();
681
+ const appSummary = this.runtime.createSummary();
692
682
  const protocolSummary = this.captureProtocolSummary();
693
683
  summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
694
684
  // Set the state as attaching as we are starting the process of attaching container.
@@ -696,6 +686,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
696
686
  // starting to attach the container to storage.
697
687
  // Also, this should only be fired in detached container.
698
688
  this._attachState = container_definitions_1.AttachState.Attaching;
689
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
699
690
  this.emit("attaching");
700
691
  if (this.offlineLoadEnabled) {
701
692
  const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
@@ -705,40 +696,38 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
705
696
  }
706
697
  }
707
698
  // Actually go and create the resolved document
708
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
709
- (0, driver_utils_1.ensureFluidResolvedUrl)(createNewResolvedUrl);
710
699
  if (this.service === undefined) {
711
- (0, common_utils_1.assert)(this.client.details.type !== summarizerClientType, 0x2c4 /* "client should not be summarizer before container is created" */);
700
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
701
+ (0, common_utils_1.assert)(this.client.details.type !== summarizerClientType &&
702
+ createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
712
703
  this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
713
- cancel: this.closeSignal,
704
+ cancel: this._deltaManager.closeAbortController.signal,
714
705
  });
715
706
  }
716
- const resolvedUrl = this.service.resolvedUrl;
717
- (0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
718
- this._resolvedUrl = resolvedUrl;
719
707
  await this.storageAdapter.connectToService(this.service);
720
708
  if (hasAttachmentBlobs) {
721
709
  // upload blobs to storage
722
- (0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
710
+ (0, common_utils_1.assert)(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
723
711
  // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
724
712
  // support blob handles that only know about the local IDs
725
713
  const redirectTable = new Map();
726
714
  // if new blobs are added while uploading, upload them too
727
- while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
728
- const newIds = this.loader.services.detachedBlobStorage
715
+ while (redirectTable.size < this.detachedBlobStorage.size) {
716
+ const newIds = this.detachedBlobStorage
729
717
  .getBlobIds()
730
718
  .filter((id) => !redirectTable.has(id));
731
719
  for (const id of newIds) {
732
- const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
720
+ const blob = await this.detachedBlobStorage.readBlob(id);
733
721
  const response = await this.storageAdapter.createBlob(blob);
734
722
  redirectTable.set(id, response.id);
735
723
  }
736
724
  }
737
725
  // take summary and upload
738
- const appSummary = this.context.createSummary(redirectTable);
726
+ const appSummary = this.runtime.createSummary(redirectTable);
739
727
  const protocolSummary = this.captureProtocolSummary();
740
728
  summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
741
729
  this._attachState = container_definitions_1.AttachState.Attaching;
730
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attaching);
742
731
  this.emit("attaching");
743
732
  if (this.offlineLoadEnabled) {
744
733
  const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
@@ -753,6 +742,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
753
742
  });
754
743
  }
755
744
  this._attachState = container_definitions_1.AttachState.Attached;
745
+ this.runtime.setAttachState(container_definitions_1.AttachState.Attached);
756
746
  this.emit("attached");
757
747
  if (!this.closed) {
758
748
  this.resumeInternal({
@@ -764,18 +754,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
764
754
  catch (error) {
765
755
  // add resolved URL on error object so that host has the ability to find this document and delete it
766
756
  const newError = (0, telemetry_utils_1.normalizeError)(error);
767
- const resolvedUrl = this.resolvedUrl;
768
- if ((0, driver_utils_1.isFluidResolvedUrl)(resolvedUrl)) {
769
- newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
770
- }
757
+ newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
771
758
  this.close(newError);
772
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
773
759
  throw newError;
774
760
  }
775
761
  }, { start: true, end: true, cancel: "generic" });
776
762
  }
777
763
  async request(path) {
778
- 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" });
779
765
  }
780
766
  setAutoReconnectInternal(mode) {
781
767
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
@@ -841,13 +827,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
841
827
  // Ensure connection to web socket
842
828
  this.connectToDeltaStream(args);
843
829
  }
844
- async getAbsoluteUrl(relativeUrl) {
845
- var _a;
846
- if (this.resolvedUrl === undefined) {
847
- return undefined;
848
- }
849
- return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
850
- }
851
830
  async proposeCodeDetails(codeDetails) {
852
831
  if (!(0, container_definitions_1.isFluidCodeDetails)(codeDetails)) {
853
832
  throw new Error("Provided codeDetails are not IFluidCodeDetails");
@@ -864,13 +843,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
864
843
  .catch(() => false);
865
844
  }
866
845
  async processCodeProposal() {
867
- var _a;
868
846
  const codeDetails = this.getCodeDetailsFromQuorum();
869
847
  await Promise.all([
870
848
  this.deltaManager.inbound.pause(),
871
849
  this.deltaManager.inboundSignal.pause(),
872
850
  ]);
873
- if ((await this.context.satisfies(codeDetails)) === true) {
851
+ if ((await this.satisfies(codeDetails)) === true) {
874
852
  this.deltaManager.inbound.resume();
875
853
  this.deltaManager.inboundSignal.resume();
876
854
  return;
@@ -878,19 +856,44 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
878
856
  // pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
879
857
  const error = new container_utils_1.GenericError("Existing context does not satisfy incoming proposal");
880
858
  this.close(error);
881
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
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;
882
891
  }
883
892
  async getVersion(version) {
884
893
  const versions = await this.storageAdapter.getVersions(version, 1);
885
894
  return versions[0];
886
895
  }
887
- recordConnectStartTime() {
888
- if (this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] === undefined) {
889
- this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
890
- }
891
- }
892
896
  connectToDeltaStream(args) {
893
- this.recordConnectStartTime();
894
897
  // All agents need "write" access, including summarizer.
895
898
  if (!this._canReconnect || !this.client.details.capabilities.interactive) {
896
899
  args.mode = "write";
@@ -902,12 +905,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
902
905
  *
903
906
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
904
907
  */
905
- async load(specifiedVersion, loadMode, pendingLocalState) {
906
- var _a;
907
- if (this._resolvedUrl === undefined) {
908
- throw new Error("Attempting to load without a resolved url");
909
- }
910
- this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
908
+ async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
909
+ var _a, _b, _c;
910
+ this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
911
911
  // Ideally we always connect as "read" by default.
912
912
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
913
913
  // We should not rely on it by (one of them will address the issue, but we need to address both)
@@ -933,9 +933,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
933
933
  else {
934
934
  // if we have pendingLocalState we can load without storage; don't wait for connection
935
935
  this.storageAdapter.connectToService(this.service).catch((error) => {
936
- var _a;
937
936
  this.close(error);
938
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
939
937
  });
940
938
  }
941
939
  this._attachState = container_definitions_1.AttachState.Attached;
@@ -952,7 +950,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
952
950
  if (this.offlineLoadEnabled) {
953
951
  this.baseSnapshot = snapshot;
954
952
  // Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
955
- this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storage);
953
+ this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storageAdapter);
956
954
  }
957
955
  }
958
956
  const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
@@ -988,7 +986,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
988
986
  for (const message of pendingLocalState.savedOps) {
989
987
  this.processRemoteMessage(message);
990
988
  // allow runtime to apply stashed ops at this op's sequence number
991
- await this.context.notifyOpReplay(message);
989
+ await ((_c = (_b = this.runtime).notifyOpReplay) === null || _c === void 0 ? void 0 : _c.call(_b, message));
992
990
  }
993
991
  pendingLocalState.savedOps = [];
994
992
  // now set clientId to stashed clientId so live ops are correctly processed as local
@@ -1061,8 +1059,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1061
1059
  }
1062
1060
  async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
1063
1061
  if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
1064
- (0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage &&
1065
- this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
1062
+ (0, common_utils_1.assert)(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
1066
1063
  delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
1067
1064
  }
1068
1065
  const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
@@ -1115,9 +1112,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1115
1112
  this.initializeProtocolState(attributes, quorumSnapshot);
1116
1113
  }
1117
1114
  initializeProtocolState(attributes, quorumSnapshot) {
1118
- var _a;
1119
- const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
1120
- const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
1115
+ const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
1121
1116
  const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
1122
1117
  protocol.quorum.on("error", (error) => {
1123
1118
  protocolLogger.sendErrorEvent(error);
@@ -1137,10 +1132,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1137
1132
  });
1138
1133
  }
1139
1134
  this.processCodeProposal().catch((error) => {
1140
- var _a;
1141
1135
  const normalizedError = (0, telemetry_utils_1.normalizeError)(error);
1142
1136
  this.close(normalizedError);
1143
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
1144
1137
  throw error;
1145
1138
  });
1146
1139
  }
@@ -1224,9 +1217,15 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1224
1217
  (0, common_utils_1.assert)(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
1225
1218
  this.connectionStateHandler.receivedConnectEvent(details);
1226
1219
  });
1220
+ deltaManager.on("establishingConnection", (reason) => {
1221
+ this.connectionStateHandler.establishingConnection(reason);
1222
+ });
1223
+ deltaManager.on("cancelEstablishingConnection", (reason) => {
1224
+ this.connectionStateHandler.cancelEstablishingConnection(reason);
1225
+ });
1227
1226
  deltaManager.on("disconnect", (reason, error) => {
1228
1227
  var _a;
1229
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1228
+ (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyDisconnect();
1230
1229
  if (!this.closed) {
1231
1230
  this.connectionStateHandler.receivedDisconnectEvent(reason, error);
1232
1231
  }
@@ -1280,10 +1279,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1280
1279
  time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
1281
1280
  durationFromDisconnected = telemetry_utils_1.TelemetryLogger.formatTick(durationFromDisconnected);
1282
1281
  }
1283
- else {
1284
- // This info is of most interest on establishing connection only.
1282
+ else if (value === connectionState_1.ConnectionState.CatchingUp) {
1283
+ // This info is of most interesting while Catching Up.
1285
1284
  checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
1286
- 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") {
1287
1288
  opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
1288
1289
  }
1289
1290
  }
@@ -1330,7 +1331,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1330
1331
  }
1331
1332
  // back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
1332
1333
  submitContainerMessage(type, contents, batch, metadata) {
1333
- var _a;
1334
1334
  switch (type) {
1335
1335
  case protocol_definitions_1.MessageType.Operation:
1336
1336
  return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
@@ -1339,7 +1339,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1339
1339
  default: {
1340
1340
  const newError = new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
1341
1341
  this.close(newError);
1342
- (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
1343
1342
  return -1;
1344
1343
  }
1345
1344
  }
@@ -1372,7 +1371,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1372
1371
  return -1;
1373
1372
  }
1374
1373
  this.messageCountAfterDisconnection += 1;
1375
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1374
+ (_a = this.noopHeuristic) === null || _a === void 0 ? void 0 : _a.notifyMessageSent();
1376
1375
  return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
1377
1376
  }
1378
1377
  processRemoteMessage(message) {
@@ -1380,24 +1379,51 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1380
1379
  this.savedOps.push(message);
1381
1380
  }
1382
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
+ }
1383
1399
  // Allow the protocol handler to process the message
1384
1400
  const result = this.protocolHandler.processMessage(message, local);
1385
1401
  // Forward messages to the loaded runtime for processing
1386
- this.context.process(message, local);
1402
+ this.runtime.process(message, local);
1387
1403
  // Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
1388
1404
  if (this.activeConnection()) {
1389
- if (this.collabWindowTracker === undefined) {
1405
+ if (this.noopHeuristic === undefined) {
1406
+ const serviceConfiguration = this.deltaManager.serviceConfiguration;
1390
1407
  // Note that config from first connection will be used for this container's lifetime.
1391
1408
  // That means that if relay service changes settings, such changes will impact only newly booted
1392
1409
  // clients.
1393
1410
  // All existing will continue to use settings they got earlier.
1394
- (0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
1395
- this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type) => {
1396
- (0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
1397
- this.submitMessage(type);
1398
- }, 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);
1399
1426
  }
1400
- this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
1401
1427
  }
1402
1428
  this.emit("op", message);
1403
1429
  }
@@ -1406,12 +1432,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1406
1432
  }
1407
1433
  processSignal(message) {
1408
1434
  // No clientId indicates a system signal message.
1409
- if (message.clientId === null) {
1435
+ if ((0, protocol_1.protocolHandlerShouldProcessSignal)(message)) {
1410
1436
  this.protocolHandler.processSignal(message);
1411
1437
  }
1412
1438
  else {
1413
1439
  const local = this.clientId === message.clientId;
1414
- this.context.processSignal(message, local);
1440
+ this.runtime.processSignal(message, local);
1415
1441
  }
1416
1442
  }
1417
1443
  /**
@@ -1444,20 +1470,37 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1444
1470
  await this.instantiateContext(existing, codeDetails, snapshot);
1445
1471
  }
1446
1472
  async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
1447
- var _a;
1448
- (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" */);
1449
1475
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1450
1476
  // are set. Global requests will still go directly to the loader
1451
- const loader = new loader_1.RelativeLoader(this, this.loader);
1452
- 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) => { var _a; return (_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error); }, (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1453
- this.emit("contextChanged", codeDetails);
1454
- }
1455
- updateDirtyContainerState(dirty) {
1456
- if (this._dirtyContainer === dirty) {
1457
- return;
1477
+ const maybeLoader = this.scope;
1478
+ const loader = new loader_1.RelativeLoader(this, maybeLoader.ILoader);
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);
1458
1491
  }
1459
- this._dirtyContainer = dirty;
1460
- 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);
1461
1504
  }
1462
1505
  /**
1463
1506
  * Set the connected state of the ContainerContext
@@ -1467,17 +1510,16 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1467
1510
  */
1468
1511
  setContextConnectedState(state, readonly) {
1469
1512
  var _a;
1470
- 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) {
1471
1514
  /**
1472
1515
  * We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
1473
1516
  * ops getting through to the DeltaManager.
1474
1517
  * The ContainerRuntime's "connected" state simply means it is ok to send ops
1475
1518
  * See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
1476
1519
  */
1477
- this.context.setConnectionState(state && !readonly, this.clientId);
1520
+ this.runtime.setConnectionState(state && !readonly, this.clientId);
1478
1521
  }
1479
1522
  }
1480
1523
  }
1481
1524
  exports.Container = Container;
1482
- Container.version = "^0.1.0";
1483
1525
  //# sourceMappingURL=container.js.map