@fluidframework/container-runtime 2.118.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +68 -4
  2. package/README.md +53 -50
  3. package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
  4. package/api-report/container-runtime.legacy.alpha.api.md +3 -1
  5. package/api-report/container-runtime.legacy.beta.api.md +3 -1
  6. package/container-runtime.test-files.tar +0 -0
  7. package/dist/batchTracker.d.ts.map +1 -1
  8. package/dist/batchTracker.js +5 -1
  9. package/dist/batchTracker.js.map +1 -1
  10. package/dist/blobManager/blobManager.d.ts +1 -1
  11. package/dist/blobManager/blobManager.d.ts.map +1 -1
  12. package/dist/blobManager/blobManager.js +264 -231
  13. package/dist/blobManager/blobManager.js.map +1 -1
  14. package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  15. package/dist/channelCollection.d.ts.map +1 -1
  16. package/dist/channelCollection.js +70 -58
  17. package/dist/channelCollection.js.map +1 -1
  18. package/dist/connectionTelemetry.js +38 -20
  19. package/dist/connectionTelemetry.js.map +1 -1
  20. package/dist/containerCompatibility.d.ts +3 -3
  21. package/dist/containerCompatibility.d.ts.map +1 -1
  22. package/dist/containerCompatibility.js +36 -45
  23. package/dist/containerCompatibility.js.map +1 -1
  24. package/dist/containerHandleContext.d.ts.map +1 -1
  25. package/dist/containerHandleContext.js +4 -0
  26. package/dist/containerHandleContext.js.map +1 -1
  27. package/dist/containerRuntime.d.ts +4 -4
  28. package/dist/containerRuntime.d.ts.map +1 -1
  29. package/dist/containerRuntime.js +346 -223
  30. package/dist/containerRuntime.js.map +1 -1
  31. package/dist/dataStore.d.ts.map +1 -1
  32. package/dist/dataStore.js +9 -1
  33. package/dist/dataStore.js.map +1 -1
  34. package/dist/dataStoreContext.d.ts.map +1 -1
  35. package/dist/dataStoreContext.js +184 -127
  36. package/dist/dataStoreContext.js.map +1 -1
  37. package/dist/dataStoreContexts.js +43 -42
  38. package/dist/dataStoreContexts.js.map +1 -1
  39. package/dist/dataStoreRegistry.js +1 -0
  40. package/dist/dataStoreRegistry.js.map +1 -1
  41. package/dist/deltaManagerProxies.js +5 -0
  42. package/dist/deltaManagerProxies.js.map +1 -1
  43. package/dist/deltaScheduler.js +104 -99
  44. package/dist/deltaScheduler.js.map +1 -1
  45. package/dist/error.js +2 -1
  46. package/dist/error.js.map +1 -1
  47. package/dist/gc/garbageCollection.js +85 -55
  48. package/dist/gc/garbageCollection.js.map +1 -1
  49. package/dist/gc/gcConfigs.js +2 -3
  50. package/dist/gc/gcConfigs.js.map +1 -1
  51. package/dist/gc/gcHelpers.js +12 -13
  52. package/dist/gc/gcHelpers.js.map +1 -1
  53. package/dist/gc/gcReferenceGraphAlgorithm.js +1 -2
  54. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
  55. package/dist/gc/gcSummaryStateTracker.js +8 -3
  56. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  57. package/dist/gc/gcTelemetry.js +12 -5
  58. package/dist/gc/gcTelemetry.js.map +1 -1
  59. package/dist/gc/gcUnreferencedStateTracker.js +18 -1
  60. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  61. package/dist/inboundBatchAggregator.js +122 -117
  62. package/dist/inboundBatchAggregator.js.map +1 -1
  63. package/dist/metadata.d.ts.map +1 -1
  64. package/dist/metadata.js +3 -3
  65. package/dist/metadata.js.map +1 -1
  66. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  67. package/dist/opLifecycle/batchManager.js +11 -5
  68. package/dist/opLifecycle/batchManager.js.map +1 -1
  69. package/dist/opLifecycle/duplicateBatchDetector.js +18 -18
  70. package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
  71. package/dist/opLifecycle/index.d.ts +2 -2
  72. package/dist/opLifecycle/index.d.ts.map +1 -1
  73. package/dist/opLifecycle/index.js +2 -1
  74. package/dist/opLifecycle/index.js.map +1 -1
  75. package/dist/opLifecycle/opCompressor.js +2 -1
  76. package/dist/opLifecycle/opCompressor.js.map +1 -1
  77. package/dist/opLifecycle/opDecompressor.js +10 -6
  78. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  79. package/dist/opLifecycle/opGroupingManager.js +5 -3
  80. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  81. package/dist/opLifecycle/opSerialization.d.ts +11 -1
  82. package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
  83. package/dist/opLifecycle/opSerialization.js +22 -6
  84. package/dist/opLifecycle/opSerialization.js.map +1 -1
  85. package/dist/opLifecycle/opSplitter.d.ts +17 -1
  86. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  87. package/dist/opLifecycle/opSplitter.js +46 -9
  88. package/dist/opLifecycle/opSplitter.js.map +1 -1
  89. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  90. package/dist/opLifecycle/outbox.js +17 -13
  91. package/dist/opLifecycle/outbox.js.map +1 -1
  92. package/dist/opLifecycle/remoteMessageProcessor.js +6 -3
  93. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  94. package/dist/opProperties.d.ts.map +1 -1
  95. package/dist/packageVersion.d.ts +1 -1
  96. package/dist/packageVersion.d.ts.map +1 -1
  97. package/dist/packageVersion.js +1 -1
  98. package/dist/packageVersion.js.map +1 -1
  99. package/dist/pendingStateManager.js +54 -47
  100. package/dist/pendingStateManager.js.map +1 -1
  101. package/dist/runCounter.js +10 -30
  102. package/dist/runCounter.js.map +1 -1
  103. package/dist/runtimeLayerCompatState.d.ts +1 -1
  104. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  105. package/dist/runtimeLayerCompatState.js +31 -11
  106. package/dist/runtimeLayerCompatState.js.map +1 -1
  107. package/dist/signalTelemetryProcessing.js +15 -17
  108. package/dist/signalTelemetryProcessing.js.map +1 -1
  109. package/dist/storageServiceWithAttachBlobs.js +2 -0
  110. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  111. package/dist/summary/documentSchema.d.ts.map +1 -1
  112. package/dist/summary/documentSchema.js +51 -13
  113. package/dist/summary/documentSchema.js.map +1 -1
  114. package/dist/summary/orderedClientElection.js +25 -17
  115. package/dist/summary/orderedClientElection.js.map +1 -1
  116. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  117. package/dist/summary/summarizerClientElection.js +18 -7
  118. package/dist/summary/summarizerClientElection.js.map +1 -1
  119. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  120. package/dist/summary/summarizerNode/summarizerNode.js +22 -13
  121. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  122. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  123. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
  124. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  125. package/dist/summary/summarizerUtils.d.ts.map +1 -1
  126. package/dist/summary/summarizerUtils.js +4 -3
  127. package/dist/summary/summarizerUtils.js.map +1 -1
  128. package/dist/summary/summaryCollection.js +23 -11
  129. package/dist/summary/summaryCollection.js.map +1 -1
  130. package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
  131. package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
  132. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  133. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
  134. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  135. package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  136. package/dist/summary/summaryDelayLoadedModule/summarizer.js +15 -6
  137. package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  138. package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
  139. package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
  140. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
  141. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  142. package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
  143. package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
  144. package/dist/summary/summaryFormat.d.ts.map +1 -1
  145. package/dist/summary/summaryFormat.js +7 -7
  146. package/dist/summary/summaryFormat.js.map +1 -1
  147. package/dist/summary/summaryHelpers.js +5 -5
  148. package/dist/summary/summaryHelpers.js.map +1 -1
  149. package/dist/summary/summaryManager.js +66 -54
  150. package/dist/summary/summaryManager.js.map +1 -1
  151. package/dist/throttler.d.ts.map +1 -1
  152. package/dist/throttler.js +4 -1
  153. package/dist/throttler.js.map +1 -1
  154. package/dist/versionMarks/inboundBatch.d.ts +8 -6
  155. package/dist/versionMarks/inboundBatch.d.ts.map +1 -1
  156. package/dist/versionMarks/inboundBatch.js +11 -7
  157. package/dist/versionMarks/inboundBatch.js.map +1 -1
  158. package/dist/versionMarks/versionMarkResolver.d.ts +30 -23
  159. package/dist/versionMarks/versionMarkResolver.d.ts.map +1 -1
  160. package/dist/versionMarks/versionMarkResolver.js +112 -63
  161. package/dist/versionMarks/versionMarkResolver.js.map +1 -1
  162. package/lib/batchTracker.d.ts.map +1 -1
  163. package/lib/batchTracker.js +5 -1
  164. package/lib/batchTracker.js.map +1 -1
  165. package/lib/blobManager/blobManager.d.ts +1 -1
  166. package/lib/blobManager/blobManager.d.ts.map +1 -1
  167. package/lib/blobManager/blobManager.js +264 -231
  168. package/lib/blobManager/blobManager.js.map +1 -1
  169. package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  170. package/lib/channelCollection.d.ts.map +1 -1
  171. package/lib/channelCollection.js +65 -53
  172. package/lib/channelCollection.js.map +1 -1
  173. package/lib/connectionTelemetry.js +36 -18
  174. package/lib/connectionTelemetry.js.map +1 -1
  175. package/lib/containerCompatibility.d.ts +3 -3
  176. package/lib/containerCompatibility.d.ts.map +1 -1
  177. package/lib/containerCompatibility.js +33 -42
  178. package/lib/containerCompatibility.js.map +1 -1
  179. package/lib/containerHandleContext.d.ts.map +1 -1
  180. package/lib/containerHandleContext.js +4 -0
  181. package/lib/containerHandleContext.js.map +1 -1
  182. package/lib/containerRuntime.d.ts +4 -4
  183. package/lib/containerRuntime.d.ts.map +1 -1
  184. package/lib/containerRuntime.js +341 -218
  185. package/lib/containerRuntime.js.map +1 -1
  186. package/lib/dataStore.d.ts.map +1 -1
  187. package/lib/dataStore.js +9 -1
  188. package/lib/dataStore.js.map +1 -1
  189. package/lib/dataStoreContext.d.ts.map +1 -1
  190. package/lib/dataStoreContext.js +182 -125
  191. package/lib/dataStoreContext.js.map +1 -1
  192. package/lib/dataStoreContexts.js +43 -42
  193. package/lib/dataStoreContexts.js.map +1 -1
  194. package/lib/dataStoreRegistry.js +1 -0
  195. package/lib/dataStoreRegistry.js.map +1 -1
  196. package/lib/deltaManagerProxies.js +5 -0
  197. package/lib/deltaManagerProxies.js.map +1 -1
  198. package/lib/deltaScheduler.js +104 -99
  199. package/lib/deltaScheduler.js.map +1 -1
  200. package/lib/error.js +2 -1
  201. package/lib/error.js.map +1 -1
  202. package/lib/gc/garbageCollection.js +85 -55
  203. package/lib/gc/garbageCollection.js.map +1 -1
  204. package/lib/gc/gcSummaryStateTracker.js +8 -3
  205. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  206. package/lib/gc/gcTelemetry.js +12 -5
  207. package/lib/gc/gcTelemetry.js.map +1 -1
  208. package/lib/gc/gcUnreferencedStateTracker.js +18 -1
  209. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
  210. package/lib/inboundBatchAggregator.js +122 -117
  211. package/lib/inboundBatchAggregator.js.map +1 -1
  212. package/lib/legacy.d.ts +1 -1
  213. package/lib/legacyAlpha.d.ts +1 -1
  214. package/lib/metadata.d.ts.map +1 -1
  215. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  216. package/lib/opLifecycle/batchManager.js +8 -2
  217. package/lib/opLifecycle/batchManager.js.map +1 -1
  218. package/lib/opLifecycle/duplicateBatchDetector.js +18 -18
  219. package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
  220. package/lib/opLifecycle/index.d.ts +2 -2
  221. package/lib/opLifecycle/index.d.ts.map +1 -1
  222. package/lib/opLifecycle/index.js +2 -2
  223. package/lib/opLifecycle/index.js.map +1 -1
  224. package/lib/opLifecycle/opCompressor.js +3 -2
  225. package/lib/opLifecycle/opCompressor.js.map +1 -1
  226. package/lib/opLifecycle/opDecompressor.js +10 -6
  227. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  228. package/lib/opLifecycle/opGroupingManager.js +3 -1
  229. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  230. package/lib/opLifecycle/opSerialization.d.ts +11 -1
  231. package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
  232. package/lib/opLifecycle/opSerialization.js +16 -0
  233. package/lib/opLifecycle/opSerialization.js.map +1 -1
  234. package/lib/opLifecycle/opSplitter.d.ts +17 -1
  235. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  236. package/lib/opLifecycle/opSplitter.js +44 -7
  237. package/lib/opLifecycle/opSplitter.js.map +1 -1
  238. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  239. package/lib/opLifecycle/outbox.js +14 -10
  240. package/lib/opLifecycle/outbox.js.map +1 -1
  241. package/lib/opLifecycle/remoteMessageProcessor.js +4 -1
  242. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  243. package/lib/opProperties.d.ts.map +1 -1
  244. package/lib/packageVersion.d.ts +1 -1
  245. package/lib/packageVersion.d.ts.map +1 -1
  246. package/lib/packageVersion.js +1 -1
  247. package/lib/packageVersion.js.map +1 -1
  248. package/lib/pendingStateManager.js +52 -45
  249. package/lib/pendingStateManager.js.map +1 -1
  250. package/lib/public.d.ts +1 -1
  251. package/lib/runCounter.js +10 -30
  252. package/lib/runCounter.js.map +1 -1
  253. package/lib/runtimeLayerCompatState.d.ts +1 -1
  254. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  255. package/lib/runtimeLayerCompatState.js +28 -8
  256. package/lib/runtimeLayerCompatState.js.map +1 -1
  257. package/lib/signalTelemetryProcessing.js +15 -17
  258. package/lib/signalTelemetryProcessing.js.map +1 -1
  259. package/lib/storageServiceWithAttachBlobs.js +2 -0
  260. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  261. package/lib/summary/documentSchema.d.ts.map +1 -1
  262. package/lib/summary/documentSchema.js +51 -13
  263. package/lib/summary/documentSchema.js.map +1 -1
  264. package/lib/summary/orderedClientElection.js +25 -17
  265. package/lib/summary/orderedClientElection.js.map +1 -1
  266. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  267. package/lib/summary/summarizerClientElection.js +18 -7
  268. package/lib/summary/summarizerClientElection.js.map +1 -1
  269. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  270. package/lib/summary/summarizerNode/summarizerNode.js +22 -13
  271. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  272. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  273. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
  274. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  275. package/lib/summary/summarizerUtils.d.ts.map +1 -1
  276. package/lib/summary/summarizerUtils.js +1 -0
  277. package/lib/summary/summarizerUtils.js.map +1 -1
  278. package/lib/summary/summaryCollection.js +23 -11
  279. package/lib/summary/summaryCollection.js.map +1 -1
  280. package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
  281. package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
  282. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  283. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
  284. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  285. package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
  286. package/lib/summary/summaryDelayLoadedModule/summarizer.js +15 -6
  287. package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  288. package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
  289. package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
  290. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
  291. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  292. package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
  293. package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
  294. package/lib/summary/summaryFormat.d.ts.map +1 -1
  295. package/lib/summary/summaryManager.js +66 -54
  296. package/lib/summary/summaryManager.js.map +1 -1
  297. package/lib/throttler.d.ts.map +1 -1
  298. package/lib/throttler.js +4 -1
  299. package/lib/throttler.js.map +1 -1
  300. package/lib/versionMarks/inboundBatch.d.ts +8 -6
  301. package/lib/versionMarks/inboundBatch.d.ts.map +1 -1
  302. package/lib/versionMarks/inboundBatch.js +10 -5
  303. package/lib/versionMarks/inboundBatch.js.map +1 -1
  304. package/lib/versionMarks/versionMarkResolver.d.ts +30 -23
  305. package/lib/versionMarks/versionMarkResolver.d.ts.map +1 -1
  306. package/lib/versionMarks/versionMarkResolver.js +112 -63
  307. package/lib/versionMarks/versionMarkResolver.js.map +1 -1
  308. package/package.json +60 -102
  309. package/src/containerCompatibility.ts +33 -42
  310. package/src/containerRuntime.ts +15 -12
  311. package/src/opLifecycle/index.ts +7 -1
  312. package/src/opLifecycle/opCompressor.ts +2 -2
  313. package/src/opLifecycle/opSerialization.ts +22 -1
  314. package/src/opLifecycle/opSplitter.ts +63 -6
  315. package/src/packageVersion.ts +1 -1
  316. package/src/runtimeLayerCompatState.ts +28 -21
  317. package/src/summary/documentSchema.ts +31 -6
  318. package/src/versionMarks/DEV.md +595 -157
  319. package/src/versionMarks/inboundBatch.ts +18 -8
  320. package/src/versionMarks/versionMarkResolver.ts +144 -75
  321. package/tsconfig.json +1 -1
  322. package/internal.d.ts +0 -11
  323. package/legacy/alpha.d.ts +0 -11
  324. package/legacy.d.ts +0 -11
  325. /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
  326. /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
