@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
@@ -12,10 +12,15 @@ import { getEffectiveBatchId, type InboundMessageResult } from "../opLifecycle/i
12
12
  */
13
13
  export interface InboundVersionMarkUpdate {
14
14
  /**
15
- * A completed batch to record via `processInboundBatch`: its id and its last op's sequence number.
16
- * Undefined when no batch completed on this message (a piecemeal batch still in progress).
15
+ * A completed batch to record via `processInboundBatch`: its id and its last op's sequence
16
+ * number and timestamp. Undefined when no batch completed on this message (a piecemeal batch
17
+ * still in progress).
17
18
  */
18
- readonly sequenced?: { readonly batchId: string; readonly sequenceNumber: number };
19
+ readonly sequenced?: {
20
+ readonly batchId: string;
21
+ readonly sequenceNumber: number;
22
+ readonly timestamp: number;
23
+ };
19
24
  /**
20
25
  * The batch id to carry for a piecemeal batch still in progress, or undefined when none is pending.
21
26
  * The caller stores this and passes it back on the next inbound result.
@@ -25,10 +30,10 @@ export interface InboundVersionMarkUpdate {
25
30
 
26
31
  /**
27
32
  * Maps an inbound batch result to the version-mark resolver update. A batch's resolved point is its last
28
- * op's sequence number, so this reports `(batchId, sequenceNumber)` only once a batch completes. It handles
29
- * both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via the batch-start key message)
30
- * and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage` with `batchEnd`), carrying the
31
- * batch id across the piecemeal messages via `carriedBatchId`.
33
+ * op's sequence number and timestamp, so this reports `(batchId, sequenceNumber, timestamp)` only once a
34
+ * batch completes. It handles both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via
35
+ * the batch-start key message) and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`
36
+ * with `batchEnd`), carrying the batch id across the piecemeal messages via `carriedBatchId`.
32
37
  *
33
38
  * @internal
34
39
  */
@@ -45,7 +50,11 @@ export function inboundVersionMarkUpdate(
45
50
  inboundResult.messages[inboundResult.messages.length - 1] ??
46
51
  inboundResult.batchStart.keyMessage;
47
52
  return {
48
- sequenced: { batchId, sequenceNumber: lastMessage.sequenceNumber },
53
+ sequenced: {
54
+ batchId,
55
+ sequenceNumber: lastMessage.sequenceNumber,
56
+ timestamp: lastMessage.timestamp,
57
+ },
49
58
  carriedBatchId: undefined,
50
59
  };
51
60
  }
@@ -61,6 +70,7 @@ export function inboundVersionMarkUpdate(
61
70
  sequenced: {
62
71
  batchId: carriedBatchId,
63
72
  sequenceNumber: inboundResult.nextMessage.sequenceNumber,
73
+ timestamp: inboundResult.nextMessage.timestamp,
64
74
  },
65
75
  carriedBatchId: undefined,
66
76
  };
@@ -12,7 +12,6 @@ import { assert } from "@fluidframework/core-utils/internal";
12
12
  import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
13
13
 
14
14
  import type { InboundMessageResult } from "../opLifecycle/index.js";
15
- import { asBatchMetadata } from "../metadata.js";
16
15
 
17
16
  import { inboundVersionMarkUpdate } from "./inboundBatch.js";
18
17
 
@@ -31,12 +30,17 @@ export interface IHistoricalOpReader {
31
30
  }
32
31
 
33
32
  /**
34
- * Result of resolving a pending batchId.
33
+ * Result of resolving a pending batchId. A resolved result includes the matched batch's last op server
34
+ * timestamp when available. The property is optional for compatibility with previously stored results.
35
35
  *
36
36
  * @legacy @beta
37
37
  */
38
38
  export type ResolveResult =
39
- | { readonly kind: "resolved"; readonly sequenceNumber: number }
39
+ | {
40
+ readonly kind: "resolved";
41
+ readonly sequenceNumber: number;
42
+ readonly timestamp?: number;
43
+ }
40
44
  | { readonly kind: "pending" }
41
45
  | { readonly kind: "unresolvable" };
42
46
 
@@ -54,7 +58,11 @@ export type VersionMarkCapture =
54
58
  readonly batchId: string;
55
59
  readonly sequenceNumberLowerBound: number;
56
60
  }
57
- | { readonly kind: "resolved"; readonly sequenceNumber: number };
61
+ | {
62
+ readonly kind: "resolved";
63
+ readonly sequenceNumber: number;
64
+ readonly timestamp?: number;
65
+ };
58
66
 
