@fluidframework/container-runtime 2.118.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +68 -4
  2. package/README.md +53 -50
  3. package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
  4. package/api-report/container-runtime.legacy.alpha.api.md +3 -1
  5. package/api-report/container-runtime.legacy.beta.api.md +3 -1
  6. package/container-runtime.test-files.tar +0 -0
  7. package/dist/batchTracker.d.ts.map +1 -1
  8. package/dist/batchTracker.js +5 -1
  9. package/dist/batchTracker.js.map +1 -1
  10. package/dist/blobManager/blobManager.d.ts +1 -1
  11. package/dist/blobManager/blobManager.d.ts.map +1 -1
  12. package/dist/blobManager/blobManager.js +264 -231
  13. package/dist/blobManager/blobManager.js.map +1 -1
  14. package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  15. package/dist/channelCollection.d.ts.map +1 -1
  16. package/dist/channelCollection.js +70 -58
  17. package/dist/channelCollection.js.map +1 -1
  18. package/dist/connectionTelemetry.js +38 -20
  19. package/dist/connectionTelemetry.js.map +1 -1
  20. package/dist/containerCompatibility.d.ts +3 -3
  21. package/dist/containerCompatibility.d.ts.map +1 -1
  22. package/dist/containerCompatibility.js +36 -45
  23. package/dist/containerCompatibility.js.map +1 -1
  24. package/dist/containerHandleContext.d.ts.map +1 -1
  25. package/dist/containerHandleContext.js +4 -0
  26. package/dist/containerHandleContext.js.map +1 -1
  27. package/dist/containerRuntime.d.ts +4 -4
  28. package/dist/containerRuntime.d.ts.map +1 -1
  29. package/dist/containerRuntime.js +346 -223
  30. package/dist/containerRuntime.js.map +1 -1
  31. package/dist/dataStore.d.ts.map +1 -1
  32. package/dist/dataStore.js +9 -1
  33. package/dist/dataStore.js.map +1 -1
  34. package/dist/dataStoreContext.d.ts.map +1 -1
  35. package/dist/dataStoreContext.js +184 -127
  36. package/dist/dataStoreContext.js.map +1 -1
  37. package/dist/dataStoreContexts.js +43 -42
  38. package/dist/dataStoreContexts.js.map +1 -1
  39. package/dist/dataStoreRegistry.js +1 -0
  40. package/dist/dataStoreRegistry.js.map +1 -1
  41. package/dist/deltaManagerProxies.js +5 -0
  42. package/dist/deltaManagerProxies.js.map +1 -1
  43. package/dist/deltaScheduler.js +104 -99
  44. package/dist/deltaScheduler.js.map +1 -1
  45. package/dist/error.js +2 -1
  46. package/dist/error.js.map +1 -1
  47. package/dist/gc/garbageCollection.js +85 -55
  48. package/dist/gc/garbageCollection.js.map +1 -1
  49. package/dist/gc/gcConfigs.js +2 -3
  50. package/dist/gc/gcConfigs.js.map +1 -1
  51. package/dist/gc/gcHelpers.js +12 -13
  52. package/dist/gc/gcHelpers.js.map +1 -1
  53. package/dist/gc/gcReferenceGraphAlgorithm.js +1 -2
  54. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
  55. package/dist/gc/gcSummaryStateTracker.js +8 -3
  56. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  57. package/dist/gc/gcTelemetry.js +12 -5
  58. package/dist/gc/gcTelemetry.js.map +1 -1
  59. package/dist/gc/gcUnreferencedStateTracker.js +18 -1
  60. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  61. package/dist/inboundBatchAggregator.js +122 -117
  62. package/dist/inboundBatchAggregator.js.map +1 -1
  63. package/dist/metadata.d.ts.map +1 -1
  64. package/dist/metadata.js +3 -3
  65. package/dist/metadata.js.map +1 -1
  66. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  67. package/dist/opLifecycle/batchManager.js +11 -5
  68. package/dist/opLifecycle/batchManager.js.map +1 -1
  69. package/dist/opLifecycle/duplicateBatchDetector.js +18 -18
  70. package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
  71. package/dist/opLifecycle/index.d.ts +2 -2
  72. package/dist/opLifecycle/index.d.ts.map +1 -1
  73. package/dist/opLifecycle/index.js +2 -1
  74. package/dist/opLifecycle/index.js.map +1 -1
  75. package/dist/opLifecycle/opCompressor.js +2 -1
  76. package/dist/opLifecycle/opCompressor.js.map +1 -1
  77. package/dist/opLifecycle/opDecompressor.js +10 -6
  78. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  79. package/dist/opLifecycle/opGroupingManager.js +5 -3
  80. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  81. package/dist/opLifecycle/opSerialization.d.ts +11 -1
  82. package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
  83. package/dist/opLifecycle/opSerialization.js +22 -6
  84. package/dist/opLifecycle/opSerialization.js.map +1 -1
  85. package/dist/opLifecycle/opSplitter.d.ts +17 -1
  86. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  87. package/dist/opLifecycle/opSplitter.js +46 -9
  88. package/dist/opLifecycle/opSplitter.js.map +1 -1
  89. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  90. package/dist/opLifecycle/outbox.js +17 -13
  91. package/dist/opLifecycle/outbox.js.map +1 -1
  92. package/dist/opLifecycle/remoteMessageProcessor.js +6 -3
  93. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  94. package/dist/opProperties.d.ts.map +1 -1
  95. package/dist/packageVersion.d.ts +1 -1
  96. package/dist/packageVersion.d.ts.map +1 -1
  97. package/dist/packageVersion.js +1 -1
  98. package/dist/packageVersion.js.map +1 -1
  99. package/dist/pendingStateManager.js +54 -47
  100. package/dist/pendingStateManager.js.map +1 -1
  101. package/dist/runCounter.js +10 -30
  102. package/dist/runCounter.js.map +1 -1
  103. package/dist/runtimeLayerCompatState.d.ts +1 -1
  104. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  105. package/dist/runtimeLayerCompatState.js +31 -11
  106. package/dist/runtimeLayerCompatState.js.map +1 -1
  107. package/dist/signalTelemetryProcessing.js +15 -17
  108. package/dist/signalTelemetryProcessing.js.map +1 -1
  109. package/dist/storageServiceWithAttachBlobs.js +2 -0
  110. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  111. package/dist/summary/documentSchema.d.ts.map +1 -1
  112. package/dist/summary/documentSchema.js +51 -13
  113. package/dist/summary/documentSchema.js.map +1 -1
  114. package/dist/summary/orderedClientElection.js +25 -17
  115. package/dist/summary/orderedClientElection.js.map +1 -1
  116. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  117. package/dist/summary/summarizerClientElection.js +18 -7
  118. package/dist/summary/summarizerClientElection.js.map +1 -1
  119. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  120. package/dist/summary/summarizerNode/summarizerNode.js +22 -13
  121. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  122. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  123. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
  124. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  125. package/dist/summary/summarizerUtils.d.ts.map +1 -1
  126. package/dist/summary/summarizerUtils.js +4 -3
  127. package/dist/summary/summarizerUtils.js.map +1 -1
  128. package/dist/summary/summaryCollection.js +23 -11
  129. package/dist/summary/summaryCollection.js.map +1 -1
  130. package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
  131. package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
  132. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  133. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
  134. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  135. package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  136. package/dist/summary/summaryDelayLoadedModule/summarizer.js +15 -6
  137. package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  138. package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
  139. package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
  140. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
  141. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  142. package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
  143. package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
  144. package/dist/summary/summaryFormat.d.ts.map +1 -1
  145. package/dist/summary/summaryFormat.js +7 -7
  146. package/dist/summary/summaryFormat.js.map +1 -1
  147. package/dist/summary/summaryHelpers.js +5 -5
  148. package/dist/summary/summaryHelpers.js.map +1 -1
  149. package/dist/summary/summaryManager.js +66 -54
  150. package/dist/summary/summaryManager.js.map +1 -1
  151. package/dist/throttler.d.ts.map +1 -1
  152. package/dist/throttler.js +4 -1
  153. package/dist/throttler.js.map +1 -1
  154. package/dist/versionMarks/inboundBatch.d.ts +8 -6
  155. package/dist/versionMarks/inboundBatch.d.ts.map +1 -1
  156. package/dist/versionMarks/inboundBatch.js +11 -7
  157. package/dist/versionMarks/inboundBatch.js.map +1 -1
  158. package/dist/versionMarks/versionMarkResolver.d.ts +30 -23
  159. package/dist/versionMarks/versionMarkResolver.d.ts.map +1 -1
  160. package/dist/versionMarks/versionMarkResolver.js +112 -63
  161. package/dist/versionMarks/versionMarkResolver.js.map +1 -1
  162. package/lib/batchTracker.d.ts.map +1 -1
  163. package/lib/batchTracker.js +5 -1
  164. package/lib/batchTracker.js.map +1 -1
  165. package/lib/blobManager/blobManager.d.ts +1 -1
  166. package/lib/blobManager/blobManager.d.ts.map +1 -1
  167. package/lib/blobManager/blobManager.js +264 -231
  168. package/lib/blobManager/blobManager.js.map +1 -1
  169. package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  170. package/lib/channelCollection.d.ts.map +1 -1
  171. package/lib/channelCollection.js +65 -53
  172. package/lib/channelCollection.js.map +1 -1
  173. package/lib/connectionTelemetry.js +36 -18
  174. package/lib/connectionTelemetry.js.map +1 -1
  175. package/lib/containerCompatibility.d.ts +3 -3
  176. package/lib/containerCompatibility.d.ts.map +1 -1
  177. package/lib/containerCompatibility.js +33 -42
  178. package/lib/containerCompatibility.js.map +1 -1
  179. package/lib/containerHandleContext.d.ts.map +1 -1
  180. package/lib/containerHandleContext.js +4 -0
  181. package/lib/containerHandleContext.js.map +1 -1
  182. package/lib/containerRuntime.d.ts +4 -4
  183. package/lib/containerRuntime.d.ts.map +1 -1
  184. package/lib/containerRuntime.js +341 -218
  185. package/lib/containerRuntime.js.map +1 -1
  186. package/lib/dataStore.d.ts.map +1 -1
  187. package/lib/dataStore.js +9 -1
  188. package/lib/dataStore.js.map +1 -1
  189. package/lib/dataStoreContext.d.ts.map +1 -1
  190. package/lib/dataStoreContext.js +182 -125
  191. package/lib/dataStoreContext.js.map +1 -1
  192. package/lib/dataStoreContexts.js +43 -42
  193. package/lib/dataStoreContexts.js.map +1 -1
  194. package/lib/dataStoreRegistry.js +1 -0
  195. package/lib/dataStoreRegistry.js.map +1 -1
  196. package/lib/deltaManagerProxies.js +5 -0
  197. package/lib/deltaManagerProxies.js.map +1 -1
  198. package/lib/deltaScheduler.js +104 -99
  199. package/lib/deltaScheduler.js.map +1 -1
  200. package/lib/error.js +2 -1
  201. package/lib/error.js.map +1 -1
  202. package/lib/gc/garbageCollection.js +85 -55
  203. package/lib/gc/garbageCollection.js.map +1 -1
  204. package/lib/gc/gcSummaryStateTracker.js +8 -3
  205. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  206. package/lib/gc/gcTelemetry.js +12 -5
  207. package/lib/gc/gcTelemetry.js.map +1 -1
  208. package/lib/gc/gcUnreferencedStateTracker.js +18 -1
  209. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
  210. package/lib/inboundBatchAggregator.js +122 -117
  211. package/lib/inboundBatchAggregator.js.map +1 -1
  212. package/lib/legacy.d.ts +1 -1
  213. package/lib/legacyAlpha.d.ts +1 -1
  214. package/lib/metadata.d.ts.map +1 -1
  215. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  216. package/lib/opLifecycle/batchManager.js +8 -2
  217. package/lib/opLifecycle/batchManager.js.map +1 -1
  218. package/lib/opLifecycle/duplicateBatchDetector.js +18 -18
  219. package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
  220. package/lib/opLifecycle/index.d.ts +2 -2
  221. package/lib/opLifecycle/index.d.ts.map +1 -1
  222. package/lib/opLifecycle/index.js +2 -2
  223. package/lib/opLifecycle/index.js.map +1 -1
  224. package/lib/opLifecycle/opCompressor.js +3 -2
  225. package/lib/opLifecycle/opCompressor.js.map +1 -1
  226. package/lib/opLifecycle/opDecompressor.js +10 -6
  227. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  228. package/lib/opLifecycle/opGroupingManager.js +3 -1
  229. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  230. package/lib/opLifecycle/opSerialization.d.ts +11 -1
  231. package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
  232. package/lib/opLifecycle/opSerialization.js +16 -0
  233. package/lib/opLifecycle/opSerialization.js.map +1 -1
  234. package/lib/opLifecycle/opSplitter.d.ts +17 -1
  235. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  236. package/lib/opLifecycle/opSplitter.js +44 -7
  237. package/lib/opLifecycle/opSplitter.js.map +1 -1
  238. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  239. package/lib/opLifecycle/outbox.js +14 -10
  240. package/lib/opLifecycle/outbox.js.map +1 -1
  241. package/lib/opLifecycle/remoteMessageProcessor.js +4 -1
  242. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  243. package/lib/opProperties.d.ts.map +1 -1
  244. package/lib/packageVersion.d.ts +1 -1
  245. package/lib/packageVersion.d.ts.map +1 -1
  246. package/lib/packageVersion.js +1 -1
  247. package/lib/packageVersion.js.map +1 -1
  248. package/lib/pendingStateManager.js +52 -45
  249. package/lib/pendingStateManager.js.map +1 -1
  250. package/lib/public.d.ts +1 -1
  251. package/lib/runCounter.js +10 -30
  252. package/lib/runCounter.js.map +1 -1
  253. package/lib/runtimeLayerCompatState.d.ts +1 -1
  254. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  255. package/lib/runtimeLayerCompatState.js +28 -8
  256. package/lib/runtimeLayerCompatState.js.map +1 -1
  257. package/lib/signalTelemetryProcessing.js +15 -17
  258. package/lib/signalTelemetryProcessing.js.map +1 -1
  259. package/lib/storageServiceWithAttachBlobs.js +2 -0
  260. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  261. package/lib/summary/documentSchema.d.ts.map +1 -1
  262. package/lib/summary/documentSchema.js +51 -13
  263. package/lib/summary/documentSchema.js.map +1 -1
  264. package/lib/summary/orderedClientElection.js +25 -17
  265. package/lib/summary/orderedClientElection.js.map +1 -1
  266. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  267. package/lib/summary/summarizerClientElection.js +18 -7
  268. package/lib/summary/summarizerClientElection.js.map +1 -1
  269. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  270. package/lib/summary/summarizerNode/summarizerNode.js +22 -13
  271. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  272. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  273. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
  274. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  275. package/lib/summary/summarizerUtils.d.ts.map +1 -1
  276. package/lib/summary/summarizerUtils.js +1 -0
  277. package/lib/summary/summarizerUtils.js.map +1 -1
  278. package/lib/summary/summaryCollection.js +23 -11
  279. package/lib/summary/summaryCollection.js.map +1 -1
  280. package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
  281. package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
  282. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  283. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
  284. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  285. package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  286. package/lib/summary/summaryDelayLoadedModule/summarizer.js +15 -6
  287. package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  288. package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
  289. package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
  290. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
  291. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  292. package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
  293. package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
  294. package/lib/summary/summaryFormat.d.ts.map +1 -1
  295. package/lib/summary/summaryManager.js +66 -54
  296. package/lib/summary/summaryManager.js.map +1 -1
  297. package/lib/throttler.d.ts.map +1 -1
  298. package/lib/throttler.js +4 -1
  299. package/lib/throttler.js.map +1 -1
  300. package/lib/versionMarks/inboundBatch.d.ts +8 -6
  301. package/lib/versionMarks/inboundBatch.d.ts.map +1 -1
  302. package/lib/versionMarks/inboundBatch.js +10 -5
  303. package/lib/versionMarks/inboundBatch.js.map +1 -1
  304. package/lib/versionMarks/versionMarkResolver.d.ts +30 -23
  305. package/lib/versionMarks/versionMarkResolver.d.ts.map +1 -1
  306. package/lib/versionMarks/versionMarkResolver.js +112 -63
  307. package/lib/versionMarks/versionMarkResolver.js.map +1 -1
  308. package/package.json +60 -102
  309. package/src/containerCompatibility.ts +33 -42
  310. package/src/containerRuntime.ts +15 -12
  311. package/src/opLifecycle/index.ts +7 -1
  312. package/src/opLifecycle/opCompressor.ts +2 -2
  313. package/src/opLifecycle/opSerialization.ts +22 -1
  314. package/src/opLifecycle/opSplitter.ts +63 -6
  315. package/src/packageVersion.ts +1 -1
  316. package/src/runtimeLayerCompatState.ts +28 -21
  317. package/src/summary/documentSchema.ts +31 -6
  318. package/src/versionMarks/DEV.md +595 -157
  319. package/src/versionMarks/inboundBatch.ts +18 -8
  320. package/src/versionMarks/versionMarkResolver.ts +144 -75
  321. package/tsconfig.json +1 -1
  322. package/internal.d.ts +0 -11
  323. package/legacy/alpha.d.ts +0 -11
  324. package/legacy.d.ts +0 -11
  325. /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
  326. /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