@@ -21,18 +21,34 @@ const blobManagerSnapSum_js_1 = require("./blobManagerSnapSum.js");
21
21
  * and loads blob.
22
22
  */
23
23
  class BlobHandle extends internal_3.FluidHandleBase {
24
+ path;
25
+ routeContext;
26
+ get;
27
+ payloadPending;
28
+ sharePayloadCallback;
29
+ attachGraphCallback;
30
+ attached = false;
31
+ payloadSharingStarted = false;
24
32
  get isAttached() {
25
33
  return this.routeContext.isAttached && this.attached;
26
34
  }
35
+ _events;
27
36
  get events() {
28
37
  return (this._events ??= (0, client_utils_1.createEmitter)());
29
38
  }
39
+ _payloadState;
30
40
  get payloadState() {
31
41
  return this._payloadState;
32
42
  }
43
+ /**
44
+ * The error property starts undefined, signalling that there has been no error yet.
45
+ * If an error occurs, the property will contain the error.
46
+ */
47
+ _payloadShareError;
33
48
  get payloadShareError() {
34
49
  return this._payloadShareError;
35
50
  }
51
+ absolutePath;
36
52
  constructor(path, routeContext,
37
53
  // TODO: just take the blob rather than a get function?
38
54
  get, payloadPending, sharePayloadCallback, attachGraphCallback) {
@@ -43,19 +59,17 @@ class BlobHandle extends internal_3.FluidHandleBase {
43
59
  this.payloadPending = payloadPending;
44
60
  this.sharePayloadCallback = sharePayloadCallback;
45
61
  this.attachGraphCallback = attachGraphCallback;
46
- this.attached = false;
47
- this.payloadSharingStarted = false;
48
- this.notifyShared = () => {
49
- this._payloadState = "shared";
50
- this._events?.emit("payloadShared");
51
- };
52
- this.notifyFailed = (error) => {
53
- this._payloadShareError = error;
54
- this._events?.emit("payloadShareFailed", error);
55
- };
56
62
  this._payloadState = payloadPending ? "pending" : "shared";
57
63
  this.absolutePath = (0, internal_3.generateHandleContextPath)(path, this.routeContext);
58
64
  }
65
+ notifyShared = () => {
66
+ this._payloadState = "shared";
67
+ this._events?.emit("payloadShared");
68
+ };
69
+ notifyFailed = (error) => {
70
+ this._payloadShareError = error;
71
+ this._events?.emit("payloadShareFailed", error);
72
+ };
59
73
  sharePayload() {
60
74
  if (this.payloadState === "shared" || this.payloadSharingStarted) {
61
75
  return;
@@ -84,228 +98,41 @@ const createAbortError = () => new internal_4.LoggingError("uploadBlob aborted")
84
98
  */
85
99
  exports.blobManagerBasePath = "_blobs";
86
100
  class BlobManager {
101
+ mc;
102
+ internalEvents = (0, client_utils_1.createEmitter)();
103
+ /**
104
+ * Map of local IDs to storage IDs. Also includes identity mappings of storage ID to storage ID for all known
105
+ * storage IDs. All requested IDs must be a key in this map. Blobs created while the container is detached are
106
+ * stored in IDetachedBlobStorage which gives pseudo storage IDs; the real storage IDs are filled in at attach
107
+ * time via setRedirectTable().
108
+ */
109
+ redirectTable;
110
+ /**
111
+ * The localBlobCache has a dual role of caching locally-created blobs, as well as tracking their state as they
112
+ * are shared. Keys are localIds.
113
+ */
114
+ localBlobCache = new Map();
115
+ /**
116
+ * Blobs with an attached handle that have not finished blob-attaching are the set we need to provide from
117
+ * getPendingState(). This stores their local IDs, and then we can look them up against the localBlobCache.
118
+ */
119
+ pendingBlobsWithAttachedHandles = new Set();
120
+ /**
121
+ * Local IDs for any pending blobs we loaded with and have not yet started the upload/attach flow for.
122
+ */
123
+ pendingOnlyLocalIds = new Set();
124
+ sendBlobAttachMessage;
125
+ routeContext;
126
+ storage;
127
+ // Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.
128
+ // blobPath's format - `/<basePath>/<localId>`.
129
+ blobRequested;
130
+ // Called to check if a blob has been deleted by GC.
131
+ // blobPath's format - `/<basePath>/<localId>`.
132
+ isBlobDeleted;
133
+ runtime;
134
+ createBlobPayloadPending;
87
135
  constructor(props) {
88
- this.internalEvents = (0, client_utils_1.createEmitter)();
89
- /**
90
- * The localBlobCache has a dual role of caching locally-created blobs, as well as tracking their state as they
91
- * are shared. Keys are localIds.
92
- */
93
- this.localBlobCache = new Map();
94
- /**
95
- * Blobs with an attached handle that have not finished blob-attaching are the set we need to provide from
96
- * getPendingState(). This stores their local IDs, and then we can look them up against the localBlobCache.
97
- */
98
- this.pendingBlobsWithAttachedHandles = new Set();
99
- /**
100
- * Local IDs for any pending blobs we loaded with and have not yet started the upload/attach flow for.
101
- */
102
- this.pendingOnlyLocalIds = new Set();
103
- /**
104
- * Upload and attach the localBlobCache entry for the given localId.
105
- *
106
- * Expects the localBlobCache entry for the given localId to be in either localOnly or uploaded state
107
- * when called. Returns a promise that resolves when the blob completes uploading and attaching, or else
108
- * rejects if an error is encountered or the signal is aborted.
109
- */
110
- this.uploadAndAttach = async (localId, signal) => {
111
- if (signal?.aborted === true) {
112
- this.localBlobCache.delete(localId);
113
- this.pendingBlobsWithAttachedHandles.delete(localId);
114
- throw createAbortError();
115
- }
116
- const localBlobRecordInitial = this.localBlobCache.get(localId);
117
- (0, internal_2.assert)(localBlobRecordInitial?.state === "localOnly" ||
118
- localBlobRecordInitial?.state === "uploaded", 0xc80 /* Expect uploadAndAttach to be called with either localOnly or uploaded state */);
119
- const { blob } = localBlobRecordInitial;
120
- /**
121
- * Expects the localBlobCache entry for the given localId to be in either localOnly or uploaded state
122
- * when called. Returns a promise that resolves when the blob is in uploaded or attached state, or else
123
- * rejects on error during upload or if the signal is aborted.
124
- *
125
- * Most of the time this should be expected to exit in uploaded state, but if we are loading from pending
126
- * state we may see an attach message from the client that generated the pending state, which can complete the
127
- * attach while the upload is outstanding.
128
- */
129
- const ensureUploaded = async () => {
130
- const localBlobRecord = this.localBlobCache.get(localId);
131
- if (localBlobRecord?.state === "uploaded") {
132
- // In normal creation flows, the blob will be in localOnly state here. But in the case of loading
133
- // with pending state we can call it with an uploaded-but-not-attached blob. Start the upload
134
- // flow only if it's localOnly.
135
- return;
136
- }
137
- (0, internal_2.assert)(localBlobRecord?.state === "localOnly", 0xc81 /* Attempting to upload from unexpected state */);
138
- this.localBlobCache.set(localId, { state: "uploading", blob });
139
- await new Promise((resolve, reject) => {
140
- // If we eventually have driver-level support for abort, then this can simplify a bit as we won't
141
- // need to track upload completion and abort separately. Until then, we need to handle the case that
142
- // the upload continues and settles after becoming irrelevant due to signal abort or blob attach.
143
- let uploadHasBecomeIrrelevant = false;
144
- const onSignalAbort = () => {
145
- removeListeners();
146
- uploadHasBecomeIrrelevant = true;
147
- this.localBlobCache.delete(localId);
148
- this.pendingBlobsWithAttachedHandles.delete(localId);
149
- reject(createAbortError());
150
- };
151
- const onProcessedBlobAttach = (_localId, _storageId) => {
152
- if (_localId === localId) {
153
- removeListeners();
154
- uploadHasBecomeIrrelevant = true;
155
- resolve();
156
- }
157
- };
158
- const removeListeners = () => {
159
- this.internalEvents.off("processedBlobAttach", onProcessedBlobAttach);
160
- signal?.removeEventListener("abort", onSignalAbort);
161
- };
162
- this.internalEvents.on("processedBlobAttach", onProcessedBlobAttach);
163
- signal?.addEventListener("abort", onSignalAbort);
164
- this.storage
165
- .createBlob(blob)
166
- .then((createBlobResponse) => {
167
- if (!uploadHasBecomeIrrelevant) {
168
- removeListeners();
169
- this.localBlobCache.set(localId, {
170
- state: "uploaded",
171
- blob,
172
- storageId: createBlobResponse.id,
173
- uploadTime: Date.now(),
174
- minTTLInSeconds: createBlobResponse.minTTLInSeconds,
175
- });
176
- resolve();
177
- }
178
- })
179
- .catch((error) => {
180
- if (!uploadHasBecomeIrrelevant) {
181
- removeListeners();
182
- // If the storage call errors, we can't recover. Reject to throw back to the caller.
183
- this.localBlobCache.delete(localId);
184
- this.pendingBlobsWithAttachedHandles.delete(localId);
185
- // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
186
- reject(error);
187
- }
188
- });
189
- });
190
- };
191
- /**
192
- * Expects the localBlobCache entry for the given localId to be in uploaded or attached state when called.
193
- * Returns a promise that resolves to true if the blob is successfully attached, or false if it cannot be
194
- * attached and the upload flow needs to be restarted from the top (currently only if the TTL expires before
195
- * attach can be completed). In the latter case, the localBlobRecord will also be reset to localOnly state.
196
- * The promise rejects if the signal is aborted.
197
- */
198
- const tryAttach = async () => {
199
- const localBlobRecord = this.localBlobCache.get(localId);
200
- if (localBlobRecord?.state === "attached") {
201
- // In normal creation flows, the blob will be in uploaded state here. But if we are loading from pending
202
- // state and see an attach message from the client that generated the pending state, we may have reached
203
- // attached state in the middle of the upload attempt. In that case there's no more work to do and we
204
- // can just return.
205
- return true;
206
- }
207
- (0, internal_2.assert)(localBlobRecord?.state === "uploaded", 0xc82 /* Attempting to attach from unexpected state */);
208
- // If we just uploaded the blob TTL really shouldn't be expired at this location. But if we loaded from
209
- // pending state, the upload may have happened some time far in the past and could be expired here.
210
- if (isTTLTooCloseToExpiry(localBlobRecord)) {
211
- // If the TTL is expired, we assume it's gone from the storage and so is effectively localOnly again.
212
- // Then when we re-enter the loop, we'll re-upload it.
213
- this.localBlobCache.set(localId, { state: "localOnly", blob });
214
- // Emitting here isn't really necessary since the only listener would be attached below. Including here
215
- // for completeness though, in case we add other listeners in the future.
216
- this.internalEvents.emit("blobExpired", localId);
217
- return false;
218
- }
219
- else {
220
- this.localBlobCache.set(localId, {
221
- ...localBlobRecord,
222
- state: "attaching",
223
- });
224
- // Send and await a blob attach op. This serves two purposes:
225
- // 1. If its a new blob, i.e., it isn't de-duped, the server will keep the blob alive if it sees this op
226
- // until its storage ID is added to the next summary.
227
- // 2. It will create a local ID to storage ID mapping in all clients which is needed to retrieve the
228
- // blob from the server via the storage ID.
229
- return new Promise((resolve, reject) => {
230
- const onProcessedBlobAttach = (_localId, _storageId) => {
231
- if (_localId === localId) {
232
- removeListeners();
233
- resolve(true);
234
- }
235
- };
236
- // Although we already checked for TTL expiry above, the message we're about to send may later be asked
237
- // to resubmit. Before we resubmit, we check again for TTL expiry - this listener is how we learn if
238
- // we discovered expiry in the resubmit flow.
239
- const onBlobExpired = (_localId) => {
240
- if (_localId === localId) {
241
- removeListeners();
242
- resolve(false);
243
- }
244
- };
245
- const onSignalAbort = () => {
246
- removeListeners();
247
- this.localBlobCache.delete(localId);
248
- this.pendingBlobsWithAttachedHandles.delete(localId);
249
- reject(createAbortError());
250
- };
251
- const removeListeners = () => {
252
- this.internalEvents.off("processedBlobAttach", onProcessedBlobAttach);
253
- this.internalEvents.off("blobExpired", onBlobExpired);
254
- signal?.removeEventListener("abort", onSignalAbort);
255
- };
256
- this.internalEvents.on("processedBlobAttach", onProcessedBlobAttach);
257
- this.internalEvents.on("blobExpired", onBlobExpired);
258
- signal?.addEventListener("abort", onSignalAbort);
259
- this.sendBlobAttachMessage(localId, localBlobRecord.storageId);
260
- });
261
- }
262
- };
263
- let attachCompleted = false;
264
- while (!attachCompleted) {
265
- await ensureUploaded();
266
- attachCompleted = await tryAttach();
267
- // If something stopped the attach from completing successfully (currently just TTL expiry),
268
- // we expect that the blob was already updated to reflect the updated state (i.e. back to localOnly)
269
- // and we'll try the loop again from the top.
270
- }
271
- // When the blob successfully attaches, the localBlobRecord will have been updated to attached state
272
- // at the time we processed the op, so there's nothing else to do here.
273
- };
274
- /**
275
- * Called in detached state just prior to attaching, this will update the redirect table by
276
- * converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
277
- * The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
278
- * @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
279
- */
280
- this.patchRedirectTable = (detachedStorageTable) => {
281
- (0, internal_2.assert)(this.runtime.attachState === internal_1.AttachState.Detached, 0x252 /* "redirect table can only be set in detached container" */);
282
- // The values of the redirect table are the pseudo storage IDs, which are the keys of the
283
- // detachedStorageTable. We expect to have a many:1 mapping from local IDs to pseudo
284
- // storage IDs (many in the case that the storage dedupes the blob).
285
- (0, internal_2.assert)(new Set(this.redirectTable.values()).size === detachedStorageTable.size, 0x391 /* Redirect table size must match BlobManager's local ID count */);
286
- // Taking a snapshot of the redirect table entries before iterating, because
287
- // we will be adding identity mappings to the the redirect table as we iterate
288
- // and we don't want to include those in the iteration.
289
- const redirectTableEntries = [...this.redirectTable.entries()];
290
- for (const [localId, detachedStorageId] of redirectTableEntries) {
291
- const newStorageId = detachedStorageTable.get(detachedStorageId);
292
- (0, internal_2.assert)(newStorageId !== undefined, 0xc53 /* Couldn't find a matching storage ID */);
293
- this.redirectTable.set(localId, newStorageId);
294
- // set identity (id -> id) entry
295
- this.redirectTable.set(newStorageId, newStorageId);
296
- }
297
- };
298
- /**
299
- * Upload and attach any pending blobs that the BlobManager was loaded with that have not already
300
- * been attached in the meantime.
301
- * @returns A promise that resolves when all the uploads and attaches have completed, or rejects
302
- * if any of them fail.
303
- */
304
- this.sharePendingBlobs = async () => {
305
- const localIdsToUpload = [...this.pendingOnlyLocalIds];
306
- this.pendingOnlyLocalIds.clear();
307
- await Promise.all(localIdsToUpload.map(async (localId) => this.uploadAndAttach(localId)));
308
- };
309
136
  const { routeContext, blobManagerLoadInfo, storage, sendBlobAttachMessage, blobRequested, isBlobDeleted, runtime, pendingBlobs, createBlobPayloadPending, } = props;
310
137
  this.routeContext = routeContext;
311
138
  this.storage = storage;
@@ -473,6 +300,177 @@ class BlobManager {
473
300
  });
474
301
  return blobHandle;
475
302
  }
303
+ /**
304
+ * Upload and attach the localBlobCache entry for the given localId.
305
+ *
306
+ * Expects the localBlobCache entry for the given localId to be in either localOnly or uploaded state
307
+ * when called. Returns a promise that resolves when the blob completes uploading and attaching, or else
308
+ * rejects if an error is encountered or the signal is aborted.
309
+ */
310
+ uploadAndAttach = async (localId, signal) => {
311
+ if (signal?.aborted === true) {
312
+ this.localBlobCache.delete(localId);
313
+ this.pendingBlobsWithAttachedHandles.delete(localId);
314
+ throw createAbortError();
315
+ }
316
+ const localBlobRecordInitial = this.localBlobCache.get(localId);
317
+ (0, internal_2.assert)(localBlobRecordInitial?.state === "localOnly" ||
318
+ localBlobRecordInitial?.state === "uploaded", 0xc80 /* Expect uploadAndAttach to be called with either localOnly or uploaded state */);
319
+ const { blob } = localBlobRecordInitial;
320
+ /**
321
+ * Expects the localBlobCache entry for the given localId to be in either localOnly or uploaded state
322
+ * when called. Returns a promise that resolves when the blob is in uploaded or attached state, or else
323
+ * rejects on error during upload or if the signal is aborted.
324
+ *
325
+ * Most of the time this should be expected to exit in uploaded state, but if we are loading from pending
326
+ * state we may see an attach message from the client that generated the pending state, which can complete the
327
+ * attach while the upload is outstanding.
328
+ */
329
+ const ensureUploaded = async () => {
330
+ const localBlobRecord = this.localBlobCache.get(localId);
331
+ if (localBlobRecord?.state === "uploaded") {
332
+ // In normal creation flows, the blob will be in localOnly state here. But in the case of loading
333
+ // with pending state we can call it with an uploaded-but-not-attached blob. Start the upload
334
+ // flow only if it's localOnly.
335
+ return;
336
+ }
337
+ (0, internal_2.assert)(localBlobRecord?.state === "localOnly", 0xc81 /* Attempting to upload from unexpected state */);
338
+ this.localBlobCache.set(localId, { state: "uploading", blob });
339
+ await new Promise((resolve, reject) => {
340
+ // If we eventually have driver-level support for abort, then this can simplify a bit as we won't
341
+ // need to track upload completion and abort separately. Until then, we need to handle the case that
342
+ // the upload continues and settles after becoming irrelevant due to signal abort or blob attach.
343
+ let uploadHasBecomeIrrelevant = false;
344
+ const onSignalAbort = () => {
345
+ removeListeners();
346
+ uploadHasBecomeIrrelevant = true;
347
+ this.localBlobCache.delete(localId);
348
+ this.pendingBlobsWithAttachedHandles.delete(localId);
349
+ reject(createAbortError());
350
+ };
351
+ const onProcessedBlobAttach = (_localId, _storageId) => {
352
+ if (_localId === localId) {
353
+ removeListeners();
354
+ uploadHasBecomeIrrelevant = true;
355
+ resolve();
356
+ }
357
+ };
358
+ const removeListeners = () => {
359
+ this.internalEvents.off("processedBlobAttach", onProcessedBlobAttach);
360
+ signal?.removeEventListener("abort", onSignalAbort);
361
+ };
362
+ this.internalEvents.on("processedBlobAttach", onProcessedBlobAttach);
363
+ signal?.addEventListener("abort", onSignalAbort);
364
+ this.storage
365
+ .createBlob(blob)
366
+ .then((createBlobResponse) => {
367
+ if (!uploadHasBecomeIrrelevant) {
368
+ removeListeners();
369
+ this.localBlobCache.set(localId, {
370
+ state: "uploaded",
371
+ blob,
372
+ storageId: createBlobResponse.id,
373
+ uploadTime: Date.now(),
374
+ minTTLInSeconds: createBlobResponse.minTTLInSeconds,
375
+ });
376
+ resolve();
377
+ }
378
+ })
379
+ .catch((error) => {
380
+ if (!uploadHasBecomeIrrelevant) {
381
+ removeListeners();
382
+ // If the storage call errors, we can't recover. Reject to throw back to the caller.
383
+ this.localBlobCache.delete(localId);
384
+ this.pendingBlobsWithAttachedHandles.delete(localId);
385
+ // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
386
+ reject(error);
387
+ }
388
+ });
389
+ });
390
+ };
391
+ /**
392
+ * Expects the localBlobCache entry for the given localId to be in uploaded or attached state when called.
393
+ * Returns a promise that resolves to true if the blob is successfully attached, or false if it cannot be
394
+ * attached and the upload flow needs to be restarted from the top (currently only if the TTL expires before
395
+ * attach can be completed). In the latter case, the localBlobRecord will also be reset to localOnly state.
396
+ * The promise rejects if the signal is aborted.
397
+ */
398
+ const tryAttach = async () => {
399
+ const localBlobRecord = this.localBlobCache.get(localId);
400
+ if (localBlobRecord?.state === "attached") {
401
+ // In normal creation flows, the blob will be in uploaded state here. But if we are loading from pending
402
+ // state and see an attach message from the client that generated the pending state, we may have reached
403
+ // attached state in the middle of the upload attempt. In that case there's no more work to do and we
404
+ // can just return.
405
+ return true;
406
+ }
407
+ (0, internal_2.assert)(localBlobRecord?.state === "uploaded", 0xc82 /* Attempting to attach from unexpected state */);
408
+ // If we just uploaded the blob TTL really shouldn't be expired at this location. But if we loaded from
409
+ // pending state, the upload may have happened some time far in the past and could be expired here.
410
+ if (isTTLTooCloseToExpiry(localBlobRecord)) {
411
+ // If the TTL is expired, we assume it's gone from the storage and so is effectively localOnly again.
412
+ // Then when we re-enter the loop, we'll re-upload it.
413
+ this.localBlobCache.set(localId, { state: "localOnly", blob });
414
+ // Emitting here isn't really necessary since the only listener would be attached below. Including here
415
+ // for completeness though, in case we add other listeners in the future.
416
+ this.internalEvents.emit("blobExpired", localId);
417
+ return false;
418
+ }
419
+ else {
420
+ this.localBlobCache.set(localId, {
421
+ ...localBlobRecord,
422
+ state: "attaching",
423
+ });
424
+ // Send and await a blob attach op. This serves two purposes:
425
+ // 1. If its a new blob, i.e., it isn't de-duped, the server will keep the blob alive if it sees this op
426
+ // until its storage ID is added to the next summary.
427
+ // 2. It will create a local ID to storage ID mapping in all clients which is needed to retrieve the
428
+ // blob from the server via the storage ID.
429
+ return new Promise((resolve, reject) => {
430
+ const onProcessedBlobAttach = (_localId, _storageId) => {
431
+ if (_localId === localId) {
432
+ removeListeners();
433
+ resolve(true);
434
+ }
435
+ };
436
+ // Although we already checked for TTL expiry above, the message we're about to send may later be asked
437
+ // to resubmit. Before we resubmit, we check again for TTL expiry - this listener is how we learn if
438
+ // we discovered expiry in the resubmit flow.
439
+ const onBlobExpired = (_localId) => {
440
+ if (_localId === localId) {
441
+ removeListeners();
442
+ resolve(false);
443
+ }
444
+ };
445
+ const onSignalAbort = () => {
446
+ removeListeners();
447
+ this.localBlobCache.delete(localId);
448
+ this.pendingBlobsWithAttachedHandles.delete(localId);
449
+ reject(createAbortError());
450
+ };
451
+ const removeListeners = () => {
452
+ this.internalEvents.off("processedBlobAttach", onProcessedBlobAttach);
453
+ this.internalEvents.off("blobExpired", onBlobExpired);
454
+ signal?.removeEventListener("abort", onSignalAbort);
455
+ };
456
+ this.internalEvents.on("processedBlobAttach", onProcessedBlobAttach);
457
+ this.internalEvents.on("blobExpired", onBlobExpired);
458
+ signal?.addEventListener("abort", onSignalAbort);
459
+ this.sendBlobAttachMessage(localId, localBlobRecord.storageId);
460
+ });
461
+ }
462
+ };
463
+ let attachCompleted = false;
464
+ while (!attachCompleted) {
465
+ await ensureUploaded();
466
+ attachCompleted = await tryAttach();
467
+ // If something stopped the attach from completing successfully (currently just TTL expiry),
468
+ // we expect that the blob was already updated to reflect the updated state (i.e. back to localOnly)
469
+ // and we'll try the loop again from the top.
470
+ }
471
+ // When the blob successfully attaches, the localBlobRecord will have been updated to attached state
472
+ // at the time we processed the op, so there's nothing else to do here.
473
+ };
476
474
  /**
477
475
  * Resubmit a BlobAttach op. Used to add storage IDs to messages that were
478
476
  * submitted to runtime while disconnected.
@@ -620,6 +618,41 @@ class BlobManager {
620
618
  }, error);
621
619
  throw error;
622
620
  }
621
+ /**
622
+ * Called in detached state just prior to attaching, this will update the redirect table by
623
+ * converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
624
+ * The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
625
+ * @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
626
+ */
627
+ patchRedirectTable = (detachedStorageTable) => {
628
+ (0, internal_2.assert)(this.runtime.attachState === internal_1.AttachState.Detached, 0x252 /* "redirect table can only be set in detached container" */);
629
+ // The values of the redirect table are the pseudo storage IDs, which are the keys of the
630
+ // detachedStorageTable. We expect to have a many:1 mapping from local IDs to pseudo
631
+ // storage IDs (many in the case that the storage dedupes the blob).
632
+ (0, internal_2.assert)(new Set(this.redirectTable.values()).size === detachedStorageTable.size, 0x391 /* Redirect table size must match BlobManager's local ID count */);
633
+ // Taking a snapshot of the redirect table entries before iterating, because
634
+ // we will be adding identity mappings to the the redirect table as we iterate
635
+ // and we don't want to include those in the iteration.
636
+ const redirectTableEntries = [...this.redirectTable.entries()];
637
+ for (const [localId, detachedStorageId] of redirectTableEntries) {
638
+ const newStorageId = detachedStorageTable.get(detachedStorageId);
639
+ (0, internal_2.assert)(newStorageId !== undefined, 0xc53 /* Couldn't find a matching storage ID */);
640
+ this.redirectTable.set(localId, newStorageId);
641
+ // set identity (id -> id) entry
642
+ this.redirectTable.set(newStorageId, newStorageId);
643
+ }
644
+ };
645
+ /**
646
+ * Upload and attach any pending blobs that the BlobManager was loaded with that have not already
647
+ * been attached in the meantime.
648
+ * @returns A promise that resolves when all the uploads and attaches have completed, or rejects
649
+ * if any of them fail.
650
+ */
651
+ sharePendingBlobs = async () => {
652
+ const localIdsToUpload = [...this.pendingOnlyLocalIds];
653
+ this.pendingOnlyLocalIds.clear();
654
+ await Promise.all(localIdsToUpload.map(async (localId) => this.uploadAndAttach(localId)));
655
+ };
623
656
  /**
624
657
  * To be used in getPendingLocalState flow. Get a serializable record of the blobs that are
625
658
  * pending upload and/or their BlobAttach op, which can be given to a new BlobManager to