@fluidframework/container-loader 2.0.2 → 2.1.0-276326

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 (227) hide show
  1. package/.eslintrc.cjs +2 -5
  2. package/api-extractor/api-extractor.legacy.json +4 -0
  3. package/api-report/container-loader.beta.api.md +0 -27
  4. package/api-report/{container-loader.alpha.api.md → container-loader.legacy.alpha.api.md} +0 -27
  5. package/api-report/container-loader.public.api.md +0 -27
  6. package/dist/attachment.d.ts +2 -1
  7. package/dist/attachment.d.ts.map +1 -1
  8. package/dist/attachment.js.map +1 -1
  9. package/dist/audience.d.ts.map +1 -1
  10. package/dist/audience.js +4 -4
  11. package/dist/audience.js.map +1 -1
  12. package/dist/catchUpMonitor.d.ts +15 -4
  13. package/dist/catchUpMonitor.d.ts.map +1 -1
  14. package/dist/catchUpMonitor.js +12 -3
  15. package/dist/catchUpMonitor.js.map +1 -1
  16. package/dist/connectionManager.d.ts +24 -8
  17. package/dist/connectionManager.d.ts.map +1 -1
  18. package/dist/connectionManager.js +36 -23
  19. package/dist/connectionManager.js.map +1 -1
  20. package/dist/connectionStateHandler.d.ts +30 -20
  21. package/dist/connectionStateHandler.d.ts.map +1 -1
  22. package/dist/connectionStateHandler.js +15 -11
  23. package/dist/connectionStateHandler.js.map +1 -1
  24. package/dist/container.d.ts +7 -2
  25. package/dist/container.d.ts.map +1 -1
  26. package/dist/container.js +45 -28
  27. package/dist/container.js.map +1 -1
  28. package/dist/containerContext.d.ts +8 -4
  29. package/dist/containerContext.d.ts.map +1 -1
  30. package/dist/containerContext.js +3 -1
  31. package/dist/containerContext.js.map +1 -1
  32. package/dist/containerStorageAdapter.d.ts +1 -1
  33. package/dist/containerStorageAdapter.d.ts.map +1 -1
  34. package/dist/containerStorageAdapter.js +12 -6
  35. package/dist/containerStorageAdapter.js.map +1 -1
  36. package/dist/contracts.d.ts +17 -8
  37. package/dist/contracts.d.ts.map +1 -1
  38. package/dist/contracts.js +4 -2
  39. package/dist/contracts.js.map +1 -1
  40. package/dist/debugLogger.js +3 -3
  41. package/dist/debugLogger.js.map +1 -1
  42. package/dist/deltaManager.d.ts +13 -9
  43. package/dist/deltaManager.d.ts.map +1 -1
  44. package/dist/deltaManager.js +32 -23
  45. package/dist/deltaManager.js.map +1 -1
  46. package/dist/deltaQueue.d.ts +1 -4
  47. package/dist/deltaQueue.d.ts.map +1 -1
  48. package/dist/deltaQueue.js +2 -2
  49. package/dist/deltaQueue.js.map +1 -1
  50. package/dist/disposal.d.ts +1 -1
  51. package/dist/disposal.d.ts.map +1 -1
  52. package/dist/disposal.js.map +1 -1
  53. package/dist/error.d.ts.map +1 -1
  54. package/dist/error.js.map +1 -1
  55. package/dist/legacy.d.ts +1 -1
  56. package/dist/loadPaused.d.ts +2 -2
  57. package/dist/loadPaused.d.ts.map +1 -1
  58. package/dist/loadPaused.js +7 -3
  59. package/dist/loadPaused.js.map +1 -1
  60. package/dist/loader.d.ts +10 -1
  61. package/dist/loader.d.ts.map +1 -1
  62. package/dist/loader.js +11 -1
  63. package/dist/loader.js.map +1 -1
  64. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +2 -1
  65. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  66. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +3 -1
  67. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  68. package/dist/memoryBlobStorage.d.ts.map +1 -1
  69. package/dist/memoryBlobStorage.js +4 -2
  70. package/dist/memoryBlobStorage.js.map +1 -1
  71. package/dist/noopHeuristic.js +1 -1
  72. package/dist/noopHeuristic.js.map +1 -1
  73. package/dist/packageVersion.d.ts +1 -1
  74. package/dist/packageVersion.d.ts.map +1 -1
  75. package/dist/packageVersion.js +1 -1
  76. package/dist/packageVersion.js.map +1 -1
  77. package/dist/protocol/protocol.d.ts +4 -3
  78. package/dist/protocol/protocol.d.ts.map +1 -1
  79. package/dist/protocol/protocol.js +6 -5
  80. package/dist/protocol/protocol.js.map +1 -1
  81. package/dist/protocol/quorum.d.ts +11 -8
  82. package/dist/protocol/quorum.d.ts.map +1 -1
  83. package/dist/protocol/quorum.js +8 -8
  84. package/dist/protocol/quorum.js.map +1 -1
  85. package/dist/protocol.d.ts +2 -0
  86. package/dist/protocol.d.ts.map +1 -1
  87. package/dist/protocol.js +7 -2
  88. package/dist/protocol.js.map +1 -1
  89. package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
  90. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  91. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  92. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  93. package/dist/retriableDocumentStorageService.js +4 -1
  94. package/dist/retriableDocumentStorageService.js.map +1 -1
  95. package/dist/serializedStateManager.d.ts +29 -12
  96. package/dist/serializedStateManager.d.ts.map +1 -1
  97. package/dist/serializedStateManager.js +55 -24
  98. package/dist/serializedStateManager.js.map +1 -1
  99. package/dist/utils.d.ts +4 -2
  100. package/dist/utils.d.ts.map +1 -1
  101. package/dist/utils.js +15 -6
  102. package/dist/utils.js.map +1 -1
  103. package/lib/attachment.d.ts +2 -1
  104. package/lib/attachment.d.ts.map +1 -1
  105. package/lib/attachment.js.map +1 -1
  106. package/lib/audience.d.ts.map +1 -1
  107. package/lib/audience.js +4 -4
  108. package/lib/audience.js.map +1 -1
  109. package/lib/catchUpMonitor.d.ts +15 -4
  110. package/lib/catchUpMonitor.d.ts.map +1 -1
  111. package/lib/catchUpMonitor.js +12 -3
  112. package/lib/catchUpMonitor.js.map +1 -1
  113. package/lib/connectionManager.d.ts +24 -8
  114. package/lib/connectionManager.d.ts.map +1 -1
  115. package/lib/connectionManager.js +36 -23
  116. package/lib/connectionManager.js.map +1 -1
  117. package/lib/connectionStateHandler.d.ts +30 -20
  118. package/lib/connectionStateHandler.d.ts.map +1 -1
  119. package/lib/connectionStateHandler.js +14 -12
  120. package/lib/connectionStateHandler.js.map +1 -1
  121. package/lib/container.d.ts +7 -2
  122. package/lib/container.d.ts.map +1 -1
  123. package/lib/container.js +45 -28
  124. package/lib/container.js.map +1 -1
  125. package/lib/containerContext.d.ts +8 -4
  126. package/lib/containerContext.d.ts.map +1 -1
  127. package/lib/containerContext.js +3 -1
  128. package/lib/containerContext.js.map +1 -1
  129. package/lib/containerStorageAdapter.d.ts +1 -1
  130. package/lib/containerStorageAdapter.d.ts.map +1 -1
  131. package/lib/containerStorageAdapter.js +12 -6
  132. package/lib/containerStorageAdapter.js.map +1 -1
  133. package/lib/contracts.d.ts +17 -8
  134. package/lib/contracts.d.ts.map +1 -1
  135. package/lib/contracts.js +4 -2
  136. package/lib/contracts.js.map +1 -1
  137. package/lib/debugLogger.js +3 -3
  138. package/lib/debugLogger.js.map +1 -1
  139. package/lib/deltaManager.d.ts +13 -9
  140. package/lib/deltaManager.d.ts.map +1 -1
  141. package/lib/deltaManager.js +32 -23
  142. package/lib/deltaManager.js.map +1 -1
  143. package/lib/deltaQueue.d.ts +1 -4
  144. package/lib/deltaQueue.d.ts.map +1 -1
  145. package/lib/deltaQueue.js +2 -2
  146. package/lib/deltaQueue.js.map +1 -1
  147. package/lib/disposal.d.ts +1 -1
  148. package/lib/disposal.d.ts.map +1 -1
  149. package/lib/disposal.js.map +1 -1
  150. package/lib/error.d.ts.map +1 -1
  151. package/lib/error.js.map +1 -1
  152. package/lib/legacy.d.ts +1 -1
  153. package/lib/loadPaused.d.ts +2 -2
  154. package/lib/loadPaused.d.ts.map +1 -1
  155. package/lib/loadPaused.js +8 -4
  156. package/lib/loadPaused.js.map +1 -1
  157. package/lib/loader.d.ts +10 -1
  158. package/lib/loader.d.ts.map +1 -1
  159. package/lib/loader.js +11 -1
  160. package/lib/loader.js.map +1 -1
  161. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +2 -1
  162. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  163. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +3 -1
  164. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  165. package/lib/memoryBlobStorage.d.ts.map +1 -1
  166. package/lib/memoryBlobStorage.js +4 -2
  167. package/lib/memoryBlobStorage.js.map +1 -1
  168. package/lib/noopHeuristic.js +1 -1
  169. package/lib/noopHeuristic.js.map +1 -1
  170. package/lib/packageVersion.d.ts +1 -1
  171. package/lib/packageVersion.d.ts.map +1 -1
  172. package/lib/packageVersion.js +1 -1
  173. package/lib/packageVersion.js.map +1 -1
  174. package/lib/protocol/protocol.d.ts +4 -3
  175. package/lib/protocol/protocol.d.ts.map +1 -1
  176. package/lib/protocol/protocol.js +6 -5
  177. package/lib/protocol/protocol.js.map +1 -1
  178. package/lib/protocol/quorum.d.ts +11 -8
  179. package/lib/protocol/quorum.d.ts.map +1 -1
  180. package/lib/protocol/quorum.js +8 -8
  181. package/lib/protocol/quorum.js.map +1 -1
  182. package/lib/protocol.d.ts +2 -0
  183. package/lib/protocol.d.ts.map +1 -1
  184. package/lib/protocol.js +7 -2
  185. package/lib/protocol.js.map +1 -1
  186. package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
  187. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  188. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  189. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  190. package/lib/retriableDocumentStorageService.js +4 -1
  191. package/lib/retriableDocumentStorageService.js.map +1 -1
  192. package/lib/serializedStateManager.d.ts +29 -12
  193. package/lib/serializedStateManager.d.ts.map +1 -1
  194. package/lib/serializedStateManager.js +56 -25
  195. package/lib/serializedStateManager.js.map +1 -1
  196. package/lib/utils.d.ts +4 -2
  197. package/lib/utils.d.ts.map +1 -1
  198. package/lib/utils.js +16 -7
  199. package/lib/utils.js.map +1 -1
  200. package/package.json +21 -17
  201. package/src/attachment.ts +2 -1
  202. package/src/audience.ts +4 -4
  203. package/src/catchUpMonitor.ts +23 -8
  204. package/src/connectionManager.ts +85 -60
  205. package/src/connectionStateHandler.ts +85 -63
  206. package/src/container.ts +118 -84
  207. package/src/containerContext.ts +5 -3
  208. package/src/containerStorageAdapter.ts +20 -13
  209. package/src/contracts.ts +21 -9
  210. package/src/debugLogger.ts +4 -4
  211. package/src/deltaManager.ts +75 -56
  212. package/src/deltaQueue.ts +16 -10
  213. package/src/disposal.ts +3 -3
  214. package/src/error.ts +2 -1
  215. package/src/loadPaused.ts +16 -8
  216. package/src/loader.ts +20 -2
  217. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +7 -3
  218. package/src/memoryBlobStorage.ts +5 -3
  219. package/src/noopHeuristic.ts +1 -1
  220. package/src/packageVersion.ts +1 -1
  221. package/src/protocol/protocol.ts +12 -11
  222. package/src/protocol/quorum.ts +49 -40
  223. package/src/protocol.ts +12 -4
  224. package/src/protocolTreeDocumentStorageService.ts +3 -2
  225. package/src/retriableDocumentStorageService.ts +6 -3
  226. package/src/serializedStateManager.ts +95 -39
  227. package/src/utils.ts +26 -10