59
67
  /**
60
68
  * Runtime-owned resolver for app-stored version mark locators.
@@ -66,34 +74,40 @@ export interface IVersionMarkResolver {
66
74
  * Captures a version mark at the current point. Seals the current outbound batch first (so a just-made
67
75
  * local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the
68
76
  * mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an
69
- * unacked local batch, or a `resolved` capture (`sequenceNumber`) when there is no in-flight local work.
77
+ * unacked local batch, or a `resolved` capture (`sequenceNumber` + the last processed op's server
78
+ * `timestamp`) when there is no in-flight local work. The timestamp property is optional both for
79
+ * compatibility with previously stored captures and because it is `undefined` when neither a last
80
+ * processed message nor a last-summary message is available.
70
81
  *
71
82
  * @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint
72
83
  * boundaries, not per keystroke.
73
84
  *
74
- * @returns The pending batch identity and exclusive sequence number lower bound, or the current sequence number
75
- * when there is no pending local batch.
85
+ * @returns The pending batch identity and inclusive sequence number lower bound, or the current sequence
86
+ * number and corresponding op timestamp (when available) when there is no pending local batch.
76
87
  */
77
88
  sealAndCaptureVersionMark(): VersionMarkCapture;
78
89
  /**
79
90
  * Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the
80
- * exclusive lower bound for a history read). A `resolved` sequence number feeds the loader's
91
+ * inclusive lower bound for a history read). A `resolved` sequence number feeds the loader's
81
92
  * `loadContainerToSequenceNumber`.
82
93
  *
83
94
  * @param batchId - The stable identity of the pending batch.
84
- * @param sequenceNumberLowerBound - The exclusive lower bound for the historical op search.
85
- * @returns The resolved sequence number, or a result indicating that the batch remains pending or can
86
- * no longer be resolved.
95
+ * @param sequenceNumberLowerBound - The inclusive lower bound for the historical op search.
96
+ * @returns The resolved sequence number and server timestamp, or a result indicating that the batch
97
+ * remains pending or can no longer be resolved.
87
98
  */
88
99
  resolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;
89
100
  /**
90
- * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber)` per batch so any connected
91
- * client can promote a matching pending mark. Returns an unsubscribe function.
101
+ * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber, timestamp)` per batch so any
102
+ * connected client can promote a matching pending mark. Returns an unsubscribe function.
92
103
  *
93
- * @param listener - Called with the stable batch identity and its final sequence number.
104
+ * @param listener - Called with the stable batch identity, its final sequence number, and the final op's
105
+ * server timestamp.
94
106
  * @returns A function that unsubscribes the listener.
95
107
  */
96
- onBatchSequenced(listener: (batchId: string, sequenceNumber: number) => void): () => void;
108
+ onBatchSequenced(
109
+ listener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,
110
+ ): () => void;
97
111
  }
98
112
 
99
113
  /**
@@ -101,6 +115,7 @@ export interface IVersionMarkResolver {
101
115
  */
