@fluidframework/container-runtime 2.117.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +70 -2
  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 -158
  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
@@ -1 +1 @@
1
- {"version":3,"file":"versionMarkResolver.js","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tISequencedDocumentMessage,\n\tIStream,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { InboundMessageResult } from \"../opLifecycle/index.js\";\nimport { asBatchMetadata } from \"../metadata.js\";\n\nimport { inboundVersionMarkUpdate } from \"./inboundBatch.js\";\n\n/**\n * Reads sequenced ops in `[from, to)` from delta storage, injected by the container so the runtime stays\n * driver-agnostic. `abortSignal` cancels an in-flight fetch.\n *\n * @internal\n */\nexport interface IHistoricalOpReader {\n\tfetchMessages(\n\t\tfrom: number,\n\t\tto?: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<IStream<ISequencedDocumentMessage[]>>;\n}\n\n/**\n * Result of resolving a pending batchId.\n *\n * @legacy @beta\n */\nexport type ResolveResult =\n\t| { readonly kind: \"resolved\"; readonly sequenceNumber: number }\n\t| { readonly kind: \"pending\" }\n\t| { readonly kind: \"unresolvable\" };\n\n/**\n * The data captured for a version mark. `pending` when the captured edit is local and not yet sequenced\n * (resolve it later via {@link IVersionMarkResolver.resolve}); `resolved` when there is no in-flight local\n * work, so the mark already points at a durable sequence number. The app packs its own stored record from\n * this — the runtime does not define the stored locator shape.\n *\n * @legacy @beta\n */\nexport type VersionMarkCapture =\n\t| {\n\t\t\treadonly kind: \"pending\";\n\t\t\treadonly batchId: string;\n\t\t\treadonly sequenceNumberLowerBound: number;\n\t }\n\t| { readonly kind: \"resolved\"; readonly sequenceNumber: number };\n\n/**\n * Runtime-owned resolver for app-stored version mark locators.\n *\n * @legacy @beta\n */\nexport interface IVersionMarkResolver {\n\t/**\n\t * Captures a version mark at the current point. Seals the current outbound batch first (so a just-made\n\t * local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the\n\t * mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an\n\t * unacked local batch, or a `resolved` capture (`sequenceNumber`) when there is no in-flight local work.\n\t *\n\t * @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint\n\t * boundaries, not per keystroke.\n\t *\n\t * @returns The pending batch identity and exclusive sequence number lower bound, or the current sequence number\n\t * when there is no pending local batch.\n\t */\n\tsealAndCaptureVersionMark(): VersionMarkCapture;\n\t/**\n\t * Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the\n\t * exclusive lower bound for a history read). A `resolved` sequence number feeds the loader's\n\t * `loadContainerToSequenceNumber`.\n\t *\n\t * @param batchId - The stable identity of the pending batch.\n\t * @param sequenceNumberLowerBound - The exclusive lower bound for the historical op search.\n\t * @returns The resolved sequence number, or a result indicating that the batch remains pending or can\n\t * no longer be resolved.\n\t */\n\tresolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;\n\t/**\n\t * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber)` per batch so any connected\n\t * client can promote a matching pending mark. Returns an unsubscribe function.\n\t *\n\t * @param listener - Called with the stable batch identity and its final sequence number.\n\t * @returns A function that unsubscribes the listener.\n\t */\n\tonBatchSequenced(listener: (batchId: string, sequenceNumber: number) => void): () => void;\n}\n\n/**\n * @internal\n */\nexport interface VersionMarkResolverRuntimeHooks {\n\treadonly getCurrentSequenceNumber: () => number;\n\treadonly getCurrentMinimumSequenceNumber: () => number;\n\treadonly getCurrentPendingBatchId: () => string | undefined;\n\t/** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */\n\treadonly logger: TelemetryLoggerExt;\n\t/**\n\t * Seals the current outbound batch (flushes the runtime), so a just-submitted local edit is moved into\n\t * the pending state with a stable `batchId` before\n\t * {@link IVersionMarkResolver.sealAndCaptureVersionMark} reads it.\n\t */\n\treadonly flushPendingBatch: () => void;\n\t/**\n\t * Injected by the container, backed by delta storage. When absent, unknown batchIds resolve as `pending`.\n\t */\n\treadonly getHistoricalOpReader?: () => IHistoricalOpReader | undefined;\n\t/**\n\t * Creates a per-scan op unpacker that mirrors the live inbound path (chunk reassembly, ungroup,\n\t * decompress), so the history scan observes the same restored `batchId` — in particular for chunked\n\t * batches, whose id is stripped from the final chunk's wire metadata and restored only after\n\t * reassembly. A fresh unpacker is created per scan since it holds reassembly state. When absent, the\n\t * history scan cannot identify batches and reports `pending`/`unresolvable`.\n\t */\n\treadonly createHistoricalOpUnpacker?: () => (\n\t\top: ISequencedDocumentMessage,\n\t) => InboundMessageResult | undefined;\n}\n\n/**\n * Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the\n * runtime exposes the capture point and an ephemeral in-session `batchId -> sequenceNumber` map.\n *\n * @internal\n */\nexport class VersionMarkResolver implements IVersionMarkResolver {\n\tprivate readonly sequenceNumberByBatchId = new Map<string, number>();\n\tprivate readonly batchListeners = new Set<\n\t\t(batchId: string, sequenceNumber: number) => void\n\t>();\n\t/**\n\t * Sticky flag: false until the feature is first used this session (a pending capture or a listener).\n\t * While false the runtime skips all per-inbound-batch tracking, so a container that never uses version\n\t * marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).\n\t */\n\tprivate tracking = false;\n\n\tpublic constructor(private readonly hooks: VersionMarkResolverRuntimeHooks) {}\n\n\t/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */\n\tpublic get isTracking(): boolean {\n\t\treturn this.tracking;\n\t}\n\n\tpublic sealAndCaptureVersionMark(): VersionMarkCapture {\n\t\t// Seal the current batch so a just-submitted local edit is flushed into the pending state with a\n\t\t// stable batchId (which is only assigned at flush) before we read it.\n\t\tthis.hooks.flushPendingBatch();\n\t\tconst sequenceNumberLowerBound = this.hooks.getCurrentSequenceNumber();\n\t\tconst batchId = this.hooks.getCurrentPendingBatchId();\n\t\tif (batchId === undefined) {\n\t\t\t// No unacked local batch: the mark already points at a durable sequence number.\n\t\t\treturn { kind: \"resolved\", sequenceNumber: sequenceNumberLowerBound };\n\t\t}\n\t\t// A pending mark needs its batch tracked so resolve() can promote it from the live map.\n\t\tthis.tracking = true;\n\t\treturn { kind: \"pending\", batchId, sequenceNumberLowerBound };\n\t}\n\n\tpublic async resolve(\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\t// Fast path: batch sequenced live this session.\n\t\tconst sequenceNumber = this.sessionSequenceFor(batchId);\n\t\tif (sequenceNumber !== undefined) {\n\t\t\treturn { kind: \"resolved\", sequenceNumber };\n\t\t}\n\n\t\t// Otherwise scan history from the mark's reference point.\n\t\tconst reader = this.hooks.getHistoricalOpReader?.();\n\t\tif (reader === undefined) {\n\t\t\t// No reader: the batch may still sequence live, so report pending.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\treturn this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);\n\t}\n\n\t/**\n\t * Scans sequenced ops after `sequenceNumberLowerBound` for `batchId`, returning its last op's\n\t * sequence number when found and aborting the fetch once found or exhausted. Each op is routed through\n\t * the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and\n\t * {@link inboundVersionMarkUpdate} derives its batch identity, so chunked batches resolve here too. The\n\t * scan anchor is not guaranteed to be a batch boundary, so a leading batch clipped by the anchor is\n\t * tolerated (its orphan end marker is dropped rather than fed to the processor). On a miss,\n\t * {@link classifyMiss} distinguishes `pending` (not sequenced yet) from `unresolvable` (ops trimmed).\n\t * See DEV.md.\n\t */\n\tprivate async resolveFromHistory(\n\t\treader: IHistoricalOpReader,\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst from = sequenceNumberLowerBound + 1;\n\t\tconst unpack = this.hooks.createHistoricalOpUnpacker?.();\n\t\tconst abortController = new AbortController();\n\t\ttry {\n\t\t\tconst stream = await reader.fetchMessages(from, undefined, abortController.signal);\n\t\t\tlet firstScannedSequenceNumber: number | undefined;\n\t\t\tlet carriedBatchId: string | undefined;\n\t\t\t// Mirrors the processor's batch-in-progress state so we can drop an orphan end marker (below).\n\t\t\tlet inBatch = false;\n\t\t\twhile (true) {\n\t\t\t\tconst result = await stream.read();\n\t\t\t\tif (result.done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const op of result.value) {\n\t\t\t\t\tfirstScannedSequenceNumber ??= op.sequenceNumber;\n\t\t\t\t\tif (!inBatch && asBatchMetadata(op.metadata)?.batch === false) {\n\t\t\t\t\t\t// The anchor (`sequenceNumberLowerBound + 1`) may land inside a batch whose start\n\t\t\t\t\t\t// precedes the window; its orphan end marker would trip the processor's 0x9d5 assert.\n\t\t\t\t\t\t// Drop the clipped tail. The target batch is fully in-window, so this can't skip it.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// undefined = a system op, or an incomplete chunk awaiting later fragments.\n\t\t\t\t\tconst inboundResult = unpack?.(op);\n\t\t\t\t\tif (inboundResult === undefined) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Track batch progress with the processor so the guard above skips only orphan ends.\n\t\t\t\t\tif (inboundResult.type === \"batchStartingMessage\") {\n\t\t\t\t\t\tinBatch = true;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tinboundResult.type === \"nextBatchMessage\" &&\n\t\t\t\t\t\tinboundResult.batchEnd === true\n\t\t\t\t\t) {\n\t\t\t\t\t\tinBatch = false;\n\t\t\t\t\t}\n\t\t\t\t\t// Derive batch identity exactly as the live inbound path does, carrying the id across a\n\t\t\t\t\t// piecemeal batch's messages.\n\t\t\t\t\tconst update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);\n\t\t\t\t\tcarriedBatchId = update.carriedBatchId;\n\t\t\t\t\tif (update.sequenced?.batchId === batchId) {\n\t\t\t\t\t\treturn { kind: \"resolved\", sequenceNumber: update.sequenced.sequenceNumber };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Not found: distinguish \"not sequenced yet\" from \"trimmed\" via the tip and where the scan landed.\n\t\t\treturn this.classifyMiss(from, firstScannedSequenceNumber);\n\t\t} finally {\n\t\t\t// Cancel any in-flight fetch once we stop reading (found or exhausted).\n\t\t\tabortController.abort();\n\t\t}\n\t}\n\n\t/**\n\t * Classifies a history-scan miss as `pending` or `unresolvable`. See {@link resolveFromHistory}.\n\t */\n\tprivate classifyMiss(\n\t\tfrom: number,\n\t\tfirstScannedSequenceNumber: number | undefined,\n\t): ResolveResult {\n\t\t// Invariant: a reader must never return an op before the requested range start. If it does, the\n\t\t// trim inference below is meaningless, so fail loudly rather than misclassify.\n\t\tassert(\n\t\t\tfirstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from,\n\t\t\t0xd1c /* historical op reader returned an op before the requested range start */,\n\t\t);\n\t\tconst tip = this.hooks.getCurrentSequenceNumber();\n\t\tif (from > tip) {\n\t\t\t// Nothing is sequenced at/after the mark's reference point yet, so the batch cannot have landed.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber === undefined) {\n\t\t\t// Empty read though ops should exist in `[from, tip]` → trimmed. ODSP-specific: a strict driver\n\t\t\t// empties a `from`-misaligned trimmed range (validateMessages); a return-from-earliest driver\n\t\t\t// would instead surface the trim via the `firstScannedSequenceNumber > from` branch below.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber > from) {\n\t\t\t// A trim gap at the anchor: the mark's ops (just after the reference point) are gone.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\t// Ops are present from the reference point and the batch is not among them: not yet sequenced.\n\t\treturn { kind: \"pending\" };\n\t}\n\n\tpublic onBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number) => void,\n\t): () => void {\n\t\t// A subscriber wants live promotions, so start tracking inbound batches from here on.\n\t\tthis.tracking = true;\n\t\tthis.batchListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.batchListeners.delete(listener);\n\t\t};\n\t}\n\n\t/** The sequence number for a batch seen live this session, or undefined. */\n\tprivate sessionSequenceFor(batchId: string): number | undefined {\n\t\treturn this.sequenceNumberByBatchId.get(batchId);\n\t}\n\n\t/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */\n\tpublic processInboundBatch(batchId: string, sequenceNumber: number): void {\n\t\tconst existingSequenceNumber = this.sequenceNumberByBatchId.get(batchId);\n\t\tif (existingSequenceNumber === sequenceNumber) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.sequenceNumberByBatchId.set(batchId, sequenceNumber);\n\t\tthis.evictBelowMinimumSequenceNumber();\n\t\tfor (const listener of this.batchListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(batchId, sequenceNumber);\n\t\t\t} catch (error) {\n\t\t\t\t// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw\n\t\t\t\t// must not abort op processing or starve the rest. Log and continue rather than fault the\n\t\t\t\t// container — a missed promotion is recoverable via resolve()'s history scan.\n\t\t\t\tthis.hooks.logger.sendErrorEvent({ eventName: \"VersionMarkListenerException\" }, error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Bounds the map to the collaboration window by dropping entries below the MSN (mirrors\n\t * `DuplicateBatchDetector`). Safe because below the MSN every client has processed the batch, so its\n\t * `onBatchSequenced` already fired; a dropped entry falls back to the history scan. Entries insert in\n\t * sequence order, so iteration stops at the first retained one.\n\t */\n\tprivate evictBelowMinimumSequenceNumber(): void {\n\t\tconst minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();\n\t\tfor (const [id, sequenceNumber] of this.sequenceNumberByBatchId) {\n\t\t\tif (sequenceNumber >= minimumSequenceNumber) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.sequenceNumberByBatchId.delete(id);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"versionMarkResolver.js","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tISequencedDocumentMessage,\n\tIStream,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { InboundMessageResult } from \"../opLifecycle/index.js\";\n\nimport { inboundVersionMarkUpdate } from \"./inboundBatch.js\";\n\n/**\n * Reads sequenced ops in `[from, to)` from delta storage, injected by the container so the runtime stays\n * driver-agnostic. `abortSignal` cancels an in-flight fetch.\n *\n * @internal\n */\nexport interface IHistoricalOpReader {\n\tfetchMessages(\n\t\tfrom: number,\n\t\tto?: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<IStream<ISequencedDocumentMessage[]>>;\n}\n\n/**\n * Result of resolving a pending batchId. A resolved result includes the matched batch's last op server\n * timestamp when available. The property is optional for compatibility with previously stored results.\n *\n * @legacy @beta\n */\nexport type ResolveResult =\n\t| {\n\t\t\treadonly kind: \"resolved\";\n\t\t\treadonly sequenceNumber: number;\n\t\t\treadonly timestamp?: number;\n\t }\n\t| { readonly kind: \"pending\" }\n\t| { readonly kind: \"unresolvable\" };\n\n/**\n * The data captured for a version mark. `pending` when the captured edit is local and not yet sequenced\n * (resolve it later via {@link IVersionMarkResolver.resolve}); `resolved` when there is no in-flight local\n * work, so the mark already points at a durable sequence number. The app packs its own stored record from\n * this — the runtime does not define the stored locator shape.\n *\n * @legacy @beta\n */\nexport type VersionMarkCapture =\n\t| {\n\t\t\treadonly kind: \"pending\";\n\t\t\treadonly batchId: string;\n\t\t\treadonly sequenceNumberLowerBound: number;\n\t }\n\t| {\n\t\t\treadonly kind: \"resolved\";\n\t\t\treadonly sequenceNumber: number;\n\t\t\treadonly timestamp?: number;\n\t };\n\n/**\n * Runtime-owned resolver for app-stored version mark locators.\n *\n * @legacy @beta\n */\nexport interface IVersionMarkResolver {\n\t/**\n\t * Captures a version mark at the current point. Seals the current outbound batch first (so a just-made\n\t * local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the\n\t * mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an\n\t * unacked local batch, or a `resolved` capture (`sequenceNumber` + the last processed op's server\n\t * `timestamp`) when there is no in-flight local work. The timestamp property is optional both for\n\t * compatibility with previously stored captures and because it is `undefined` when neither a last\n\t * processed message nor a last-summary message is available.\n\t *\n\t * @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint\n\t * boundaries, not per keystroke.\n\t *\n\t * @returns The pending batch identity and inclusive sequence number lower bound, or the current sequence\n\t * number and corresponding op timestamp (when available) when there is no pending local batch.\n\t */\n\tsealAndCaptureVersionMark(): VersionMarkCapture;\n\t/**\n\t * Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the\n\t * inclusive lower bound for a history read). A `resolved` sequence number feeds the loader's\n\t * `loadContainerToSequenceNumber`.\n\t *\n\t * @param batchId - The stable identity of the pending batch.\n\t * @param sequenceNumberLowerBound - The inclusive lower bound for the historical op search.\n\t * @returns The resolved sequence number and server timestamp, or a result indicating that the batch\n\t * remains pending or can no longer be resolved.\n\t */\n\tresolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;\n\t/**\n\t * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber, timestamp)` per batch so any\n\t * connected client can promote a matching pending mark. Returns an unsubscribe function.\n\t *\n\t * @param listener - Called with the stable batch identity, its final sequence number, and the final op's\n\t * server timestamp.\n\t * @returns A function that unsubscribes the listener.\n\t */\n\tonBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,\n\t): () => void;\n}\n\n/**\n * @internal\n */\nexport interface VersionMarkResolverRuntimeHooks {\n\treadonly getCurrentSequenceNumber: () => number;\n\treadonly getCurrentTimestamp: () => number | undefined;\n\treadonly getCurrentMinimumSequenceNumber: () => number;\n\treadonly getCurrentPendingBatchId: () => string | undefined;\n\t/** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */\n\treadonly logger: TelemetryLoggerExt;\n\t/**\n\t * Seals the current outbound batch (flushes the runtime), so a just-submitted local edit is moved into\n\t * the pending state with a stable `batchId` before\n\t * {@link IVersionMarkResolver.sealAndCaptureVersionMark} reads it.\n\t */\n\treadonly flushPendingBatch: () => void;\n\t/**\n\t * Injected by the container, backed by delta storage. When absent, unknown batchIds resolve as `pending`.\n\t */\n\treadonly getHistoricalOpReader?: () => IHistoricalOpReader | undefined;\n\t/**\n\t * Creates a per-scan op unpacker that mirrors the live inbound path (chunk reassembly, ungroup,\n\t * decompress), so the history scan observes the same restored `batchId` — in particular for chunked\n\t * batches, whose id is stripped from the final chunk's wire metadata and restored only after\n\t * reassembly. A fresh unpacker is created per scan since it holds reassembly state. When absent, the\n\t * history scan cannot identify batches and reports `pending`/`unresolvable`.\n\t */\n\treadonly createHistoricalOpUnpacker?: () => (\n\t\top: ISequencedDocumentMessage,\n\t) => InboundMessageResult | undefined;\n}\n\n/**\n * Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the\n * runtime exposes the capture point and an ephemeral in-session `batchId -> sequenceNumber` map.\n *\n * @internal\n */\nexport class VersionMarkResolver implements IVersionMarkResolver {\n\tprivate readonly resolvedBatchById = new Map<\n\t\tstring,\n\t\t{ readonly sequenceNumber: number; readonly timestamp: number }\n\t>();\n\tprivate readonly batchListeners = new Set<\n\t\t(batchId: string, sequenceNumber: number, timestamp: number) => void\n\t>();\n\t/**\n\t * Sticky flag: false until the feature is first used this session (a pending capture or a listener).\n\t * While false the runtime skips all per-inbound-batch tracking, so a container that never uses version\n\t * marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).\n\t */\n\tprivate tracking = false;\n\n\tpublic constructor(private readonly hooks: VersionMarkResolverRuntimeHooks) {}\n\n\t/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */\n\tpublic get isTracking(): boolean {\n\t\treturn this.tracking;\n\t}\n\n\tpublic sealAndCaptureVersionMark(): VersionMarkCapture {\n\t\t// Seal the current batch so a just-submitted local edit is flushed into the pending state with a\n\t\t// stable batchId (which is only assigned at flush) before we read it.\n\t\tthis.hooks.flushPendingBatch();\n\t\tconst referenceSequenceNumber = this.hooks.getCurrentSequenceNumber();\n\t\tconst batchId = this.hooks.getCurrentPendingBatchId();\n\t\tlet result: VersionMarkCapture;\n\t\tif (batchId === undefined) {\n\t\t\t// No unacked local batch: the mark already points at a durable sequence number.\n\t\t\tresult = {\n\t\t\t\tkind: \"resolved\",\n\t\t\t\tsequenceNumber: referenceSequenceNumber,\n\t\t\t\ttimestamp: this.hooks.getCurrentTimestamp(),\n\t\t\t};\n\t\t} else {\n\t\t\t// A pending mark needs its batch tracked so resolve() can promote it from the live map.\n\t\t\tthis.tracking = true;\n\t\t\t// The pending batch is sequenced after the reference point, so its first possible sequence\n\t\t\t// number is `referenceSequenceNumber + 1`. Store that as an inclusive lower bound so resolve()\n\t\t\t// scans directly from it.\n\t\t\tresult = {\n\t\t\t\tkind: \"pending\",\n\t\t\t\tbatchId,\n\t\t\t\tsequenceNumberLowerBound: referenceSequenceNumber + 1,\n\t\t\t};\n\t\t}\n\t\tthis.hooks.logger.sendTelemetryEvent({ eventName: \"Capture\", kind: result.kind });\n\t\treturn result;\n\t}\n\n\tpublic async resolve(\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst startTime = Date.now();\n\t\t// Track from here so inbound batches are recorded even without a prior capture/subscribe; otherwise\n\t\t// a batch sequencing during the scan (or live, in the no-reader case) is skipped and unrecoverable.\n\t\tthis.tracking = true;\n\t\t// Defaults cover the throw path (only the history scan can throw — e.g. an unpacker\n\t\t// DataCorruptionError or the 0xd1c reader-contract assert): the Resolve event still fires via\n\t\t// `finally`, with outcome \"error\".\n\t\tlet path: \"session\" | \"history\" | \"noReader\" = \"history\";\n\t\tlet outcome: ResolveResult[\"kind\"] | \"error\" = \"error\";\n\t\tlet resolvedSequenceNumber: number | undefined;\n\t\ttry {\n\t\t\t// Fast path: batch sequenced live this session.\n\t\t\tconst resolvedBatch = this.sessionResolutionFor(batchId);\n\t\t\tif (resolvedBatch === undefined) {\n\t\t\t\tconst reader = this.hooks.getHistoricalOpReader?.();\n\t\t\t\tif (reader === undefined) {\n\t\t\t\t\t// No reader: the batch may still sequence live, so report pending.\n\t\t\t\t\tpath = \"noReader\";\n\t\t\t\t\toutcome = \"pending\";\n\t\t\t\t\treturn { kind: \"pending\" };\n\t\t\t\t}\n\t\t\t\t// Otherwise scan history from the mark's reference point.\n\t\t\t\tpath = \"history\";\n\t\t\t\tlet result = await this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);\n\t\t\t\tif (result.kind !== \"resolved\") {\n\t\t\t\t\t// The batch may have sequenced live while the history scan was in progress. Prefer that\n\t\t\t\t\t// authoritative in-session result over a stale history miss.\n\t\t\t\t\tconst liveResult = this.sessionResolutionFor(batchId);\n\t\t\t\t\tif (liveResult !== undefined) {\n\t\t\t\t\t\tpath = \"session\";\n\t\t\t\t\t\tresult = { kind: \"resolved\", ...liveResult };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\toutcome = result.kind;\n\t\t\t\tif (result.kind === \"resolved\") {\n\t\t\t\t\tresolvedSequenceNumber = result.sequenceNumber;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tpath = \"session\";\n\t\t\toutcome = \"resolved\";\n\t\t\tresolvedSequenceNumber = resolvedBatch.sequenceNumber;\n\t\t\treturn { kind: \"resolved\", ...resolvedBatch };\n\t\t} finally {\n\t\t\tthis.hooks.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"Resolve\",\n\t\t\t\toutcome,\n\t\t\t\tpath,\n\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\t...(resolvedSequenceNumber === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { sequenceNumber: resolvedSequenceNumber }),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's\n\t * sequence number when found and aborting the fetch once found or exhausted. Each op is routed through\n\t * the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and\n\t * {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.\n\t * The injected historical unpacker owns virtualization details, including tolerating a requested range\n\t * that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes\n\t * `pending` (not sequenced yet) from `unresolvable` (ops trimmed).\n\t */\n\tprivate async resolveFromHistory(\n\t\treader: IHistoricalOpReader,\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst from = sequenceNumberLowerBound;\n\t\tconst unpack = this.hooks.createHistoricalOpUnpacker?.();\n\t\tconst abortController = new AbortController();\n\t\ttry {\n\t\t\tconst stream = await reader.fetchMessages(from, undefined, abortController.signal);\n\t\t\tlet firstScannedSequenceNumber: number | undefined;\n\t\t\tlet carriedBatchId: string | undefined;\n\t\t\twhile (true) {\n\t\t\t\tconst result = await stream.read();\n\t\t\t\tif (result.done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const op of result.value) {\n\t\t\t\t\tfirstScannedSequenceNumber ??= op.sequenceNumber;\n\t\t\t\t\t// undefined = a system op or virtualized input that has not produced a complete result.\n\t\t\t\t\tconst inboundResult = unpack?.(op);\n\t\t\t\t\tif (inboundResult === undefined) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Derive batch identity exactly as the live inbound path does, carrying the id across a\n\t\t\t\t\t// piecemeal batch's messages.\n\t\t\t\t\tconst update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);\n\t\t\t\t\tcarriedBatchId = update.carriedBatchId;\n\t\t\t\t\tif (update.sequenced?.batchId === batchId) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: \"resolved\",\n\t\t\t\t\t\t\tsequenceNumber: update.sequenced.sequenceNumber,\n\t\t\t\t\t\t\ttimestamp: update.sequenced.timestamp,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Not found: distinguish \"not sequenced yet\" from \"trimmed\" via the tip and where the scan landed.\n\t\t\treturn this.classifyMiss(from, firstScannedSequenceNumber);\n\t\t} finally {\n\t\t\t// Cancel any in-flight fetch once we stop reading (found or exhausted).\n\t\t\tabortController.abort();\n\t\t}\n\t}\n\n\t/**\n\t * Classifies a history-scan miss as `pending` or `unresolvable`. See {@link resolveFromHistory}.\n\t */\n\tprivate classifyMiss(\n\t\tfrom: number,\n\t\tfirstScannedSequenceNumber: number | undefined,\n\t): ResolveResult {\n\t\t// Invariant: a reader must never return an op before the requested range start. If it does, the\n\t\t// trim inference below is meaningless, so fail loudly rather than misclassify.\n\t\tassert(\n\t\t\tfirstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from,\n\t\t\t0xd1c /* historical op reader returned an op before the requested range start */,\n\t\t);\n\t\tconst tip = this.hooks.getCurrentSequenceNumber();\n\t\tif (from > tip) {\n\t\t\t// Nothing is sequenced at/after the mark's lower bound yet, so the batch cannot have landed.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber === undefined) {\n\t\t\t// Empty read though ops should exist in `[from, tip]` → trimmed. ODSP-specific: a strict driver\n\t\t\t// empties a `from`-misaligned trimmed range (validateMessages); a return-from-earliest driver\n\t\t\t// would instead surface the trim via the `firstScannedSequenceNumber > from` branch below.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber > from) {\n\t\t\t// A trim gap at the anchor: the mark's ops (at/after its lower bound) are gone.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\t// Ops are present from the lower bound and the batch is not among them: not yet sequenced.\n\t\treturn { kind: \"pending\" };\n\t}\n\n\tpublic onBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,\n\t): () => void {\n\t\t// A subscriber wants live promotions, so start tracking inbound batches from here on.\n\t\tthis.tracking = true;\n\t\tthis.batchListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.batchListeners.delete(listener);\n\t\t};\n\t}\n\n\t/** The resolved point for a batch seen live this session, or undefined. */\n\tprivate sessionResolutionFor(\n\t\tbatchId: string,\n\t): { readonly sequenceNumber: number; readonly timestamp: number } | undefined {\n\t\treturn this.resolvedBatchById.get(batchId);\n\t}\n\n\t/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */\n\tpublic processInboundBatch(\n\t\tbatchId: string,\n\t\tsequenceNumber: number,\n\t\ttimestamp: number,\n\t): void {\n\t\tconst existingResolution = this.resolvedBatchById.get(batchId);\n\t\tif (existingResolution !== undefined) {\n\t\t\t// NOTE: It's possible (but exceedingly rare) that this is a spurious duplicated batch,\n\t\t\t// with a different sequence number/timestamp.\n\t\t\t// Rather than asserting or overwriting, we let the earlier version mark stand, it is sufficient.\n\t\t\treturn;\n\t\t}\n\n\t\tthis.resolvedBatchById.set(batchId, { sequenceNumber, timestamp });\n\t\tthis.evictBelowMinimumSequenceNumber();\n\t\tfor (const listener of this.batchListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(batchId, sequenceNumber, timestamp);\n\t\t\t} catch (error) {\n\t\t\t\t// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw\n\t\t\t\t// must not abort op processing or starve the rest. Log and continue rather than fault the\n\t\t\t\t// container — a missed promotion is recoverable via resolve()'s history scan.\n\t\t\t\tthis.hooks.logger.sendErrorEvent({ eventName: \"VersionMarkListenerException\" }, error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Bounds the map to the collaboration window by dropping entries below the MSN (mirrors\n\t * `DuplicateBatchDetector`). Safe because below the MSN every client has processed the batch, so its\n\t * `onBatchSequenced` already fired; a dropped entry falls back to the history scan. Entries insert in\n\t * sequence order, so iteration stops at the first retained one.\n\t */\n\tprivate evictBelowMinimumSequenceNumber(): void {\n\t\tconst minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();\n\t\tfor (const [id, resolution] of this.resolvedBatchById) {\n\t\t\tif (resolution.sequenceNumber >= minimumSequenceNumber) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.resolvedBatchById.delete(id);\n\t\t}\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.117.0",
3
+ "version": "3.0.0",
4
4
  "description": "Fluid container runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -14,98 +14,51 @@
14
14
  "type": "module",
15
15
  "exports": {
16
16
  ".": {
17
- "import": {
18
- "types": "./lib/public.d.ts",
19
- "default": "./lib/index.js"
20
- },
21
- "require": {
22
- "types": "./dist/public.d.ts",
23
- "default": "./dist/index.js"
17
+ "types": "./lib/public.d.ts",
18
+ "default": "./lib/index.js",
19
+ "internal-entrypoint-generation": {
20
+ "types": "./dist/public.d.ts"
24
21
  }
25
22
  },
26
23
  "./legacy": {
27
- "import": {
28
- "types": "./lib/legacy.d.ts",
29
- "default": "./lib/index.js"
30
- },
31
- "require": {
32
- "types": "./dist/legacy.d.ts",
33
- "default": "./dist/index.js"
24
+ "types": "./lib/legacy.d.ts",
25
+ "default": "./lib/index.js",
26
+ "internal-entrypoint-generation": {
27
+ "types": "./dist/legacy.d.ts"
34
28
  }
35
29
  },
36
30
  "./legacy/alpha": {
37
- "import": {
38
- "types": "./lib/legacyAlpha.d.ts",
39
- "default": "./lib/index.js"
40
- },
41
- "require": {
42
- "types": "./dist/legacyAlpha.d.ts",
43
- "default": "./dist/index.js"
31
+ "types": "./lib/legacyAlpha.d.ts",
32
+ "default": "./lib/index.js",
33
+ "internal-entrypoint-generation": {
34
+ "types": "./dist/legacyAlpha.d.ts"
44
35
  }
45
36
  },
46
37
  "./internal": {
47
- "import": {
48
- "types": "./lib/index.d.ts",
49
- "default": "./lib/index.js"
50
- },
51
- "require": {
52
- "types": "./dist/index.d.ts",
53
- "default": "./dist/index.js"
54
- }
38
+ "types": "./lib/index.d.ts",
39
+ "default": "./lib/index.js"
55
40
  },
56
41
  "./internal/test/containerRuntime": {
57
- "import": {
58
- "types": "./lib/containerRuntime.d.ts",
59
- "default": "./lib/containerRuntime.js"
60
- },
61
- "require": {
62
- "types": "./dist/containerRuntime.d.ts",
63
- "default": "./dist/containerRuntime.js"
64
- }
42
+ "types": "./lib/containerRuntime.d.ts",
43
+ "default": "./lib/containerRuntime.js"
65
44
  },
66
45
  "./internal/test/deltaScheduler": {
67
- "import": {
68
- "types": "./lib/deltaScheduler.d.ts",
69
- "default": "./lib/deltaScheduler.js"
70
- },
71
- "require": {
72
- "types": "./dist/deltaScheduler.d.ts",
73
- "default": "./dist/deltaScheduler.js"
74
- }
46
+ "types": "./lib/deltaScheduler.d.ts",
47
+ "default": "./lib/deltaScheduler.js"
75
48
  },
76
49
  "./internal/test/blobManager": {
77
- "import": {
78
- "types": "./lib/blobManager/index.d.ts",
79
- "default": "./lib/blobManager/index.js"
80
- },
81
- "require": {
82
- "types": "./dist/blobManager/index.d.ts",
83
- "default": "./dist/blobManager/index.js"
84
- }
50
+ "types": "./lib/blobManager/index.d.ts",
51
+ "default": "./lib/blobManager/index.js"
85
52
  },
86
53
  "./internal/test/summary": {
87
- "import": {
88
- "types": "./lib/summary/index.d.ts",
89
- "default": "./lib/summary/index.js"
90
- },
91
- "require": {
92
- "types": "./dist/summary/index.d.ts",
93
- "default": "./dist/summary/index.js"
94
- }
54
+ "types": "./lib/summary/index.d.ts",
55
+ "default": "./lib/summary/index.js"
95
56
  },
96
57
  "./internal/test/gc": {
97
- "import": {
98
- "types": "./lib/gc/index.d.ts",
99
- "default": "./lib/gc/index.js"
100
- },
101
- "require": {
102
- "types": "./gc/index.d.ts",
103
- "default": "./gc/index.js"
104
- }
58
+ "types": "./lib/gc/index.d.ts",
59
+ "default": "./lib/gc/index.js"
105
60
  }
106
61
  },
107
- "main": "lib/index.js",
108
- "types": "lib/public.d.ts",
109
62
  "c8": {
110
63
  "all": true,
111
64
  "cache-dir": "nyc/.cache",
@@ -129,18 +82,18 @@
129
82
  "temp-directory": "nyc/.nyc_output"
130
83
  },
131
84
  "dependencies": {
132
- "@fluid-internal/client-utils": "~2.117.0",
133
- "@fluidframework/container-definitions": "~2.117.0",
134
- "@fluidframework/container-runtime-definitions": "~2.117.0",
135
- "@fluidframework/core-interfaces": "~2.117.0",
136
- "@fluidframework/core-utils": "~2.117.0",
137
- "@fluidframework/datastore": "~2.117.0",
138
- "@fluidframework/driver-definitions": "~2.117.0",
139
- "@fluidframework/driver-utils": "~2.117.0",
140
- "@fluidframework/id-compressor": "~2.117.0",
141
- "@fluidframework/runtime-definitions": "~2.117.0",
142
- "@fluidframework/runtime-utils": "~2.117.0",
143
- "@fluidframework/telemetry-utils": "~2.117.0",
85
+ "@fluid-internal/client-utils": "~3.0.0",
86
+ "@fluidframework/container-definitions": "~3.0.0",
87
+ "@fluidframework/container-runtime-definitions": "~3.0.0",
88
+ "@fluidframework/core-interfaces": "~3.0.0",
89
+ "@fluidframework/core-utils": "~3.0.0",
90
+ "@fluidframework/datastore": "~3.0.0",
91
+ "@fluidframework/driver-definitions": "~3.0.0",
92
+ "@fluidframework/driver-utils": "~3.0.0",
93
+ "@fluidframework/id-compressor": "~3.0.0",
94
+ "@fluidframework/runtime-definitions": "~3.0.0",
95
+ "@fluidframework/runtime-utils": "~3.0.0",
96
+ "@fluidframework/telemetry-utils": "~3.0.0",
144
97
  "@tylerbu/sorted-btree-es6": "^2.1.1",
145
98
  "double-ended-queue": "^2.1.0-0",
146
99
  "lz4js": "^0.2.0",
@@ -150,16 +103,16 @@
150
103
  "devDependencies": {
151
104
  "@arethetypeswrong/cli": "^0.18.5",
152
105
  "@biomejs/biome": "~2.4.5",
153
- "@fluid-internal/mocha-test-setup": "~2.117.0",
154
- "@fluid-private/stochastic-test-utils": "~2.117.0",
155
- "@fluid-private/test-pairwise-generator": "~2.117.0",
106
+ "@fluid-internal/mocha-test-setup": "~3.0.0",
107
+ "@fluid-private/stochastic-test-utils": "~3.0.0",
108
+ "@fluid-private/test-pairwise-generator": "~3.0.0",
156
109
  "@fluid-tools/benchmark": "^0.59.0",
157
110
  "@fluid-tools/build-cli": "^0.67.0",
158
111
  "@fluidframework/build-common": "^2.0.3",
159
112
  "@fluidframework/build-tools": "^0.67.0",
160
113
  "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.116.0",
161
114
  "@fluidframework/eslint-config-fluid": "^14.0.0",
162
- "@fluidframework/test-runtime-utils": "~2.117.0",
115
+ "@fluidframework/test-runtime-utils": "~3.0.0",
163
116
  "@microsoft/api-extractor": "7.58.1",
164
117
  "@types/double-ended-queue": "^2.1.0",
165
118
  "@types/lz4js": "^0.2.0",
@@ -177,30 +130,36 @@
177
130
  "mocha-multi-reporters": "^1.5.1",
178
131
  "rimraf": "^6.1.3",
179
132
  "sinon": "^18.0.1",
180
- "typescript": "~5.4.5"
133
+ "typescript": "~6.0.3"
181
134
  },
182
135
  "typeValidation": {
183
- "broken": {},
136
+ "broken": {
137
+ "Interface_LoadContainerRuntimeParams": {
138
+ "backCompat": false,
139
+ "forwardCompat": false
140
+ }
141
+ },
184
142
  "entrypoint": "legacy"
185
143
  },
186
144
  "scripts": {
187
145
  "bench": "cross-env FLUID_TEST_PERF_MODE=1 mocha --parentProcess",
188
146
  "build": "fluid-build . --task build",
189
147
  "build:api-reports": "concurrently \"npm:build:api-reports:*\"",
190
- "build:api-reports:current": "api-extractor run --local --config api-extractor/api-extractor.current.json",
191
- "build:api-reports:legacy": "api-extractor run --local --config api-extractor/api-extractor.legacy.json",
148
+ "build:api-reports:current": "api-extractor run --local --config api-extractor/api-extractor-report.current.json",
149
+ "build:api-reports:legacy": "api-extractor run --local --config api-extractor/api-extractor-report.legacy.json",
150
+ "build:cjs": "fluid-tsc commonjs --project ./tsconfig.cjs.json && npm run place:cjs:package-stub",
192
151
  "build:commonjs": "fluid-build . --task commonjs",
193
152
  "build:compile": "fluid-build . --task compile",
194
- "build:docs": "api-extractor run --local",
153
+ "build:docs": "api-extractor run --local --config api-extractor/api-extractor-model.json",
195
154
  "build:entrypoints": "fluid-build . --task build:entrypoints",
196
155
  "build:entrypoints:cjs": "flub generate entrypoints --resolutionConditions require --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./dist",
197
- "build:entrypoints:esm": "flub generate entrypoints --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./lib --node10TypeCompat",
198
- "build:esnext": "tsc --project ./tsconfig.json",
156
+ "build:entrypoints:esm": "flub generate entrypoints --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./lib",
157
+ "build:esm": "tsc --project ./tsconfig.json",
199
158
  "build:genver": "gen-version",
200
159
  "build:test": "concurrently npm:build:test:esm npm:build:test:cjs",
201
160
  "build:test:cjs": "fluid-tsc commonjs --project ./src/test/tsconfig.cjs.json",
202
161
  "build:test:esm": "tsc --project ./src/test/tsconfig.json",
203
- "check:are-the-types-wrong": "attw --pack . --exclude-entrypoints ./internal/test/containerRuntime ./internal/test/deltaScheduler ./internal/test/blobManager ./internal/test/summary ./internal/test/gc",
162
+ "check:are-the-types-wrong": "attw --pack . --profile esm-only",
204
163
  "check:biome": "biome check .",
205
164
  "check:exports": "concurrently \"npm:check:exports:*\"",
206
165
  "check:exports:bundle-release-tags": "api-extractor run --config api-extractor/api-extractor-lint-bundle.json",
@@ -212,9 +171,9 @@
212
171
  "check:exports:esm:public": "api-extractor run --config api-extractor/api-extractor-lint-public.esm.json",
213
172
  "check:format": "npm run check:biome",
214
173
  "ci:build:api-reports": "concurrently \"npm:ci:build:api-reports:*\"",
215
- "ci:build:api-reports:current": "api-extractor run --config api-extractor/api-extractor.current.json",
216
- "ci:build:api-reports:legacy": "api-extractor run --config api-extractor/api-extractor.legacy.json",
217
- "ci:build:docs": "api-extractor run",
174
+ "ci:build:api-reports:current": "api-extractor run --config api-extractor/api-extractor-report.current.json",
175
+ "ci:build:api-reports:legacy": "api-extractor run --config api-extractor/api-extractor-report.legacy.json",
176
+ "ci:build:docs": "api-extractor run --config api-extractor/api-extractor-model.json",
218
177
  "clean": "rimraf --glob dist lib {alpha,beta,internal,legacy}.d.ts legacyAlpha.d.ts \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
219
178
  "depcruise": "depcruise src/",
220
179
  "eslint": "eslint --quiet --format stylish src",
@@ -231,8 +190,7 @@
231
190
  "test:mocha:cjs": "cross-env FLUID_TEST_MODULE_SYSTEM=CJS mocha",
232
191
  "test:mocha:esm": "mocha",
233
192
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
234
- "tsc": "fluid-tsc commonjs --project ./tsconfig.cjs.json && npm run place:cjs:package-stub",
235
- "tsc:watch": "npm run place:cjs:package-stub && fluid-tsc commonjs --project ./tsconfig.cjs.json --watch",
236
- "typetests:gen": "flub generate typetests --dir . -v"
193
+ "typetests:gen": "flub generate typetests --dir . -v",
194
+ "watch:cjs": "npm run place:cjs:package-stub && fluid-tsc commonjs --project ./tsconfig.cjs.json --watch"
237
195
  }
238
196
  }
@@ -98,27 +98,28 @@ export type RuntimeOptionKeysThatRequireExplicitSchemaControl = keyof Omit<
98
98
  * the format changes introduced by the property, then the default value for that OldestSupportedClientVersion will enable the feature associated with the property.
99
99
  * Otherwise, the feature will be disabled.
100
100
  *
101
- * For example if the minVersionForCollab is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x
102
- * clients do not understand the document format when batching is enabled. If the minVersionForCollab is a 2.x client (i.e. "2.0.0" or later), then the
103
- * default value for `enableGroupedBatching` will be true because clients running 2.0 or later will be able to understand the format changes associated
104
- * with the batching feature.
101
+ * For example, the default value for `enableGroupedBatching` is true at the supported
102
+ * floor of 2.0.0 because clients running 2.0.0 or later understand the associated
103
+ * document format.
105
104
  */
106
105
  const runtimeOptionsAffectingDocSchemaConfigMap: ConfigMap<RuntimeOptionsAffectingDocSchema> =
107
106
  {
108
107
  enableGroupedBatching: {
109
- "1.0.0": false,
110
- "2.0.0-defaults": true,
108
+ // Grouped batching was already enabled by default before callers explicitly selected
109
+ // a compatibility version. All supported 2.x clients understand this format.
110
+ "2.0.0": true,
111
111
  },
112
112
  compressionOptions: {
113
- "1.0.0": disabledCompressionConfig,
114
- "2.0.0-defaults": enabledCompressionConfig,
113
+ // Compression was already enabled by default before callers explicitly selected a
114
+ // compatibility version. All supported 2.x clients understand this format.
115
+ "2.0.0": enabledCompressionConfig,
115
116
  },
116
117
  enableRuntimeIdCompressor: {
117
118
  // For IdCompressorMode, `undefined` represents a logical state (off).
118
119
  // However, to satisfy the Required<> constraint while
119
120
  // `exactOptionalPropertyTypes` is `false` (TODO: AB#8215), we need
120
121
  // to have it defined, so we trick the type checker here.
121
- "1.0.0": undefined,
122
+ "2.0.0": undefined,
122
123
  // We do not yet want to enable idCompressor by default since it will
123
124
  // increase bundle sizes, and not all customers will benefit from it.
124
125
  // Therefore, we will require customers to explicitly enable it. We
@@ -126,28 +127,18 @@ const runtimeOptionsAffectingDocSchemaConfigMap: ConfigMap<RuntimeOptionsAffecti
126
127
  // change in the future.
127
128
  },
128
129
  explicitSchemaControl: {
129
- "1.0.0": false,
130
130
  // This option's intention is to prevent 1.x clients from joining sessions
131
- // when enabled. This is set to true when the minVersionForCollab is set
132
- // to >=2.0.0 (explicitly). This is different than other 2.0 defaults
133
- // because it was not enabled by default prior to the implementation of
134
- // `minVersionForCollab`.
135
- // `defaultMinVersionForCollab` is set to "2.0.0-defaults" which "2.0.0"
136
- // does not satisfy to avoiding enabling this option by default as of
137
- // `minVersionForCollab` introduction, which could be unexpected.
138
- // Only enable as a default when `minVersionForCollab` is specified at
139
- // 2.0.0+.
131
+ // when enabled. Before Client 3.0, this became true only when callers explicitly
132
+ // selected 2.0.0 or later. Client 3.0 also uses 2.0.0 when the setting is omitted.
140
133
  "2.0.0": true,
141
134
  },
142
135
  flushMode: {
143
- // Note: 1.x clients are compatible with TurnBased flushing, but here we elect to remain on Immediate flush mode
144
- // as a work-around for inability to send batches larger than 1Mb. Immediate flushing keeps batches smaller as
145
- // fewer messages will be included per flush.
146
- "1.0.0": FlushMode.Immediate,
147
- "2.0.0-defaults": FlushMode.TurnBased,
136
+ // TurnBased flushing was already the default before callers explicitly selected a
137
+ // compatibility version.
138
+ "2.0.0": FlushMode.TurnBased,
148
139
  },
149
140
  gcOptions: {
150
- "1.0.0": {},
141
+ "2.0.0": {},
151
142
  // Although sweep is supported in 2.x, it is disabled by default until minVersionForCollab>=3.0.0 to be extra safe.
152
143
  // Note that enabling this is a significant change, that should likely be announced in the relevant version:
153
144
  // It would be bad if this simple caused the enablement when when the current package version passed this point without anyone being aware.
@@ -160,7 +151,7 @@ const runtimeOptionsAffectingDocSchemaConfigMap: ConfigMap<RuntimeOptionsAffecti
160
151
  // This feature is new and disabled by default. In the future we will enable it by default, but we have not
161
152
  // closed on the version where that will happen yet. Probably a .10 release since blob functionality is not
162
153
  // exposed on the `@public` API surface.
163
- "1.0.0": undefined,
154
+ "2.0.0": undefined,
164
155
  },
165
156
  };
166
157
 
@@ -181,32 +172,32 @@ export const runtimeOptionKeysThatRequireExplicitSchemaControl = (
181
172
  const runtimeOptionsAffectingDocSchemaConfigValidationMap: ConfigValidationMap<RuntimeOptionsAffectingDocSchema> =
182
173
  {
183
174
  enableGroupedBatching: configValueToMinVersionForCollab([
184
- [false, "1.0.0"],
185
- [true, "2.0.0-defaults"],
175
+ [false, "2.0.0"],
176
+ [true, "2.0.0"],
186
177
  ]),
187
178
  compressionOptions: configValueToMinVersionForCollab([
188
- [{ ...disabledCompressionConfig }, "1.0.0"],
189
- [{ ...enabledCompressionConfig }, "2.0.0-defaults"],
179
+ [{ ...disabledCompressionConfig }, "2.0.0"],
180
+ [{ ...enabledCompressionConfig }, "2.0.0"],
190
181
  ]),
191
182
  enableRuntimeIdCompressor: configValueToMinVersionForCollab([
192
- [undefined, "1.0.0"],
193
- ["on", "2.0.0-defaults"],
194
- ["delayed", "2.0.0-defaults"],
183
+ [undefined, "2.0.0"],
184
+ ["on", "2.0.0"],
185
+ ["delayed", "2.0.0"],
195
186
  ]),
196
187
  explicitSchemaControl: configValueToMinVersionForCollab([
197
- [false, "1.0.0"],
198
- [true, "2.0.0-defaults"],
188
+ [false, "2.0.0"],
189
+ [true, "2.0.0"],
199
190
  ]),
200
191
  flushMode: configValueToMinVersionForCollab([
201
- [FlushMode.Immediate, "1.0.0"],
202
- [FlushMode.TurnBased, "2.0.0-defaults"],
192
+ [FlushMode.Immediate, "2.0.0"],
193
+ [FlushMode.TurnBased, "2.0.0"],
203
194
  ]),
204
195
  gcOptions: configValueToMinVersionForCollab([
205
- [{ enableGCSweep: undefined }, "1.0.0"],
206
- [{ enableGCSweep: true }, "2.0.0-defaults"],
196
+ [{ enableGCSweep: undefined }, "2.0.0"],
197
+ [{ enableGCSweep: true }, "2.0.0"],
207
198
  ]),
208
199
  createBlobPayloadPending: configValueToMinVersionForCollab([
209
- [undefined, "1.0.0"],
200
+ [undefined, "2.0.0"],
210
201
  [true, "2.40.0"],
211
202
  ]),
212
203
  };
@@ -225,8 +216,8 @@ export function getMinVersionForCollabDefaults(
225
216
 
226
217
  /**
227
218
  * Validates if the runtime options passed in from the user are compatible with the minVersionForCollab.
228
- * For example, if a user sets the `enableGroupedBatching` option to true, but the minVersionForCollab
229
- * is set to "1.0.0", then we should throw a UsageError since 1.x clients do not support batching.
219
+ * For example, if a user enables an option introduced in 2.40.0 while the
220
+ * minVersionForCollab is set to "2.0.0", then we should throw a UsageError.
230
221
  * */
231
222
  export function validateRuntimeOptions(
232
223
  minVersionForCollab: OldestSupportedClientVersion,
@@ -253,6 +253,7 @@ import {
253
253
  OpSplitter,
254
254
  Outbox,
255
255
  RemoteMessageProcessor,
256
+ tryGetDeserializedRuntimeOpCopy,
256
257
  type OutboundBatch,
257
258
  type BatchResubmitInfo,
258
259
  } from "./opLifecycle/index.js";
@@ -818,10 +819,10 @@ export interface LoadContainerRuntimeParams {
818
819
  *
819
820
  * @privateRemarks
820
821
  * Used to determine the default configuration for {@link IContainerRuntimeOptionsInternal} that affect the document schema.
821
- * For example, let's say that feature `foo` was added in 2.0 which introduces a new op type. Additionally, option `bar`
822
- * was added to `IContainerRuntimeOptionsInternal` in 2.0 to enable/disable `foo` since clients prior to 2.0 would not
823
- * understand the new op type. If a customer were to set oldestSupportedClient to 2.0.0, then `bar` would be set to
824
- * enable `foo` by default. If a customer were to set oldestSupportedClient to 1.0.0, then `bar` would be set to
822
+ * For example, let's say that feature `foo` was added in 2.40 which introduces a new op type. Additionally, option `bar`
823
+ * was added to `IContainerRuntimeOptionsInternal` in 2.40 to enable/disable `foo` since clients prior to 2.40 would not
824
+ * understand the new op type. If a customer were to set oldestSupportedClient to 2.40.0, then `bar` would be set to
825
+ * enable `foo` by default. If a customer were to set oldestSupportedClient to 2.0.0, then `bar` would be set to
825
826
  * disable `foo` by default.
826
827
  */
827
828
  oldestSupportedClient?: OldestSupportedClientVersion;
@@ -1033,8 +1034,8 @@ export class ContainerRuntime
1033
1034
 
1034
1035
  // Some options require a minimum version of the FF runtime to operate, so the default configs will be generated
1035
1036
  // based on the minVersionForCollab.
1036
- // For example, if minVersionForCollab is set to "1.0.0", the default configs will ensure compatibility with FF runtime
1037
- // 1.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility
1037
+ // For example, if minVersionForCollab is set to "2.0.0", the default configs will ensure compatibility with FF runtime
1038
+ // 2.0.0 or later. If the minVersionForCollab is set to "2.10.0", the default values will be generated to ensure compatibility
1038
1039
  // with FF runtime 2.10.0 or later.
1039
1040
  if (!isValidMinVersionForCollab(minVersionForCollab)) {
1040
1041
  throw new UsageError(
@@ -1942,6 +1943,7 @@ export class ContainerRuntime
1942
1943
  const fetchOps = (context as IContainerContextInternal).fetchOps;
1943
1944
  this.versionMarkResolverInternal = new VersionMarkResolver({
1944
1945
  getCurrentSequenceNumber: () => this.deltaManager.lastSequenceNumber,
1946
+ getCurrentTimestamp: () => this.getCurrentReferenceTimestampMs(),
1945
1947
  getCurrentMinimumSequenceNumber: () => this.deltaManager.minimumSequenceNumber,
1946
1948
  getCurrentPendingBatchId: () => this.pendingStateManager.getMostRecentPendingBatchId(),
1947
1949
  logger: createChildLogger({
@@ -1951,7 +1953,8 @@ export class ContainerRuntime
1951
1953
  // Seal the current outbound batch so a just-submitted edit gets a stable batchId in the pending
1952
1954
  // state before sealAndCaptureVersionMark reads it (a batchId is only assigned when flushed).
1953
1955
  flushPendingBatch: () => this.flush(),
1954
- // Wire the container-provided op reader (if any) so resolution can read historical ops.
1956
+ // Keep this optional while a supported older loader may not provide fetchOps. AB#81034 tracks
1957
+ // making it required once the Runtime -> Loader compatibility window reaches generation 21.
1955
1958
  getHistoricalOpReader: fetchOps ? () => ({ fetchMessages: fetchOps }) : undefined,
1956
1959
  // Unpack scanned historical ops through the same pipeline the live inbound path uses, so a
1957
1960
  // chunked batch's batchId (only restored after reassembly) is observed by the history scan too.
@@ -1966,6 +1969,7 @@ export class ContainerRuntime
1966
1969
  runtimeOptions.chunkSizeInBytes,
1967
1970
  runtimeOptions.maxBatchSizeInBytes,
1968
1971
  this.mc.logger,
1972
+ { allowInitialPartialChunkStream: true },
1969
1973
  ),
1970
1974
  new OpDecompressor(this.mc.logger),
1971
1975
  new OpGroupingManager(
@@ -1974,13 +1978,11 @@ export class ContainerRuntime
1974
1978
  ),
1975
1979
  );
1976
1980
  return (op) => {
1977
- // Mirror the live path: only modern runtime-envelope ops (type Operation, with a
1978
- // client id) carry batches; skip system/server ops.
1979
- if (op.type !== MessageType.Operation || typeof op.clientId !== "string") {
1981
+ // Only runtime ops carry batches; deserialize a copy so the reader-owned op stays untouched.
1982
+ const messageCopy = tryGetDeserializedRuntimeOpCopy(op);
1983
+ if (messageCopy === undefined) {
1980
1984
  return undefined;
1981
1985
  }
1982
- const messageCopy = { ...op };
1983
- ensureContentsDeserialized(messageCopy);
1984
1986
  return scanProcessor.process(
1985
1987
  messageCopy,
1986
1988
  getSingleUseLegacyLogCallback(this.mc.logger, messageCopy.type),
@@ -3422,6 +3424,7 @@ export class ContainerRuntime
3422
3424
  this.versionMarkResolverInternal.processInboundBatch(
3423
3425
  versionMarkUpdate.sequenced.batchId,
3424
3426
  versionMarkUpdate.sequenced.sequenceNumber,
3427
+ versionMarkUpdate.sequenced.timestamp,
3425
3428
  );
3426
3429
  }
3427
3430
  this.versionMarkInboundBatchId = versionMarkUpdate.carriedBatchId;
@@ -26,6 +26,7 @@ export { DuplicateBatchDetector } from "./duplicateBatchDetector.js";
26
26
  export {
27
27
  serializeOp,
28
28
  ensureContentsDeserialized,
29
+ tryGetDeserializedRuntimeOpCopy,
29
30
  } from "./opSerialization.js";
30
31
  export {
31
32
  type BatchResubmitInfo,
@@ -36,7 +37,12 @@ export {
36
37
  } from "./outbox.js";
37
38
  export { OpCompressor } from "./opCompressor.js";
38
39
  export { OpDecompressor } from "./opDecompressor.js";
39
- export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
40
+ export {
41
+ type OpSplitterOptions,
42
+ OpSplitter,
43
+ splitOp,
44
+ isChunkedMessage,
45
+ } from "./opSplitter.js";
40
46
  export {
41
47
  type InboundMessageResult,
42
48
  type BatchStartInfo,
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IsoBuffer } from "@fluid-internal/client-utils";
6
+ import { Uint8ArrayToString } from "@fluid-internal/client-utils";
7
7
  import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
8
8
  import { assert } from "@fluidframework/core-utils/internal";
9
9
  import {
@@ -46,7 +46,7 @@ export class OpCompressor {
46
46
  const compressionStart = Date.now();
47
47
  const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
48
48
  const compressedContents = compress(contentsAsBuffer);
49
- const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
49
+ const compressedContent = Uint8ArrayToString(compressedContents, "base64");
50
50
  const duration = Date.now() - compressionStart;
51
51
 
52
52
  const messages: [OutboundBatchMessage] = [