@@ -12,8 +12,9 @@ import type {
12
12
  IEventProvider,
13
13
  IEvent,
14
14
  ITelemetryBaseLogger,
15
+ Tagged,
15
16
  } from "@fluidframework/core-interfaces";
16
- import { assert } from "@fluidframework/core-utils/internal";
17
+ import { Timer, assert } from "@fluidframework/core-utils/internal";
17
18
  import {
18
19
  FetchSource,
19
20
  IDocumentStorageService,
@@ -30,6 +31,7 @@ import {
30
31
  PerformanceEvent,
31
32
  UsageError,
32
33
  createChildMonitoringContext,
34
+ type TelemetryEventPropertyTypeExt,
33
35
  } from "@fluidframework/telemetry-utils/internal";
34
36
 
35
37
  import {
@@ -66,7 +68,9 @@ export interface SnapshotWithBlobs {
66
68
  * @internal
67
69
  */
68
70
  export interface IPendingContainerState extends SnapshotWithBlobs {
69
- /** This container was attached (as opposed to IPendingDetachedContainerState.attached which is false) */
71
+ /**
72
+ * This container was attached (as opposed to IPendingDetachedContainerState.attached which is false)
73
+ */
70
74
  attached: true;
71
75
  /**
72
76
  * Runtime-specific state that will be needed to properly rehydrate
@@ -83,9 +87,13 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
83
87
  * ops at the same sequence number at which they were made.
84
88
  */
85
89
  savedOps: ISequencedDocumentMessage[];
86
- /** The Container's URL in the service, needed to hook up the driver during rehydration */
90
+ /**
91
+ * The Container's URL in the service, needed to hook up the driver during rehydration
92
+ */
87
93
  url: string;
88
- /** If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected. */
94
+ /**
95
+ * If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected.
96
+ */
89
97
  clientId?: string;
90
98
  }
91
99
 
@@ -95,11 +103,17 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
95
103
  * @internal
96
104
  */
97
105
  export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
98
- /** This container was not attached (as opposed to IPendingContainerState.attached which is true) */
106
+ /**
107
+ * This container was not attached (as opposed to IPendingContainerState.attached which is true)
108
+ */
99
109
  attached: false;
100
- /** Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage */
110
+ /**
111
+ * Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage
112
+ */
101
113
  hasAttachmentBlobs: boolean;
102
- /** Used by the memory blob storage to persisted attachment blobs */
114
+ /**
115
+ * Used by the memory blob storage to persisted attachment blobs
116
+ */
103
117
  attachmentBlobs?: string;
104
118
  /**
105
119
  * Runtime-specific state that will be needed to properly rehydrate
@@ -136,8 +150,10 @@ export class SerializedStateManager {
136
150
  private readonly mc: MonitoringContext;
137
151
  private snapshot: ISnapshotInfo | undefined;
138
152
  private latestSnapshot: ISnapshotInfo | undefined;
139
- private refreshSnapshotP: Promise<void> | undefined;
153
+ private _refreshSnapshotP: Promise<number> | undefined;
140
154
  private readonly lastSavedOpSequenceNumber: number = 0;
155
+ private readonly refreshTimer: Timer;
156
+ private readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;
141
157
 
142
158
  /**
143
159
  * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)
@@ -155,12 +171,17 @@ export class SerializedStateManager {
155
171
  containerEvent: IEventProvider<ISerializerEvent>,
156
172
  private readonly containerDirty: () => boolean,
157
173
  private readonly supportGetSnapshotApi: () => boolean,
174
+ snapshotRefreshTimeoutMs?: number,
158
175
  ) {
159
176
  this.mc = createChildMonitoringContext({
160
177
  logger: subLogger,
161
178
  namespace: "serializedStateManager",
162
179
  });
163
180
 
181
+ this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
182
+ this.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>
183
+ this.tryRefreshSnapshot(),
184
+ );
164
185
  // special case handle. Obtaining the last saved op seq num to avoid
165
186
  // refreshing the snapshot before we have processed it. It could cause
166
187
  // a subsequent stashing to have a newer snapshot than allowed.
@@ -169,7 +190,7 @@ export class SerializedStateManager {
169
190
  this.lastSavedOpSequenceNumber =
170
191
  pendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;
171
192
  }
172
- containerEvent.once("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
193
+ containerEvent.on("saved", () => this.updateSnapshotAndProcessedOpsMaybe());
173
194
  }
174
195
 
175
196
  public get offlineLoadEnabled(): boolean {
@@ -178,15 +199,17 @@ export class SerializedStateManager {
178
199
 
179
200
  /**
180
201
  * Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage
202
+ * only intended to be used for testing purposes.
203
+ * @returns The snapshot sequence number associated with the latest fetched snapshot
181
204
  */
182
- public get waitForInitialRefresh(): Promise<void> | undefined {
183
- return this.refreshSnapshotP;
205
+ public get refreshSnapshotP(): Promise<number> | undefined {
206
+ return this._refreshSnapshotP;
184
207
  }
185
208
 
186
209
  /**
187
210
  * Called whenever an incoming op is processed by the Container
188
211
  */
189
- public addProcessedOp(message: ISequencedDocumentMessage) {
212
+ public addProcessedOp(message: ISequencedDocumentMessage): void {
190
213
  if (this.offlineLoadEnabled) {
191
214
  this.processedOps.push(message);
192
215
  this.updateSnapshotAndProcessedOpsMaybe();
@@ -197,13 +220,16 @@ export class SerializedStateManager {
197
220
  * This wraps the basic functionality of fetching the snapshot for this container during Container load.
198
221
  *
199
222
  * If we have pendingLocalState, we get the snapshot from there.
200
- * Otherwise, fetch it from storage (according to specifiedVersion if provided)
223
+ * Otherwise, fetch it from storage (according to specifiedVersion if provided).
201
224
  *
202
- * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage
225
+ * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.
203
226
  * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
204
227
  * @returns The snapshot to boot the container from
205
228
  */
206
- public async fetchSnapshot(specifiedVersion: string | undefined) {
229
+ public async fetchSnapshot(specifiedVersion: string | undefined): Promise<{
230
+ baseSnapshot: ISnapshot | ISnapshotTree;
231
+ version: IVersion | undefined;
232
+ }> {
207
233
  if (this.pendingLocalState === undefined) {
208
234
  const { baseSnapshot, version } = await getSnapshot(
209
235
  this.mc,
@@ -224,6 +250,7 @@ export class SerializedStateManager {
224
250
  snapshotBlobs,
225
251
  snapshotSequenceNumber: attributes.sequenceNumber,
226
252
  };
253
+ this.refreshTimer.start();
227
254
  }
228
255
  return { baseSnapshot, version };
229
256
  } else {
@@ -234,21 +261,7 @@ export class SerializedStateManager {
234
261
  snapshotBlobs,
235
262
  snapshotSequenceNumber: attributes.sequenceNumber,
236
263
  };
237
-
238
- if (
239
- this.refreshSnapshotP === undefined &&
240
- this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true
241
- ) {
242
- // Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
243
- this.refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());
244
- this.refreshSnapshotP.catch((e) => {
245
- this.mc.logger.sendErrorEvent({
246
- eventName: "RefreshLatestSnapshotFailed",
247
- error: e,
248
- });
249
- });
250
- }
251
-
264
+ this.tryRefreshSnapshot();
252
265
  const blobContents = new Map<string, ArrayBuffer>();
253
266
  for (const [id, value] of Object.entries(snapshotBlobs)) {
254
267
  blobContents.set(id, stringToBuffer(value, "utf8"));
@@ -265,13 +278,36 @@ export class SerializedStateManager {
265
278
  }
266
279
  }
267
280
 
281
+ private tryRefreshSnapshot(): void {
282
+ if (
283
+ this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true &&
284
+ this._refreshSnapshotP === undefined &&
285
+ this.latestSnapshot === undefined
286
+ ) {
287
+ // Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
288
+ this._refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());
289
+ this._refreshSnapshotP
290
+ .catch(
291
+ (error: TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>) => {
292
+ this.mc.logger.sendTelemetryEvent({
293
+ eventName: "RefreshLatestSnapshotFailed",
294
+ error,
295
+ });
296
+ },
297
+ )
298
+ .finally(() => {
299
+ this._refreshSnapshotP = undefined;
300
+ });
301
+ }
302
+ }
303
+
268
304
  /**
269
305
  * Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.
270
306
  * Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)
271
307
  *
272
308
  * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)
273
309
  */
274
- private async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<void> {
310
+ private async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<number> {
275
311
  this.latestSnapshot = await getLatestSnapshotInfo(
276
312
  this.mc,
277
313
  this.storageAdapter,
@@ -297,15 +333,16 @@ export class SerializedStateManager {
297
333
  assert(snapshot !== undefined, 0x973 /* Snapshot should exist */);
298
334
  }
299
335
 
300
- this.updateSnapshotAndProcessedOpsMaybe();
336
+ return this.updateSnapshotAndProcessedOpsMaybe();
301
337
  }
302
338
 
303
339
  /**
304
340
  * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
305
341
  */
306
- private updateSnapshotAndProcessedOpsMaybe() {
342
+ private updateSnapshotAndProcessedOpsMaybe(): number {
343
+ const snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;
307
344
  if (
308
- this.latestSnapshot === undefined ||
345
+ snapshotSequenceNumber === undefined ||
309
346
  this.processedOps.length === 0 ||
310
347
  this.processedOps[this.processedOps.length - 1].sequenceNumber <
311
348
  this.lastSavedOpSequenceNumber ||
@@ -313,9 +350,8 @@ export class SerializedStateManager {
313
350
  ) {
314
351
  // can't refresh latest snapshot until we have processed the ops up to it.
315
352
  // Pending state would be behind the latest snapshot.
316
- return;
353
+ return -1;
317
354
  }
318
- const snapshotSequenceNumber = this.latestSnapshot.snapshotSequenceNumber;
319
355
  const firstProcessedOpSequenceNumber = this.processedOps[0].sequenceNumber;
320
356
  const lastProcessedOpSequenceNumber =
321
357
  this.processedOps[this.processedOps.length - 1].sequenceNumber;
@@ -331,12 +367,14 @@ export class SerializedStateManager {
331
367
  stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
332
368
  });
333
369
  this.latestSnapshot = undefined;
370
+ this.refreshTimer.restart();
334
371
  } else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
335
372
  // Snapshot seq num is between the first and last processed op.
336
373
  // Remove the ops that are already part of the snapshot
337
374
  this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
338
375
  this.snapshot = this.latestSnapshot;
339
376
  this.latestSnapshot = undefined;
377
+ this.refreshTimer.restart();
340
378
  this.mc.logger.sendTelemetryEvent({
341
379
  eventName: "SnapshotRefreshed",
342
380
  snapshotSequenceNumber,
@@ -345,6 +383,7 @@ export class SerializedStateManager {
345
383
  this.processedOps.length === 0 ? undefined : this.processedOps[0].sequenceNumber,
346
384
  });
347
385
  }
386
+ return snapshotSequenceNumber;
348
387
  }
349
388
 
350
389
  /**
@@ -353,7 +392,7 @@ export class SerializedStateManager {
353
392
  * base snapshot when attaching.
354
393
  * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)
355
394
  */
356
- public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined) {
395
+ public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void {
357
396
  if (this.offlineLoadEnabled) {
358
397
  assert(
359
398
  this.snapshot === undefined,
@@ -365,12 +404,19 @@ export class SerializedStateManager {
365
404
  ".protocol" in baseSnapshot.trees
366
405
  ? baseSnapshot.trees[".protocol"].blobs.attributes
367
406
  : baseSnapshot.blobs[".attributes"];
407
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
368
408
  const attributes = JSON.parse(snapshotBlobs[attributesHash]);
369
409
  assert(
410
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
370
411
  attributes.sequenceNumber === 0,
371
412
  0x939 /* trying to set a non attachment snapshot */,
372
413
  );
373
- this.snapshot = { ...snapshot, snapshotSequenceNumber: attributes.sequenceNumber };
414
+ this.snapshot = {
415
+ ...snapshot,
416
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
417
+ snapshotSequenceNumber: attributes.sequenceNumber as number,
418
+ };
419
+ this.refreshTimer.start();
374
420
  }
375
421
  }
376
422
 
@@ -450,11 +496,19 @@ export async function getLatestSnapshotInfo(
450
496
  mc.logger,
451
497
  { eventName: "GetLatestSnapshotInfo" },
452
498
  async () => {
499
+ // get the latest non cached snapshot version
500
+ const specifiedVersion: IVersion[] = await storageAdapter.getVersions(
501
+ // eslint-disable-next-line unicorn/no-null
502
+ null,
503
+ 1,
504
+ "getLatestSnapshotInfo",
505
+ FetchSource.noCache,
506
+ );
453
507
  const { baseSnapshot } = await getSnapshot(
454
508
  mc,
455
509
  storageAdapter,
456
510
  supportGetSnapshotApi,
457
- undefined,
511
+ specifiedVersion[0]?.id,
458
512
  );
459
513
 
460
514
  const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
@@ -549,6 +603,8 @@ export async function fetchISnapshotTree(
549
603
  storageAdapter: Pick<IDocumentStorageService, "getSnapshotTree" | "getVersions">,
550
604
  specifiedVersion: string | undefined,
551
605
  ): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {
606
+ // API uses null
607
+ // eslint-disable-next-line unicorn/no-null
552
608
  const versions = await storageAdapter.getVersions(specifiedVersion ?? null, 1);
553
609
  const version = versions[0];
554
610
 
package/src/utils.ts CHANGED
@@ -25,7 +25,11 @@ import {
25
25
  isCombinedAppAndProtocolSummary,
26
26
  readAndParse,
27
27
  } from "@fluidframework/driver-utils/internal";
28
- import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
28
+ import {
29
+ LoggingError,
30
+ UsageError,
31
+ type IFluidErrorBase,
32
+ } from "@fluidframework/telemetry-utils/internal";
29
33
  import { v4 as uuid } from "uuid";
30
34
 
31
35
  import { ISerializableBlobContents } from "./containerStorageAdapter.js";
@@ -47,6 +51,7 @@ export interface ISnapshotTreeWithBlobContents extends ISnapshotTree {
47
51
  * Interface to represent the parsed parts of IResolvedUrl.url to help
48
52
  * in getting info about different parts of the url.
49
53
  * May not be compatible or relevant for any Url Resolver
54
+ * @legacy
50
55
  * @alpha
51
56
  */
52
57
  export interface IParsedUrl {
@@ -76,6 +81,7 @@ export interface IParsedUrl {
76
81
  * with urls of type: protocol://<string>/.../..?<querystring>
77
82
  * @param url - This is the IResolvedUrl.url part of the resolved url.
78
83
  * @returns The IParsedUrl representing the input URL, or undefined if the format was not supported
84
+ * @legacy
79
85
  * @alpha
80
86
  */
81
87
  export function tryParseCompatibleResolvedUrl(url: string): IParsedUrl | undefined {
@@ -150,9 +156,10 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBl
150
156
  blobContents = { ...blobContents, ...innerSnapshot.snapshotBlobs };
151
157
  break;
152
158
  }
153
- case SummaryType.Attachment:
159
+ case SummaryType.Attachment: {
154
160
  treeNode.blobs[key] = summaryObject.id;
155
161
  break;
162
+ }
156
163
  case SummaryType.Blob: {
157
164
  const blobId = uuid();
158
165
  treeNode.blobs[key] = blobId;
@@ -163,11 +170,13 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBl
163
170
  blobContents[blobId] = contentString;
164
171
  break;
165
172
  }
166
- case SummaryType.Handle:
173
+ case SummaryType.Handle: {
167
174
  throw new LoggingError(
168
175
  "No handles should be there in summary in detached container!!",
169
176
  );
177
+ }
170
178
  default: {
179
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
171
180
  unreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);
172
181
  }
173
182
  }
@@ -293,12 +302,13 @@ export const combineSnapshotTreeAndSnapshotBlobs = (
293
302
  };
294
303
 
295
304
  export function isDeltaStreamConnectionForbiddenError(
296
- error: any,
305
+ error: unknown,
297
306
  ): error is DeltaStreamConnectionForbiddenError {
298
307
  return (
299
308
  typeof error === "object" &&
300
309
  error !== null &&
301
- error?.errorType === DriverErrorTypes.deltaStreamConnectionForbidden
310
+ (error as Partial<IFluidErrorBase>)?.errorType ===
311
+ DriverErrorTypes.deltaStreamConnectionForbidden
302
312
  );
303
313
  }
304
314
 
@@ -329,9 +339,12 @@ export function getDetachedContainerStateFromSerializedContainer(
329
339
  serializedContainer: string,
330
340
  ): IPendingDetachedContainerState {
331
341
  const hasBlobsSummaryTree = ".hasAttachmentBlobs";
342
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
332
343
  const parsedContainerState = JSON.parse(serializedContainer);
344
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
333
345
  if (isPendingDetachedContainerState(parsedContainerState)) {
334
346
  return parsedContainerState;
347
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
335
348
  } else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
336
349
  const { baseSnapshot, snapshotBlobs } =
337
350
  getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
@@ -354,16 +367,19 @@ export function getDetachedContainerStateFromSerializedContainer(
354
367
  export function getAttachedContainerStateFromSerializedContainer(
355
368
  serializedContainer: string | undefined,
356
369
  ): IPendingContainerState | undefined {
357
- return serializedContainer !== undefined
358
- ? (JSON.parse(serializedContainer) as IPendingContainerState)
359
- : undefined;
370
+ return serializedContainer === undefined
371
+ ? undefined
372
+ : (JSON.parse(serializedContainer) as IPendingContainerState);
360
373
  }
361
374
 
362
375
  /**
363
376
  * Ensures only a single instance of the provided async function is running.
364
377
  * If there are multiple calls they will all get the same promise to wait on.
365
378
  */
366
- export const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>) => {
379
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
+ export const runSingle = <A extends any[], R>(
381
+ func: (...args: A) => Promise<R>,
382
+ ): ((...args: A) => Promise<R>) => {
367
383
  let running:
368
384
  | {
369
385
  args: A;
@@ -373,7 +389,7 @@ export const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>)
373
389
  // don't mark this function async, so we return the same promise,
374
390
  // rather than one that is wrapped due to async
375
391
  // eslint-disable-next-line @typescript-eslint/promise-function-async
376
- return (...args: A) => {
392
+ return (...args: A): Promise<R> => {
377
393
  if (running !== undefined) {
378
394
  if (!compareArrays(running.args, args)) {
379
395
  return Promise.reject(