102
116
  export interface VersionMarkResolverRuntimeHooks {
103
117
  readonly getCurrentSequenceNumber: () => number;
118
+ readonly getCurrentTimestamp: () => number | undefined;
104
119
  readonly getCurrentMinimumSequenceNumber: () => number;
105
120
  readonly getCurrentPendingBatchId: () => string | undefined;
106
121
  /** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */
@@ -134,9 +149,12 @@ export interface VersionMarkResolverRuntimeHooks {
134
149
  * @internal
135
150
  */
136
151
  export class VersionMarkResolver implements IVersionMarkResolver {
137
- private readonly sequenceNumberByBatchId = new Map<string, number>();
152
+ private readonly resolvedBatchById = new Map<
153
+ string,
154
+ { readonly sequenceNumber: number; readonly timestamp: number }
155
+ >();
138
156
  private readonly batchListeners = new Set<
139
- (batchId: string, sequenceNumber: number) => void
157
+ (batchId: string, sequenceNumber: number, timestamp: number) => void
140
158
  >();
141
159
  /**
142
160
  * Sticky flag: false until the feature is first used this session (a pending capture or a listener).
@@ -156,60 +174,113 @@ export class VersionMarkResolver implements IVersionMarkResolver {
156
174
  // Seal the current batch so a just-submitted local edit is flushed into the pending state with a
157
175
  // stable batchId (which is only assigned at flush) before we read it.
158
176
  this.hooks.flushPendingBatch();
159
- const sequenceNumberLowerBound = this.hooks.getCurrentSequenceNumber();
177
+ const referenceSequenceNumber = this.hooks.getCurrentSequenceNumber();
160
178
  const batchId = this.hooks.getCurrentPendingBatchId();
179
+ let result: VersionMarkCapture;
161
180
  if (batchId === undefined) {
162
181
  // No unacked local batch: the mark already points at a durable sequence number.
163
- return { kind: "resolved", sequenceNumber: sequenceNumberLowerBound };
182
+ result = {
183
+ kind: "resolved",
184
+ sequenceNumber: referenceSequenceNumber,
185
+ timestamp: this.hooks.getCurrentTimestamp(),
186
+ };
187
+ } else {
188
+ // A pending mark needs its batch tracked so resolve() can promote it from the live map.
189
+ this.tracking = true;
190
+ // The pending batch is sequenced after the reference point, so its first possible sequence
191
+ // number is `referenceSequenceNumber + 1`. Store that as an inclusive lower bound so resolve()
192
+ // scans directly from it.
193
+ result = {
194
+ kind: "pending",
195
+ batchId,
196
+ sequenceNumberLowerBound: referenceSequenceNumber + 1,
197
+ };
164
198
  }
165
- // A pending mark needs its batch tracked so resolve() can promote it from the live map.
166
- this.tracking = true;
167
- return { kind: "pending", batchId, sequenceNumberLowerBound };
199
+ this.hooks.logger.sendTelemetryEvent({ eventName: "Capture", kind: result.kind });
200
+ return result;
168
201
  }
169
202
 
170
203
  public async resolve(
171
204
  batchId: string,
172
205
  sequenceNumberLowerBound: number,
173
206
  ): Promise<ResolveResult> {
174
- // Fast path: batch sequenced live this session.
175
- const sequenceNumber = this.sessionSequenceFor(batchId);
176
- if (sequenceNumber !== undefined) {
177
- return { kind: "resolved", sequenceNumber };
178
- }
179
-
180
- // Otherwise scan history from the mark's reference point.
181
- const reader = this.hooks.getHistoricalOpReader?.();
182
- if (reader === undefined) {
183
- // No reader: the batch may still sequence live, so report pending.
184
- return { kind: "pending" };
207
+ const startTime = Date.now();
208
+ // Track from here so inbound batches are recorded even without a prior capture/subscribe; otherwise
209
+ // a batch sequencing during the scan (or live, in the no-reader case) is skipped and unrecoverable.
210
+ this.tracking = true;
211
+ // Defaults cover the throw path (only the history scan can throw — e.g. an unpacker
212
+ // DataCorruptionError or the 0xd1c reader-contract assert): the Resolve event still fires via
213
+ // `finally`, with outcome "error".
214
+ let path: "session" | "history" | "noReader" = "history";
215
+ let outcome: ResolveResult["kind"] | "error" = "error";
216
+ let resolvedSequenceNumber: number | undefined;
217
+ try {
218
+ // Fast path: batch sequenced live this session.
219
+ const resolvedBatch = this.sessionResolutionFor(batchId);
220
+ if (resolvedBatch === undefined) {
221
+ const reader = this.hooks.getHistoricalOpReader?.();
222
+ if (reader === undefined) {
223
+ // No reader: the batch may still sequence live, so report pending.
224
+ path = "noReader";
225
+ outcome = "pending";
226
+ return { kind: "pending" };
227
+ }
228
+ // Otherwise scan history from the mark's reference point.
229
+ path = "history";
230
+ let result = await this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
231
+ if (result.kind !== "resolved") {
232
+ // The batch may have sequenced live while the history scan was in progress. Prefer that
233
+ // authoritative in-session result over a stale history miss.
234
+ const liveResult = this.sessionResolutionFor(batchId);
235
+ if (liveResult !== undefined) {
236
+ path = "session";
237
+ result = { kind: "resolved", ...liveResult };
238
+ }
239
+ }
240
+ outcome = result.kind;
241
+ if (result.kind === "resolved") {
242
+ resolvedSequenceNumber = result.sequenceNumber;
243
+ }
244
+ return result;
245
+ }
246
+ path = "session";
247
+ outcome = "resolved";
248
+ resolvedSequenceNumber = resolvedBatch.sequenceNumber;
249
+ return { kind: "resolved", ...resolvedBatch };
250
+ } finally {
251
+ this.hooks.logger.sendTelemetryEvent({
252
+ eventName: "Resolve",
253
+ outcome,
254
+ path,
255
+ durationMs: Date.now() - startTime,
256
+ ...(resolvedSequenceNumber === undefined
257
+ ? {}
258
+ : { sequenceNumber: resolvedSequenceNumber }),
259
+ });
185
260
  }
186
- return this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
187
261
  }
188
262
 
189
263
  /**
190
- * Scans sequenced ops after `sequenceNumberLowerBound` for `batchId`, returning its last op's
264
+ * Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's
191
265
  * sequence number when found and aborting the fetch once found or exhausted. Each op is routed through
192
266
  * the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and
193
- * {@link inboundVersionMarkUpdate} derives its batch identity, so chunked batches resolve here too. The
194
- * scan anchor is not guaranteed to be a batch boundary, so a leading batch clipped by the anchor is
195
- * tolerated (its orphan end marker is dropped rather than fed to the processor). On a miss,
196
- * {@link classifyMiss} distinguishes `pending` (not sequenced yet) from `unresolvable` (ops trimmed).
197
- * See DEV.md.
267
+ * {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.
268
+ * The injected historical unpacker owns virtualization details, including tolerating a requested range
269
+ * that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes
270
+ * `pending` (not sequenced yet) from `unresolvable` (ops trimmed).
198
271
  */
199
272
  private async resolveFromHistory(
200
273
  reader: IHistoricalOpReader,
201
274
  batchId: string,
202
275
  sequenceNumberLowerBound: number,
203
276
  ): Promise<ResolveResult> {
204
- const from = sequenceNumberLowerBound + 1;
277
+ const from = sequenceNumberLowerBound;
205
278
  const unpack = this.hooks.createHistoricalOpUnpacker?.();
206
279
  const abortController = new AbortController();
207
280
  try {
208
281
  const stream = await reader.fetchMessages(from, undefined, abortController.signal);
209
282
  let firstScannedSequenceNumber: number | undefined;
210
283
  let carriedBatchId: string | undefined;
211
- // Mirrors the processor's batch-in-progress state so we can drop an orphan end marker (below).
212
- let inBatch = false;
213
284
  while (true) {
214
285
  const result = await stream.read();
215
286
  if (result.done) {
@@ -217,32 +288,21 @@ export class VersionMarkResolver implements IVersionMarkResolver {
217
288
  }
218
289
  for (const op of result.value) {
219
290
  firstScannedSequenceNumber ??= op.sequenceNumber;
220
- if (!inBatch && asBatchMetadata(op.metadata)?.batch === false) {
221
- // The anchor (`sequenceNumberLowerBound + 1`) may land inside a batch whose start
222
- // precedes the window; its orphan end marker would trip the processor's 0x9d5 assert.
223
- // Drop the clipped tail. The target batch is fully in-window, so this can't skip it.
224
- continue;
225
- }
226
- // undefined = a system op, or an incomplete chunk awaiting later fragments.
291
+ // undefined = a system op or virtualized input that has not produced a complete result.
227
292
  const inboundResult = unpack?.(op);
228
293
  if (inboundResult === undefined) {
229
294
  continue;
230
295
  }
231
- // Track batch progress with the processor so the guard above skips only orphan ends.
232
- if (inboundResult.type === "batchStartingMessage") {
233
- inBatch = true;
234
- } else if (
235
- inboundResult.type === "nextBatchMessage" &&
236
- inboundResult.batchEnd === true
237
- ) {
238
- inBatch = false;
239
- }
240
296
  // Derive batch identity exactly as the live inbound path does, carrying the id across a
241
297
  // piecemeal batch's messages.
242
298
  const update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);
243
299
  carriedBatchId = update.carriedBatchId;
244
300
  if (update.sequenced?.batchId === batchId) {
245
- return { kind: "resolved", sequenceNumber: update.sequenced.sequenceNumber };
301
+ return {
302
+ kind: "resolved",
303
+ sequenceNumber: update.sequenced.sequenceNumber,
304
+ timestamp: update.sequenced.timestamp,
305
+ };
246
306
  }
247
307
  }
248
308
  }
@@ -269,7 +329,7 @@ export class VersionMarkResolver implements IVersionMarkResolver {
269
329
  );
270
330
  const tip = this.hooks.getCurrentSequenceNumber();
271
331
  if (from > tip) {
272
- // Nothing is sequenced at/after the mark's reference point yet, so the batch cannot have landed.
332
+ // Nothing is sequenced at/after the mark's lower bound yet, so the batch cannot have landed.
273
333
  return { kind: "pending" };
274
334
  }
275
335
  if (firstScannedSequenceNumber === undefined) {
@@ -279,15 +339,15 @@ export class VersionMarkResolver implements IVersionMarkResolver {
279
339
  return { kind: "unresolvable" };
280
340
  }
281
341
  if (firstScannedSequenceNumber > from) {
282
- // A trim gap at the anchor: the mark's ops (just after the reference point) are gone.
342
+ // A trim gap at the anchor: the mark's ops (at/after its lower bound) are gone.
283
343
  return { kind: "unresolvable" };
284
344
  }
285
- // Ops are present from the reference point and the batch is not among them: not yet sequenced.
345
+ // Ops are present from the lower bound and the batch is not among them: not yet sequenced.
286
346
  return { kind: "pending" };
287
347
  }
288
348
 
289
349
  public onBatchSequenced(
290
- listener: (batchId: string, sequenceNumber: number) => void,
350
+ listener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,
291
351
  ): () => void {
292
352
  // A subscriber wants live promotions, so start tracking inbound batches from here on.
293
353
  this.tracking = true;
@@ -297,23 +357,32 @@ export class VersionMarkResolver implements IVersionMarkResolver {
297
357
  };
298
358
  }
299
359
 
300
- /** The sequence number for a batch seen live this session, or undefined. */
301
- private sessionSequenceFor(batchId: string): number | undefined {
302
- return this.sequenceNumberByBatchId.get(batchId);
360
+ /** The resolved point for a batch seen live this session, or undefined. */
361
+ private sessionResolutionFor(
362
+ batchId: string,
363
+ ): { readonly sequenceNumber: number; readonly timestamp: number } | undefined {
364
+ return this.resolvedBatchById.get(batchId);
303
365
  }
304
366
 
305
367
  /** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */
306
- public processInboundBatch(batchId: string, sequenceNumber: number): void {
307
- const existingSequenceNumber = this.sequenceNumberByBatchId.get(batchId);
308
- if (existingSequenceNumber === sequenceNumber) {
368
+ public processInboundBatch(
369
+ batchId: string,
370
+ sequenceNumber: number,
371
+ timestamp: number,
372
+ ): void {
373
+ const existingResolution = this.resolvedBatchById.get(batchId);
374
+ if (existingResolution !== undefined) {
375
+ // NOTE: It's possible (but exceedingly rare) that this is a spurious duplicated batch,
376
+ // with a different sequence number/timestamp.
377
+ // Rather than asserting or overwriting, we let the earlier version mark stand, it is sufficient.
309
378
  return;
310
379
  }
311
380
 
312
- this.sequenceNumberByBatchId.set(batchId, sequenceNumber);
381
+ this.resolvedBatchById.set(batchId, { sequenceNumber, timestamp });
313
382
  this.evictBelowMinimumSequenceNumber();
314
383
  for (const listener of this.batchListeners) {
315
384
  try {
316
- listener(batchId, sequenceNumber);
385
+ listener(batchId, sequenceNumber, timestamp);
317
386
  } catch (error) {
318
387
  // Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw
319
388
  // must not abort op processing or starve the rest. Log and continue rather than fault the
@@ -331,11 +400,11 @@ export class VersionMarkResolver implements IVersionMarkResolver {
331
400
  */
332
401
  private evictBelowMinimumSequenceNumber(): void {
333
402
  const minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();
334
- for (const [id, sequenceNumber] of this.sequenceNumberByBatchId) {
335
- if (sequenceNumber >= minimumSequenceNumber) {
403
+ for (const [id, resolution] of this.resolvedBatchById) {
404
+ if (resolution.sequenceNumber >= minimumSequenceNumber) {
336
405
  break;
337
406
  }
338
- this.sequenceNumberByBatchId.delete(id);
407
+ this.resolvedBatchById.delete(id);
339
408
  }
340
409
  }
341
410
  }
package/tsconfig.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../../common/build/build-common/tsconfig.node16.json",
2
+ "extends": "../../../common/build/build-common/tsconfig.node20.json",
3
3
  "include": ["src/**/*"],
4
4
  "exclude": ["src/test/**/*"],
5
5
  "compilerOptions": {
package/internal.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- /*
7
- * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
8
- * Generated by "flub generate entrypoints --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./lib --node10TypeCompat" in @fluid-tools/build-cli.
9
- */
10
-
11
- export * from "./lib/index.js";
package/legacy/alpha.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- /*
7
- * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
8
- * Generated by "flub generate entrypoints --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./lib --node10TypeCompat" in @fluid-tools/build-cli.
9
- */
10
-
11
- export * from "../lib/legacyAlpha.js";
package/legacy.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- /*
7
- * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
8
- * Generated by "flub generate entrypoints --outFileLegacyBeta legacy --outFileLegacyAlpha legacyAlpha --outDir ./lib --node10TypeCompat" in @fluid-tools/build-cli.
9
- */
10
-
11
- export * from "./lib/legacy.js";