@@ -32,7 +32,7 @@ import { DeltaScheduler } from "./deltaScheduler.js";
32
32
  import { GCNodeType, GarbageCollector, } from "./gc/index.js";
33
33
  import { InboundBatchAggregator } from "./inboundBatchAggregator.js";
34
34
  import { ContainerMessageType, } from "./messageTypes.js";
35
- import { DuplicateBatchDetector, ensureContentsDeserialized, largeBatchThreshold, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
35
+ import { DuplicateBatchDetector, ensureContentsDeserialized, largeBatchThreshold, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, tryGetDeserializedRuntimeOpCopy, } from "./opLifecycle/index.js";
36
36
  import { pkgVersion } from "./packageVersion.js";
37
37
  import { PendingStateManager, } from "./pendingStateManager.js";
38
38
  import { BatchRunCounter, RunCounter } from "./runCounter.js";
@@ -247,6 +247,17 @@ function canStageMessageOfType(type) {
247
247
  * @internal
248
248
  */
249
249
  export class ContainerRuntime extends TypedEventEmitter {
250
+ registry;
251
+ metadata;
252
+ electedSummarizerData;
253
+ runtimeOptions;
254
+ containerScope;
255
+ _storage;
256
+ createIdCompressorFn;
257
+ documentsSchemaController;
258
+ minVersionForCollab;
259
+ requestHandler;
260
+ summaryConfiguration;
250
261
  /**
251
262
  * Load the stores from a snapshot and returns the runtime.
252
263
  * @param params - An object housing the runtime properties.
@@ -265,6 +276,11 @@ export class ContainerRuntime extends TypedEventEmitter {
265
276
  static async loadRuntime(params) {
266
277
  return ContainerRuntime.loadRuntime2(params).then((r) => r.runtime);
267
278
  }
279
+ /**
280
+ * Hack to allow test-utils to detect which version of loadRuntime API to expect.
281
+ * See note in loadRuntime's private remarks.
282
+ */
283
+ static loadRuntimeAPIVersion = 2;
268
284
  /**
269
285
  * Load the stores from a snapshot and returns an object containing the runtime.
270
286
  * @remarks
@@ -303,8 +319,8 @@ export class ContainerRuntime extends TypedEventEmitter {
303
319
  const mc = loggerToMonitoringContext(logger);
304
320
  // Some options require a minimum version of the FF runtime to operate, so the default configs will be generated
305
321
  // based on the minVersionForCollab.
306
- // For example, if minVersionForCollab is set to "1.0.0", the default configs will ensure compatibility with FF runtime
307
- // 1.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility
322
+ // For example, if minVersionForCollab is set to "2.0.0", the default configs will ensure compatibility with FF runtime
323
+ // 2.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility
308
324
  // with FF runtime 2.10.0 or later.
309
325
  if (!isValidMinVersionForCollab(minVersionForCollab)) {
310
326
  throw new UsageError(`Invalid minVersionForCollab: ${minVersionForCollab}. It must be an existing FF version (i.e. 2.22.1).`);
@@ -515,15 +531,27 @@ export class ContainerRuntime extends TypedEventEmitter {
515
531
  await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
516
532
  return { runtime };
517
533
  }
534
+ options;
535
+ _getClientId;
518
536
  get clientId() {
519
537
  return this._getClientId();
520
538
  }
539
+ clientDetails;
540
+ isSummarizerClient;
521
541
  get storage() {
522
542
  return this._storage;
523
543
  }
524
544
  get containerRuntime() {
525
545
  return this;
526
546
  }
547
+ submitSummaryFn;
548
+ submitSignalFn;
549
+ disposeFn;
550
+ /**
551
+ * Initiate closing of the container due to a critical error.
552
+ * @param error - The critical error that caused the container to close.
553
+ */
554
+ closeFn;
527
555
  get flushMode() {
528
556
  return this._flushMode;
529
557
  }
@@ -533,9 +561,29 @@ export class ContainerRuntime extends TypedEventEmitter {
533
561
  get IFluidDataStoreRegistry() {
534
562
  return this.registry;
535
563
  }
564
+ _getAttachState;
536
565
  get attachState() {
537
566
  return this._getAttachState();
538
567
  }
568
+ /**
569
+ * Whether local op submission is currently disallowed.
570
+ *
571
+ * This is `true` in two distinct situations.
572
+ *
573
+ * First: the delta manager reports a read-only connection (host/service-imposed permission or connection state — the historical meaning of `readOnly`).
574
+ *
575
+ * Second: the `PendingStateManager` is replaying stashed ops (`isApplyingStashedOps`). During this window DDSes must not submit new local ops, as doing so would interleave fresh content ahead of the stashed pending stream and corrupt pending local state. Surfacing it through `isReadOnly()` lets DDSes that consult `readOnly` at realize time self-suppress; see the apply-lifecycle docs on `PendingStateManager` for the full rationale.
576
+ *
577
+ * Note this layers a third meaning ("transiently quiescing for stashed-op replay") onto the `readOnly` predicate, which is broader than its host/connection-permission origin.
578
+ */
579
+ isReadOnly = () =>
580
+ // `_deltaManager` and `pendingStateManager` are both assigned partway
581
+ // through the constructor; `baseLogger` is built earlier and stamps
582
+ // `isReadOnly` on every error event (e.g. layer-compat failures
583
+ // during construction), so this can be called before either is
584
+ // assigned. Optional chains keep that window safe.
585
+ this._deltaManager?.readOnlyInfo.readonly === true ||
586
+ this.pendingStateManager?.isApplyingStashedOps === true;
539
587
  /**
540
588
  * Current session schema - defines what options are on & off.
541
589
  * It's overlap of document schema (controlled by summary & ops) and options controlling this session.
@@ -549,6 +597,15 @@ export class ContainerRuntime extends TypedEventEmitter {
549
597
  get sessionSchema() {
550
598
  return this.documentsSchemaController.sessionSchema.runtime;
551
599
  }
600
+ _idCompressor;
601
+ // We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
602
+ // Once it loads, it will process all such ops and we will stop accumulating further ops - ops will be processes as they come in.
603
+ pendingIdCompressorOps = [];
604
+ // Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
605
+ // (such ops will be marked by Loader layer as savedOp === true)
606
+ // That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
607
+ // In such case we have to process all ops, including those marked with savedOp === true.
608
+ skipSavedCompressorOps;
552
609
  /**
553
610
  * {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.idCompressor}
554
611
  */
@@ -571,6 +628,7 @@ export class ContainerRuntime extends TypedEventEmitter {
571
628
  get IFluidHandleContext() {
572
629
  return this.handleContext;
573
630
  }
631
+ handleContext;
574
632
  /**
575
633
  * This is a proxy to the delta manager provided by the container context (innerDeltaManager). It restricts certain
576
634
  * accesses such as sets "read-only" mode for the summarizer client. This is the default delta manager that should
@@ -579,6 +637,41 @@ export class ContainerRuntime extends TypedEventEmitter {
579
637
  get deltaManager() {
580
638
  return this._deltaManager;
581
639
  }
640
+ _deltaManager;
641
+ /**
642
+ * The delta manager provided by the container context. By default, using the default delta manager (proxy)
643
+ * should be sufficient. This should be used only if necessary. For example, for validating and propagating connected
644
+ * events which requires access to the actual real only info, this is needed.
645
+ */
646
+ innerDeltaManager;
647
+ // internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
648
+ mc;
649
+ summarizerClientElection;
650
+ /**
651
+ * summaryManager will only be created if this client is permitted to spawn a summarizing client
652
+ * It is created only by interactive client, i.e. summarizer client, as well as non-interactive bots
653
+ * do not create it (see SummarizerClientElection.clientDetailsPermitElection() for details)
654
+ */
655
+ summaryManager;
656
+ summarizerNode;
657
+ maxConsecutiveReconnects;
658
+ batchRunner = new BatchRunCounter();
659
+ _flushMode;
660
+ stagingModeAutoFlushThreshold;
661
+ /**
662
+ * BatchId tracking is needed whenever there's a possibility of a "forked Container",
663
+ * where the same local state is pending in two different running Containers, each of
664
+ * which is trying to ensure it's persisted.
665
+ * "Offline Load" from serialized pending state is one such scenario since two Containers
666
+ * could load from the same serialized pending state.
667
+ */
668
+ batchIdTrackingEnabled;
669
+ flushScheduled = false;
670
+ canSendOps;
671
+ canSendSignals;
672
+ getConnectionState;
673
+ consecutiveReconnects = 0;
674
+ dataModelChangeRunner = new RunCounter();
582
675
  /**
583
676
  * Invokes the given callback and expects that no ops are submitted
584
677
  * until execution finishes. If an op is submitted, it will be marked as reentrant.
@@ -601,15 +694,50 @@ export class ContainerRuntime extends TypedEventEmitter {
601
694
  get summarizerClientId() {
602
695
  return this.summarizerClientElection?.electedClientId;
603
696
  }
697
+ _disposed = false;
604
698
  get disposed() {
605
699
  return this._disposed;
606
700
  }
701
+ lastEmittedDirty;
702
+ emitDirtyDocumentEvent = true;
703
+ lastEmittedHasStagedChanges;
704
+ useDeltaManagerOpsProxy;
705
+ closeSummarizerDelayMs;
706
+ signalTelemetryManager = new SignalTelemetryManager();
707
+ /**
708
+ * Summarizer is responsible for coordinating when to send generate and send summaries.
709
+ * It is the main entry point for summary work.
710
+ * It is created only by summarizing container (i.e. one with clientType === "summarizer")
711
+ */
712
+ _summarizer;
713
+ deltaScheduler;
714
+ inboundBatchAggregator;
715
+ blobManager;
716
+ pendingStateManager;
717
+ duplicateBatchDetector;
718
+ versionMarkResolverInternal;
607
719
  /**
608
720
  * Host-facing version mark resolver.
609
721
  */
610
722
  get versionMarkResolver() {
611
723
  return this.versionMarkResolverInternal;
612
724
  }
725
+ outbox;
726
+ garbageCollector;
727
+ channelCollection;
728
+ remoteMessageProcessor;
729
+ versionMarkInboundBatchId;
730
+ /**
731
+ * The last message processed at the time of the last summary.
732
+ */
733
+ messageAtLastSummary;
734
+ summariesDisabled;
735
+ createContainerMetadata;
736
+ /**
737
+ * The summary number of the next summary that will be generated for this container. This is incremented every time
738
+ * a summary is generated.
739
+ */
740
+ nextSummaryNumber;
613
741
  /**
614
742
  * If false, loading or using a Tombstoned object should merely log, not fail.
615
743
  * @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
@@ -626,6 +754,27 @@ export class ContainerRuntime extends TypedEventEmitter {
626
754
  get gcThrowOnTombstoneUsage() {
627
755
  return false;
628
756
  }
757
+ /**
758
+ * GUID to identify a document in telemetry
759
+ * ! Note: should not be used for anything other than telemetry and is not considered a stable GUID
760
+ */
761
+ telemetryDocumentId;
762
+ /**
763
+ * The id of the version used to initially load this runtime, or undefined if it's newly created.
764
+ */
765
+ loadedFromVersionId;
766
+ isSnapshotInstanceOfISnapshot;
767
+ /**
768
+ * The summary context of the last acked summary. The properties from this as used when uploading a summary.
769
+ */
770
+ lastAckedSummaryContext;
771
+ /**
772
+ * It a cache for holding mapping for loading groupIds with its snapshot from the service. Add expiry policy of 1 minute.
773
+ * Starting with 1 min and based on recorded usage we can tweak it later on.
774
+ */
775
+ snapshotCacheForLoadingGroupIds = new PromiseCache({
776
+ expiry: { policy: "absolute", durationMs: 60000 },
777
+ });
629
778
  /**
630
779
  * The compatibility details of the Runtime layer that is exposed to the Loader layer
631
780
  * for validating Loader-Runtime compatibility.
@@ -633,6 +782,8 @@ export class ContainerRuntime extends TypedEventEmitter {
633
782
  get ILayerCompatDetails() {
634
783
  return runtimeCompatDetailsForLoader;
635
784
  }
785
+ extensions = new Map();
786
+ baseLogger;
636
787
  /***/
637
788
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope,
638
789
  // Create a custom ITelemetryBaseLogger to output telemetry events.
@@ -656,206 +807,6 @@ export class ContainerRuntime extends TypedEventEmitter {
656
807
  this.minVersionForCollab = minVersionForCollab;
657
808
  this.requestHandler = requestHandler;
658
809
  this.summaryConfiguration = summaryConfiguration;
659
- /**
660
- * Whether local op submission is currently disallowed.
661
- *
662
- * This is `true` in two distinct situations.
663
- *
664
- * First: the delta manager reports a read-only connection (host/service-imposed permission or connection state — the historical meaning of `readOnly`).
665
- *
666
- * Second: the `PendingStateManager` is replaying stashed ops (`isApplyingStashedOps`). During this window DDSes must not submit new local ops, as doing so would interleave fresh content ahead of the stashed pending stream and corrupt pending local state. Surfacing it through `isReadOnly()` lets DDSes that consult `readOnly` at realize time self-suppress; see the apply-lifecycle docs on `PendingStateManager` for the full rationale.
667
- *
668
- * Note this layers a third meaning ("transiently quiescing for stashed-op replay") onto the `readOnly` predicate, which is broader than its host/connection-permission origin.
669
- */
670
- this.isReadOnly = () =>
671
- // `_deltaManager` and `pendingStateManager` are both assigned partway
672
- // through the constructor; `baseLogger` is built earlier and stamps
673
- // `isReadOnly` on every error event (e.g. layer-compat failures
674
- // during construction), so this can be called before either is
675
- // assigned. Optional chains keep that window safe.
676
- this._deltaManager?.readOnlyInfo.readonly === true ||
677
- this.pendingStateManager?.isApplyingStashedOps === true;
678
- // We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
679
- // Once it loads, it will process all such ops and we will stop accumulating further ops - ops will be processes as they come in.
680
- this.pendingIdCompressorOps = [];
681
- this.batchRunner = new BatchRunCounter();
682
- this.flushScheduled = false;
683
- this.consecutiveReconnects = 0;
684
- this.dataModelChangeRunner = new RunCounter();
685
- this._disposed = false;
686
- this.emitDirtyDocumentEvent = true;
687
- this.signalTelemetryManager = new SignalTelemetryManager();
688
- /**
689
- * It a cache for holding mapping for loading groupIds with its snapshot from the service. Add expiry policy of 1 minute.
690
- * Starting with 1 min and based on recorded usage we can tweak it later on.
691
- */
692
- this.snapshotCacheForLoadingGroupIds = new PromiseCache({
693
- expiry: { policy: "absolute", durationMs: 60000 },
694
- });
695
- this.extensions = new Map();
696
- // Boolean payload from the `"readonly"` delta-manager event is intentionally
697
- // ignored — `isReadOnly()` aggregates delta-manager readonly with the PSM
698
- // apply window, and that aggregation is the source of truth for fanout.
699
- // `channelCollection?.` guards against future wiring changes; both callers
700
- // today (the `"readonly"` listener and `onAfterStashedOpsApplied`) fire
701
- // after `channelCollection` is assigned.
702
- this.notifyReadOnlyState = (_readonly) => this.channelCollection?.notifyReadOnlyState(this.isReadOnly());
703
- /**
704
- * Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
705
- * To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
706
- *
707
- * @remarks
708
- * The `stagingModeChanged` event is emitted when staging mode is entered or exited via this method.
709
- * It is NOT emitted when staging mode is used internally (e.g. by `orderSequentially` for rollback support).
710
- *
711
- * @returns Controls for exiting Staging Mode.
712
- */
713
- this.enterStagingMode = () => this.enterStagingModeCore(false);
714
- /**
715
- * Internal implementation of enterStagingMode.
716
- * @param silent - When true, suppresses `stagingModeChanged` event emission.
717
- * Pass `true` when staging mode is used as an internal implementation detail (e.g. by
718
- * `orderSequentially` for rollback support) so that external listeners only observe
719
- * user-initiated staging mode transitions. Pass `false` for all public entry points.
720
- */
721
- this.enterStagingModeCore = (silent) => {
722
- if (this.stageControls !== undefined) {
723
- throw new UsageError("Already in staging mode");
724
- }
725
- if (this.attachState === AttachState.Detached) {
726
- throw new UsageError("Cannot enter staging mode while Detached");
727
- }
728
- // Make sure Outbox is empty before entering staging mode,
729
- // since we mark whole batches as "staged" or not to indicate whether to submit them.
730
- this.flush();
731
- // Note: `silent` is captured from the enclosing `enterStagingModeCore` call.
732
- // When `true`, both enter and exit events are suppressed (see orderSequentially).
733
- const exitStagingMode = (discardOrCommit, exitMethod) => {
734
- if (this.stageControls !== stageControls) {
735
- throw new UsageError("Not in staging mode");
736
- }
737
- try {
738
- PerformanceEvent.timedExec(this.mc.logger, {
739
- eventName: `ExitStagingMode_${exitMethod}`,
740
- }, (event) => {
741
- // Final flush of any last staged changes
742
- // NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
743
- this.outbox.flush();
744
- this.stageControls = undefined;
745
- const batchInfos = discardOrCommit();
746
- event.reportProgress({
747
- details: {
748
- autoFlushThreshold: this.stagingModeAutoFlushThreshold,
749
- batches: batchInfos.length,
750
- batchesAtOrOverThreshold: batchInfos.filter((b) => b.length >= this.stagingModeAutoFlushThreshold).length,
751
- },
752
- });
753
- this.channelCollection.notifyStagingMode(false);
754
- });
755
- }
756
- catch (error) {
757
- const normalizedError = normalizeError(error);
758
- this.closeFn(normalizedError);
759
- throw normalizedError;
760
- }
761
- if (!silent) {
762
- this.emit("stagingModeChanged", {
763
- inStagingMode: false,
764
- commit: exitMethod === "commit",
765
- });
766
- }
767
- };
768
- const stageControls = {
769
- discardChanges: () => exitStagingMode(() => {
770
- // Pop all staged batches from the PSM and roll them back in LIFO order
771
- const batchInfos = this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
772
- this.rollbackStagedChange(runtimeOp, localOpMetadata);
773
- });
774
- this.updateDocumentDirtyState();
775
- this.updateHasStagedChangesState();
776
- return batchInfos;
777
- }, "discard"),
778
- commitChanges: (options) => {
779
- const { squash } = { ...defaultStagingCommitOptions, ...options };
780
- exitStagingMode(() => {
781
- // Replay all staged batches in typical FIFO order.
782
- // We'll be out of staging mode so they'll be sent to the service finally.
783
- const batchInfos = this.pendingStateManager.replayPendingStates({
784
- committingStagedBatches: true,
785
- squash,
786
- });
787
- this.updateHasStagedChangesState();
788
- return batchInfos;
789
- }, "commit");
790
- },
791
- };
792
- this.stageControls = stageControls;
793
- this.channelCollection.notifyStagingMode(true);
794
- if (!silent) {
795
- try {
796
- this.emit("stagingModeChanged", { inStagingMode: true });
797
- }
798
- catch (error) {
799
- // Don't let a listener error prevent the caller from receiving stage controls.
800
- this.mc.logger.sendErrorEvent({ eventName: "StagingModeChangedError" }, error);
801
- }
802
- }
803
- return stageControls;
804
- };
805
- this.readAndParseBlob = async (id) => readAndParse(this.storage, id);
806
- /**
807
- * ContainerRuntime knows about additional restrictions on when blob sharing can be resumed as compared
808
- * to BlobManager. In particular, it wants to avoid sharing blobs while in readonly state, and it also
809
- * wants to avoid sharing blobs before connection completes (otherwise it may cause the sharing to happen
810
- * before processing shared ops).
811
- *
812
- * This method can be called safely before those conditions are met. In the background, it will wait until
813
- * it is safe before initiating sharing. It will close the container on any error.
814
- */
815
- this.sharePendingBlobs = () => {
816
- new Promise((resolve) => {
817
- // eslint-disable-next-line unicorn/consistent-function-scoping
818
- const canStartSharing = () => this.connected && this.deltaManager.readOnlyInfo.readonly !== true;
819
- if (canStartSharing()) {
820
- resolve();
821
- return;
822
- }
823
- const checkCanShare = (readonly) => {
824
- if (canStartSharing()) {
825
- this.deltaManager.off("readonly", checkCanShare);
826
- this.off("connected", checkCanShare);
827
- resolve();
828
- }
829
- };
830
- this.deltaManager.on("readonly", checkCanShare);
831
- this.on("connected", checkCanShare);
832
- })
833
- .then(this.blobManager.sharePendingBlobs)
834
- // It may not be necessary to close the container on failures - this should just mean there's
835
- // a handle in the container that is stuck pending, which is a scenario that customers need to
836
- // handle anyway. Starting with this more aggressive/restrictive behavior to be cautious.
837
- .catch(this.closeFn);
838
- };
839
- // While internal, ContainerRuntime has not been converted to use the new events support.
840
- // Recreate the required events (new pattern) with injected, wrapper new emitter.
841
- // It is lazily create to avoid listeners (old events) that ultimately go nowhere.
842
- this.lazyEventsForExtensions = new Lazy(() => {
843
- const eventEmitter = createEmitter();
844
- if (this.getConnectionState) {
845
- this.on("connectedToService", (clientId, canWrite) => {
846
- eventEmitter.emit("joined", { clientId, canWrite });
847
- });
848
- this.on("disconnectedFromService", () => eventEmitter.emit("disconnected"));
849
- this.on("operabilityChanged", (canWrite) => eventEmitter.emit("operabilityChanged", canWrite));
850
- }
851
- else {
852
- this.on("connected", (clientId) => {
853
- eventEmitter.emit("joined", { clientId, canWrite: true });
854
- });
855
- this.on("disconnected", () => eventEmitter.emit("disconnected"));
856
- }
857
- return eventEmitter;
858
- });
859
810
  const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, signalAudience, pendingLocalState, snapshotWithContents, getConnectionState, } = context;
860
811
  this.getConnectionState = getConnectionState;
861
812
  // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
@@ -1014,6 +965,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1014
965
  const fetchOps = context.fetchOps;
1015
966
  this.versionMarkResolverInternal = new VersionMarkResolver({
1016
967
  getCurrentSequenceNumber: () => this.deltaManager.lastSequenceNumber,
968
+ getCurrentTimestamp: () => this.getCurrentReferenceTimestampMs(),
1017
969
  getCurrentMinimumSequenceNumber: () => this.deltaManager.minimumSequenceNumber,
1018
970
  getCurrentPendingBatchId: () => this.pendingStateManager.getMostRecentPendingBatchId(),
1019
971
  logger: createChildLogger({
@@ -1023,7 +975,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1023
975
  // Seal the current outbound batch so a just-submitted edit gets a stable batchId in the pending
1024
976
  // state before sealAndCaptureVersionMark reads it (a batchId is only assigned when flushed).
1025
977
  flushPendingBatch: () => this.flush(),
1026
- // Wire the container-provided op reader (if any) so resolution can read historical ops.
978
+ // Keep this optional while a supported older loader may not provide fetchOps. AB#81034 tracks
979
+ // making it required once the Runtime -> Loader compatibility window reaches generation 21.
1027
980
  getHistoricalOpReader: fetchOps ? () => ({ fetchMessages: fetchOps }) : undefined,
1028
981
  // Unpack scanned historical ops through the same pipeline the live inbound path uses, so a
1029
982
  // chunked batch's batchId (only restored after reassembly) is observed by the history scan too.
@@ -1031,15 +984,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1031
984
  ? () => {
1032
985
  // A fresh processor per scan: it holds chunk-reassembly state, so it must not share
1033
986
  // the live inbound processor's state. submit/size args are unused on the read path.
1034
- const scanProcessor = new RemoteMessageProcessor(new OpSplitter([], submitBatchFn, runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger), new OpDecompressor(this.mc.logger), new OpGroupingManager({ groupedBatchingEnabled: this.groupedBatchingEnabled }, this.mc.logger));
987
+ const scanProcessor = new RemoteMessageProcessor(new OpSplitter([], submitBatchFn, runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger, { allowInitialPartialChunkStream: true }), new OpDecompressor(this.mc.logger), new OpGroupingManager({ groupedBatchingEnabled: this.groupedBatchingEnabled }, this.mc.logger));
1035
988
  return (op) => {
1036
- // Mirror the live path: only modern runtime-envelope ops (type Operation, with a
1037
- // client id) carry batches; skip system/server ops.
1038
- if (op.type !== MessageType.Operation || typeof op.clientId !== "string") {
989
+ // Only runtime ops carry batches; deserialize a copy so the reader-owned op stays untouched.
990
+ const messageCopy = tryGetDeserializedRuntimeOpCopy(op);
991
+ if (messageCopy === undefined) {
1039
992
  return undefined;
1040
993
  }
1041
- const messageCopy = { ...op };
1042
- ensureContentsDeserialized(messageCopy);
1043
994
  return scanProcessor.process(messageCopy, getSingleUseLegacyLogCallback(this.mc.logger, messageCopy.type));
1044
995
  };
1045
996
  }
@@ -1263,10 +1214,10 @@ export class ContainerRuntime extends TypedEventEmitter {
1263
1214
  const clientId = this._getClientId();
1264
1215
  return clientId === undefined
1265
1216
  ? undefined
1266
- : ({
1217
+ : {
1267
1218
  clientId,
1268
1219
  client: audience.getMember(clientId),
1269
- });
1220
+ };
1270
1221
  };
1271
1222
  let oldClientId = this.clientId;
1272
1223
  this.on("connected", () => {
@@ -1678,6 +1629,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1678
1629
  async getEntryPoint() {
1679
1630
  return this.entryPoint;
1680
1631
  }
1632
+ entryPoint;
1681
1633
  internalId(maybeAlias) {
1682
1634
  return this.channelCollection.internalId(maybeAlias);
1683
1635
  }
@@ -1896,6 +1848,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1896
1848
  assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
1897
1849
  }
1898
1850
  }
1851
+ // Boolean payload from the `"readonly"` delta-manager event is intentionally
1852
+ // ignored — `isReadOnly()` aggregates delta-manager readonly with the PSM
1853
+ // apply window, and that aggregation is the source of truth for fanout.
1854
+ // `channelCollection?.` guards against future wiring changes; both callers
1855
+ // today (the `"readonly"` listener and `onAfterStashedOpsApplied`) fire
1856
+ // after `channelCollection` is assigned.
1857
+ notifyReadOnlyState = (_readonly) => this.channelCollection?.notifyReadOnlyState(this.isReadOnly());
1899
1858
  setConnectionState(canSendOps, clientId) {
1900
1859
  this.setConnectionStateToConnectedOrDisconnected(canSendOps, clientId);
1901
1860
  }
@@ -2118,7 +2077,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2118
2077
  if (this.versionMarkResolverInternal.isTracking) {
2119
2078
  const versionMarkUpdate = inboundVersionMarkUpdate(inboundResult, this.versionMarkInboundBatchId);
2120
2079
  if (versionMarkUpdate.sequenced !== undefined) {
2121
- this.versionMarkResolverInternal.processInboundBatch(versionMarkUpdate.sequenced.batchId, versionMarkUpdate.sequenced.sequenceNumber);
2080
+ this.versionMarkResolverInternal.processInboundBatch(versionMarkUpdate.sequenced.batchId, versionMarkUpdate.sequenced.sequenceNumber, versionMarkUpdate.sequenced.timestamp);
2122
2081
  }
2123
2082
  this.versionMarkInboundBatchId = versionMarkUpdate.carriedBatchId;
2124
2083
  }
@@ -2168,6 +2127,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2168
2127
  this.resetReconnectCount();
2169
2128
  }
2170
2129
  }
2130
+ _processedClientSequenceNumber;
2171
2131
  /**
2172
2132
  * Processes inbound message(s). It calls delta scheduler according to the messages' location in the batch.
2173
2133
  * @param messagesWithMetadata - messages to process along with their metadata.
@@ -2504,6 +2464,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2504
2464
  }
2505
2465
  return result;
2506
2466
  }
2467
+ stageControls;
2507
2468
  /**
2508
2469
  * If true, the ContainerRuntime is not submitting any new ops to the ordering service.
2509
2470
  * Ops submitted to the ContainerRuntime while in Staging Mode will be queued in the PendingStateManager,
@@ -2512,6 +2473,108 @@ export class ContainerRuntime extends TypedEventEmitter {
2512
2473
  get inStagingMode() {
2513
2474
  return this.stageControls !== undefined;
2514
2475
  }
2476
+ /**
2477
+ * Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
2478
+ * To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
2479
+ *
2480
+ * @remarks
2481
+ * The `stagingModeChanged` event is emitted when staging mode is entered or exited via this method.
2482
+ * It is NOT emitted when staging mode is used internally (e.g. by `orderSequentially` for rollback support).
2483
+ *
2484
+ * @returns Controls for exiting Staging Mode.
2485
+ */
2486
+ enterStagingMode = () => this.enterStagingModeCore(false);
2487
+ /**
2488
+ * Internal implementation of enterStagingMode.
2489
+ * @param silent - When true, suppresses `stagingModeChanged` event emission.
2490
+ * Pass `true` when staging mode is used as an internal implementation detail (e.g. by
2491
+ * `orderSequentially` for rollback support) so that external listeners only observe
2492
+ * user-initiated staging mode transitions. Pass `false` for all public entry points.
2493
+ */
2494
+ enterStagingModeCore = (silent) => {
2495
+ if (this.stageControls !== undefined) {
2496
+ throw new UsageError("Already in staging mode");
2497
+ }
2498
+ if (this.attachState === AttachState.Detached) {
2499
+ throw new UsageError("Cannot enter staging mode while Detached");
2500
+ }
2501
+ // Make sure Outbox is empty before entering staging mode,
2502
+ // since we mark whole batches as "staged" or not to indicate whether to submit them.
2503
+ this.flush();
2504
+ // Note: `silent` is captured from the enclosing `enterStagingModeCore` call.
2505
+ // When `true`, both enter and exit events are suppressed (see orderSequentially).
2506
+ const exitStagingMode = (discardOrCommit, exitMethod) => {
2507
+ if (this.stageControls !== stageControls) {
2508
+ throw new UsageError("Not in staging mode");
2509
+ }
2510
+ try {
2511
+ PerformanceEvent.timedExec(this.mc.logger, {
2512
+ eventName: `ExitStagingMode_${exitMethod}`,
2513
+ }, (event) => {
2514
+ // Final flush of any last staged changes
2515
+ // NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
2516
+ this.outbox.flush();
2517
+ this.stageControls = undefined;
2518
+ const batchInfos = discardOrCommit();
2519
+ event.reportProgress({
2520
+ details: {
2521
+ autoFlushThreshold: this.stagingModeAutoFlushThreshold,
2522
+ batches: batchInfos.length,
2523
+ batchesAtOrOverThreshold: batchInfos.filter((b) => b.length >= this.stagingModeAutoFlushThreshold).length,
2524
+ },
2525
+ });
2526
+ this.channelCollection.notifyStagingMode(false);
2527
+ });
2528
+ }
2529
+ catch (error) {
2530
+ const normalizedError = normalizeError(error);
2531
+ this.closeFn(normalizedError);
2532
+ throw normalizedError;
2533
+ }
2534
+ if (!silent) {
2535
+ this.emit("stagingModeChanged", {
2536
+ inStagingMode: false,
2537
+ commit: exitMethod === "commit",
2538
+ });
2539
+ }
2540
+ };
2541
+ const stageControls = {
2542
+ discardChanges: () => exitStagingMode(() => {
2543
+ // Pop all staged batches from the PSM and roll them back in LIFO order
2544
+ const batchInfos = this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
2545
+ this.rollbackStagedChange(runtimeOp, localOpMetadata);
2546
+ });
2547
+ this.updateDocumentDirtyState();
2548
+ this.updateHasStagedChangesState();
2549
+ return batchInfos;
2550
+ }, "discard"),
2551
+ commitChanges: (options) => {
2552
+ const { squash } = { ...defaultStagingCommitOptions, ...options };
2553
+ exitStagingMode(() => {
2554
+ // Replay all staged batches in typical FIFO order.
2555
+ // We'll be out of staging mode so they'll be sent to the service finally.
2556
+ const batchInfos = this.pendingStateManager.replayPendingStates({
2557
+ committingStagedBatches: true,
2558
+ squash,
2559
+ });
2560
+ this.updateHasStagedChangesState();
2561
+ return batchInfos;
2562
+ }, "commit");
2563
+ },
2564
+ };
2565
+ this.stageControls = stageControls;
2566
+ this.channelCollection.notifyStagingMode(true);
2567
+ if (!silent) {
2568
+ try {
2569
+ this.emit("stagingModeChanged", { inStagingMode: true });
2570
+ }
2571
+ catch (error) {
2572
+ // Don't let a listener error prevent the caller from receiving stage controls.
2573
+ this.mc.logger.sendErrorEvent({ eventName: "StagingModeChangedError" }, error);
2574
+ }
2575
+ }
2576
+ return stageControls;
2577
+ };
2515
2578
  /**
2516
2579
  * Returns the aliased data store's entryPoint, given the alias.
2517
2580
  * @param alias - The alias for the data store.
@@ -2558,12 +2621,21 @@ export class ContainerRuntime extends TypedEventEmitter {
2558
2621
  // container runtime's ability to send ops depend on the actual readonly state of the delta manager.
2559
2622
  return this.connected && this.innerDeltaManager.readOnlyInfo.readonly !== true;
2560
2623
  }
2624
+ _quorum;
2561
2625
  getQuorum() {
2562
2626
  return this._quorum;
2563
2627
  }
2628
+ _audience;
2564
2629
  getAudience() {
2565
2630
  return this._audience;
2566
2631
  }
2632
+ /**
2633
+ * When defined, this {@link @fluidframework/container-definitions#IAudience}
2634
+ * maintains member list using signals only.
2635
+ * Thus "write" members may be known earlier than quorum and avoid noise from
2636
+ * un-summarized quorum history.
2637
+ */
2638
+ signalAudience;
2567
2639
  /**
2568
2640
  * Returns true of container is dirty, i.e. there are some pending local changes that
2569
2641
  * either were not sent out to delta stream or were not yet acknowledged.
@@ -2663,6 +2735,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2663
2735
  this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
2664
2736
  return summarizeResult.summary;
2665
2737
  }
2738
+ getAbsoluteUrl;
2666
2739
  /**
2667
2740
  * Builds the Summary tree including all the channels and the container state.
2668
2741
  *
@@ -3603,6 +3676,7 @@ export class ContainerRuntime extends TypedEventEmitter {
3603
3676
  referenceSequenceNumber: summaryRefSeq,
3604
3677
  };
3605
3678
  }
3679
+ readAndParseBlob = async (id) => readAndParse(this.storage, id);
3606
3680
  /**
3607
3681
  * Fetches the latest snapshot from storage. If the fetched snapshot is same or newer than the one for which ack
3608
3682
  * was received, close this client. Fetching the snapshot will update the cache for this client so if it's
@@ -3694,6 +3768,39 @@ export class ContainerRuntime extends TypedEventEmitter {
3694
3768
  return pendingRuntimeState;
3695
3769
  });
3696
3770
  }
3771
+ /**
3772
+ * ContainerRuntime knows about additional restrictions on when blob sharing can be resumed as compared
3773
+ * to BlobManager. In particular, it wants to avoid sharing blobs while in readonly state, and it also
3774
+ * wants to avoid sharing blobs before connection completes (otherwise it may cause the sharing to happen
3775
+ * before processing shared ops).
3776
+ *
3777
+ * This method can be called safely before those conditions are met. In the background, it will wait until
3778
+ * it is safe before initiating sharing. It will close the container on any error.
3779
+ */
3780
+ sharePendingBlobs = () => {
3781
+ new Promise((resolve) => {
3782
+ // eslint-disable-next-line unicorn/consistent-function-scoping
3783
+ const canStartSharing = () => this.connected && this.deltaManager.readOnlyInfo.readonly !== true;
3784
+ if (canStartSharing()) {
3785
+ resolve();
3786
+ return;
3787
+ }
3788
+ const checkCanShare = (readonly) => {
3789
+ if (canStartSharing()) {
3790
+ this.deltaManager.off("readonly", checkCanShare);
3791
+ this.off("connected", checkCanShare);
3792
+ resolve();
3793
+ }
3794
+ };
3795
+ this.deltaManager.on("readonly", checkCanShare);
3796
+ this.on("connected", checkCanShare);
3797
+ })
3798
+ .then(this.blobManager.sharePendingBlobs)
3799
+ // It may not be necessary to close the container on failures - this should just mean there's
3800
+ // a handle in the container that is stuck pending, which is a scenario that customers need to
3801
+ // handle anyway. Starting with this more aggressive/restrictive behavior to be cautious.
3802
+ .catch(this.closeFn);
3803
+ };
3697
3804
  summarizeOnDemand(options) {
3698
3805
  if (this._summarizer !== undefined) {
3699
3806
  return this._summarizer.summarizeOnDemand(options);
@@ -3722,6 +3829,26 @@ export class ContainerRuntime extends TypedEventEmitter {
3722
3829
  return this.summaryManager.enqueueSummarize(options);
3723
3830
  }
3724
3831
  }
3832
+ // While internal, ContainerRuntime has not been converted to use the new events support.
3833
+ // Recreate the required events (new pattern) with injected, wrapper new emitter.
3834
+ // It is lazily create to avoid listeners (old events) that ultimately go nowhere.
3835
+ lazyEventsForExtensions = new Lazy(() => {
3836
+ const eventEmitter = createEmitter();
3837
+ if (this.getConnectionState) {
3838
+ this.on("connectedToService", (clientId, canWrite) => {
3839
+ eventEmitter.emit("joined", { clientId, canWrite });
3840
+ });
3841
+ this.on("disconnectedFromService", () => eventEmitter.emit("disconnected"));
3842
+ this.on("operabilityChanged", (canWrite) => eventEmitter.emit("operabilityChanged", canWrite));
3843
+ }
3844
+ else {
3845
+ this.on("connected", (clientId) => {
3846
+ eventEmitter.emit("joined", { clientId, canWrite: true });
3847
+ });
3848
+ this.on("disconnected", () => eventEmitter.emit("disconnected"));
3849
+ }
3850
+ return eventEmitter;
3851
+ });
3725
3852
  getJoinedStatus() {
3726
3853
  const getConnectionState = this.getConnectionState;
3727
3854
  if (getConnectionState) {
@@ -3737,6 +3864,7 @@ export class ContainerRuntime extends TypedEventEmitter {
3737
3864
  }
3738
3865
  return "disconnected";
3739
3866
  }
3867
+ submitExtensionSignal;
3740
3868
  acquireExtension(id, factory, ...useContext) {
3741
3869
  return this.acquireExtensionInternal(
3742
3870
  /* injectionPermitted */ true, id, factory, ...useContext);
@@ -3815,11 +3943,6 @@ export class ContainerRuntime extends TypedEventEmitter {
3815
3943
  return this.sessionSchema.opGroupingEnabled === true;
3816
3944
  }
3817
3945
  }
3818
- /**
3819
- * Hack to allow test-utils to detect which version of loadRuntime API to expect.
3820
- * See note in loadRuntime's private remarks.
3821
- */
3822
- ContainerRuntime.loadRuntimeAPIVersion = 2;
3823
3946
  export function isContainerMessageDirtyable({ type, contents, }) {
3824
3947
  // Certain container runtime messages should not mark the container dirty such as the old built-in
3825
3948
  // AgentScheduler and Garbage collector messages.