@fluidframework/container-loader 2.63.0-359734 → 2.63.0-359962

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 (68) hide show
  1. package/api-report/container-loader.legacy.alpha.api.md +3 -0
  2. package/dist/container.d.ts.map +1 -1
  3. package/dist/container.js +2 -2
  4. package/dist/container.js.map +1 -1
  5. package/dist/containerStorageAdapter.d.ts +2 -2
  6. package/dist/containerStorageAdapter.d.ts.map +1 -1
  7. package/dist/containerStorageAdapter.js +1 -1
  8. package/dist/containerStorageAdapter.js.map +1 -1
  9. package/dist/createAndLoadContainerUtils.js +1 -1
  10. package/dist/createAndLoadContainerUtils.js.map +1 -1
  11. package/dist/frozenServices.d.ts +10 -1
  12. package/dist/frozenServices.d.ts.map +1 -1
  13. package/dist/frozenServices.js +24 -4
  14. package/dist/frozenServices.js.map +1 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/legacyAlpha.d.ts +1 -0
  20. package/dist/packageVersion.d.ts +1 -1
  21. package/dist/packageVersion.js +1 -1
  22. package/dist/packageVersion.js.map +1 -1
  23. package/dist/serializedStateManager.d.ts +8 -4
  24. package/dist/serializedStateManager.d.ts.map +1 -1
  25. package/dist/serializedStateManager.js +74 -76
  26. package/dist/serializedStateManager.js.map +1 -1
  27. package/dist/utils.d.ts +3 -3
  28. package/dist/utils.d.ts.map +1 -1
  29. package/dist/utils.js +2 -2
  30. package/dist/utils.js.map +1 -1
  31. package/lib/container.d.ts.map +1 -1
  32. package/lib/container.js +2 -2
  33. package/lib/container.js.map +1 -1
  34. package/lib/containerStorageAdapter.d.ts +2 -2
  35. package/lib/containerStorageAdapter.d.ts.map +1 -1
  36. package/lib/containerStorageAdapter.js +1 -1
  37. package/lib/containerStorageAdapter.js.map +1 -1
  38. package/lib/createAndLoadContainerUtils.js +2 -2
  39. package/lib/createAndLoadContainerUtils.js.map +1 -1
  40. package/lib/frozenServices.d.ts +10 -1
  41. package/lib/frozenServices.d.ts.map +1 -1
  42. package/lib/frozenServices.js +20 -1
  43. package/lib/frozenServices.js.map +1 -1
  44. package/lib/index.d.ts +1 -0
  45. package/lib/index.d.ts.map +1 -1
  46. package/lib/index.js +1 -0
  47. package/lib/index.js.map +1 -1
  48. package/lib/legacyAlpha.d.ts +1 -0
  49. package/lib/packageVersion.d.ts +1 -1
  50. package/lib/packageVersion.js +1 -1
  51. package/lib/packageVersion.js.map +1 -1
  52. package/lib/serializedStateManager.d.ts +8 -4
  53. package/lib/serializedStateManager.d.ts.map +1 -1
  54. package/lib/serializedStateManager.js +76 -78
  55. package/lib/serializedStateManager.js.map +1 -1
  56. package/lib/utils.d.ts +3 -3
  57. package/lib/utils.d.ts.map +1 -1
  58. package/lib/utils.js +3 -3
  59. package/lib/utils.js.map +1 -1
  60. package/package.json +11 -11
  61. package/src/container.ts +6 -3
  62. package/src/containerStorageAdapter.ts +5 -6
  63. package/src/createAndLoadContainerUtils.ts +2 -2
  64. package/src/frozenServices.ts +28 -2
  65. package/src/index.ts +1 -0
  66. package/src/packageVersion.ts +1 -1
  67. package/src/serializedStateManager.ts +112 -106
  68. package/src/utils.ts +5 -6
@@ -22,7 +22,7 @@ import {
22
22
  type IVersion,
23
23
  type ISequencedDocumentMessage,
24
24
  } from "@fluidframework/driver-definitions/internal";
25
- import { getSnapshotTree } from "@fluidframework/driver-utils/internal";
25
+ import { getSnapshotTree, isInstanceOfISnapshot } from "@fluidframework/driver-utils/internal";
26
26
  import {
27
27
  type MonitoringContext,
28
28
  PerformanceEvent,
@@ -31,12 +31,12 @@ import {
31
31
  } from "@fluidframework/telemetry-utils/internal";
32
32
 
33
33
  import {
34
- type ISerializableBlobContents,
35
34
  getBlobContentsFromTree,
35
+ type ISerializableBlobContents,
36
36
  } from "./containerStorageAdapter.js";
37
37
  import {
38
- convertSnapshotToSnapshotInfo,
39
38
  convertISnapshotToSnapshotWithBlobs,
39
+ convertSnapshotToSnapshotInfo,
40
40
  getDocumentAttributes,
41
41
  } from "./utils.js";
42
42
 
@@ -80,7 +80,7 @@ export interface IPendingContainerState extends SnapshotWithBlobs {
80
80
  /**
81
81
  * Any group snapshots (aka delay-loaded) we've downloaded from the service for this container
82
82
  */
83
- loadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;
83
+ loadedGroupIdSnapshots?: Record<string, SerializedSnapshotInfo>;
84
84
  /**
85
85
  * All ops since base snapshot sequence number up to the latest op
86
86
  * seen when the container was closed. Used to apply stashed (saved pending)
@@ -122,9 +122,14 @@ export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
122
122
  pendingRuntimeState?: unknown;
123
123
  }
124
124
 
125
- export interface ISnapshotInfo extends SnapshotWithBlobs {
125
+ export interface SerializedSnapshotInfo extends SnapshotWithBlobs {
126
+ snapshotSequenceNumber: number;
127
+ }
128
+
129
+ interface ISnapshotInfo {
126
130
  snapshotSequenceNumber: number;
127
131
  snapshotFetchedTime?: number | undefined;
132
+ snapshot: ISnapshot | ISnapshotTree;
128
133
  }
129
134
 
130
135
  export type ISerializedStateManagerDocumentStorageService = Pick<
@@ -169,7 +174,7 @@ class RefreshPromiseTracker {
169
174
  export class SerializedStateManager implements IDisposable {
170
175
  private readonly processedOps: ISequencedDocumentMessage[] = [];
171
176
  private readonly mc: MonitoringContext;
172
- private snapshot: ISnapshotInfo | undefined;
177
+ private snapshotInfo: ISnapshotInfo | undefined;
173
178
  private latestSnapshot: ISnapshotInfo | undefined;
174
179
  private readonly refreshTracker = new RefreshPromiseTracker(
175
180
  // eslint-disable-next-line unicorn/consistent-function-scoping
@@ -211,6 +216,7 @@ export class SerializedStateManager implements IDisposable {
211
216
  this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
212
217
 
213
218
  this.#snapshotRefreshEnabled =
219
+ _offlineLoadEnabled &&
214
220
  (this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") ??
215
221
  this.mc.config.getBoolean("Fluid.Container.enableOfflineFull")) === true;
216
222
 
@@ -270,68 +276,60 @@ export class SerializedStateManager implements IDisposable {
270
276
  specifiedVersion: string | undefined,
271
277
  pendingLocalState: IPendingContainerState | undefined,
272
278
  ): Promise<{
273
- baseSnapshot: ISnapshot | ISnapshotTree;
279
+ snapshot: ISnapshot | ISnapshotTree;
274
280
  version: IVersion | undefined;
275
281
  attributes: IDocumentAttributes;
276
282
  }> {
277
283
  this.verifyNotDisposed();
278
284
  if (pendingLocalState === undefined) {
279
- const { baseSnapshot, version } = await getSnapshot(
285
+ const { snapshot, version } = await getSnapshot(
280
286
  this.mc,
281
287
  this.storageAdapter,
282
288
  this.supportGetSnapshotApi(),
283
289
  specifiedVersion,
284
290
  );
285
- const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
291
+ const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(snapshot);
286
292
  const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshotTree);
287
- // non-interactive clients will not have any pending state we want to save
288
293
  if (this.offlineLoadEnabled) {
289
- // we defer getting the blobs to not impact the container load flow
290
- // only getPendingState depends on the resolution of this promise
291
- this.refreshTracker.setPromise(
292
- getBlobContentsFromTree(baseSnapshot, this.storageAdapter).then((snapshotBlobs) => {
293
- this.snapshot = {
294
- baseSnapshot: baseSnapshotTree,
295
- snapshotBlobs,
296
- snapshotSequenceNumber: attributes.sequenceNumber,
297
- };
298
- this.refreshTimer?.start();
299
- return attributes.sequenceNumber;
300
- }),
301
- );
294
+ this.refreshTimer?.start();
295
+ this.snapshotInfo = {
296
+ snapshot,
297
+ snapshotSequenceNumber: attributes.sequenceNumber,
298
+ };
302
299
  }
303
- return { baseSnapshot, version, attributes };
300
+ return { snapshot, version, attributes };
304
301
  } else {
305
302
  const { baseSnapshot, snapshotBlobs, savedOps } = pendingLocalState;
306
-
307
- // special case handle. Obtaining the last saved op seq num to avoid
308
- // refreshing the snapshot before we have processed it. It could cause
309
- // a subsequent stashing to have a newer snapshot than allowed.
310
- if (savedOps.length > 0) {
311
- const savedOpsSize = savedOps.length;
312
- this.lastSavedOpSequenceNumber = savedOps[savedOpsSize - 1].sequenceNumber;
313
- }
314
-
315
303
  const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
316
- this.snapshot = {
317
- baseSnapshot,
318
- snapshotBlobs,
319
- snapshotSequenceNumber: attributes.sequenceNumber,
320
- };
321
- this.tryRefreshSnapshot();
322
304
  const blobContents = new Map<string, ArrayBuffer>();
323
305
  for (const [id, value] of Object.entries(snapshotBlobs)) {
324
306
  blobContents.set(id, stringToBuffer(value, "utf8"));
325
307
  }
326
- const iSnapshot: ISnapshot = {
327
- sequenceNumber: this.snapshot.snapshotSequenceNumber,
308
+ const snapshot: ISnapshot = {
309
+ sequenceNumber: attributes.sequenceNumber,
328
310
  snapshotTree: baseSnapshot,
329
311
  blobContents,
330
312
  latestSequenceNumber: undefined,
331
313
  ops: [],
332
314
  snapshotFormatV: 1,
333
315
  };
334
- return { baseSnapshot: iSnapshot, version: undefined, attributes };
316
+
317
+ if (this.offlineLoadEnabled) {
318
+ // special case handle. Obtaining the last saved op seq num to avoid
319
+ // refreshing the snapshot before we have processed it. It could cause
320
+ // a subsequent stashing to have a newer snapshot than allowed.
321
+ if (savedOps.length > 0) {
322
+ const savedOpsSize = savedOps.length;
323
+ this.lastSavedOpSequenceNumber = savedOps[savedOpsSize - 1].sequenceNumber;
324
+ }
325
+
326
+ this.snapshotInfo = {
327
+ snapshot,
328
+ snapshotSequenceNumber: attributes.sequenceNumber,
329
+ };
330
+ this.tryRefreshSnapshot();
331
+ }
332
+ return { snapshot, version: undefined, attributes };
335
333
  }
336
334
  }
337
335
 
@@ -415,7 +413,7 @@ export class SerializedStateManager implements IDisposable {
415
413
  snapshotSequenceNumber,
416
414
  firstProcessedOpSequenceNumber,
417
415
  lastProcessedOpSequenceNumber,
418
- stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
416
+ stashedSnapshotSequenceNumber: this.snapshotInfo?.snapshotSequenceNumber,
419
417
  });
420
418
  this.latestSnapshot = undefined;
421
419
  this.refreshTimer?.restart();
@@ -423,7 +421,7 @@ export class SerializedStateManager implements IDisposable {
423
421
  // Snapshot seq num is between the first and last processed op.
424
422
  // Remove the ops that are already part of the snapshot
425
423
  this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
426
- this.snapshot = this.latestSnapshot;
424
+ this.snapshotInfo = this.latestSnapshot;
427
425
  this.latestSnapshot = undefined;
428
426
  this.refreshTimer?.restart();
429
427
  this.mc.logger.sendTelemetryEvent({
@@ -446,18 +444,9 @@ export class SerializedStateManager implements IDisposable {
446
444
  public setInitialSnapshot(snapshot: ISnapshot): void {
447
445
  this.verifyNotDisposed();
448
446
  if (this.offlineLoadEnabled) {
449
- assert(
450
- this.snapshot === undefined,
451
- 0x937 /* inital snapshot should only be defined once */,
452
- );
453
- assert(
454
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
455
- snapshot.sequenceNumber === 0,
456
- 0x939 /* trying to set a non attachment snapshot */,
457
- );
458
- this.snapshot = {
459
- ...convertISnapshotToSnapshotWithBlobs(snapshot),
460
- snapshotSequenceNumber: snapshot.sequenceNumber,
447
+ this.snapshotInfo = {
448
+ snapshot,
449
+ snapshotSequenceNumber: snapshot.sequenceNumber ?? 0,
461
450
  snapshotFetchedTime: Date.now(),
462
451
  };
463
452
  this.refreshTimer?.start();
@@ -474,6 +463,9 @@ export class SerializedStateManager implements IDisposable {
474
463
  resolvedUrl: IResolvedUrl,
475
464
  ): Promise<string> {
476
465
  this.verifyNotDisposed();
466
+ if (!this.offlineLoadEnabled) {
467
+ throw new UsageError("Can't get pending local state unless offline load is enabled");
468
+ }
477
469
 
478
470
  return PerformanceEvent.timedExecAsync(
479
471
  this.mc.logger,
@@ -488,25 +480,14 @@ export class SerializedStateManager implements IDisposable {
488
480
  clientId,
489
481
  },
490
482
  async () => {
491
- if (!this.offlineLoadEnabled) {
492
- throw new UsageError("Can't get pending local state unless offline load is enabled");
493
- }
494
- if (this.snapshot === undefined && this.refreshTracker.hasPromise) {
495
- // we deferred the initial download of the snapshot to not block
496
- // the container load flow, so if it is not resolved
497
- // and we don't have a snapshot, we will wait for the download
498
- // to finish.
499
- await this.refreshTracker.Promise;
500
- }
501
-
502
- assert(this.snapshot !== undefined, 0x8e5 /* no base data */);
483
+ assert(this.snapshotInfo !== undefined, 0x8e5 /* no base data */);
503
484
  const pendingRuntimeState = await runtime.getPendingLocalState({
504
485
  notifyImminentClosure: false,
505
- snapshotSequenceNumber: this.snapshot.snapshotSequenceNumber,
506
- sessionExpiryTimerStarted: this.snapshot.snapshotFetchedTime,
486
+ snapshotSequenceNumber: this.snapshotInfo.snapshotSequenceNumber,
487
+ sessionExpiryTimerStarted: this.snapshotInfo.snapshotFetchedTime,
507
488
  });
508
489
  // This conversion is required because ArrayBufferLike doesn't survive JSON.stringify
509
- const loadedGroupIdSnapshots = {};
490
+ const loadedGroupIdSnapshots: Record<string, SerializedSnapshotInfo> = {};
510
491
  let hasGroupIdSnapshots = false;
511
492
  const groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);
512
493
  if (groupIdSnapshots.length > 0) {
@@ -515,11 +496,20 @@ export class SerializedStateManager implements IDisposable {
515
496
  loadedGroupIdSnapshots[groupId] = convertSnapshotToSnapshotInfo(snapshot);
516
497
  }
517
498
  }
499
+
500
+ const snapshotWithBlobs: SnapshotWithBlobs = isInstanceOfISnapshot(
501
+ this.snapshotInfo.snapshot,
502
+ )
503
+ ? convertISnapshotToSnapshotWithBlobs(this.snapshotInfo.snapshot)
504
+ : await convertSnapshotTreeToSnapshotWithBlobs(
505
+ this.snapshotInfo.snapshot,
506
+ this.storageAdapter,
507
+ );
508
+
518
509
  const pendingState: IPendingContainerState = {
519
510
  attached: true,
520
511
  pendingRuntimeState,
521
- baseSnapshot: this.snapshot.baseSnapshot,
522
- snapshotBlobs: this.snapshot.snapshotBlobs,
512
+ ...snapshotWithBlobs,
523
513
  loadedGroupIdSnapshots: hasGroupIdSnapshots ? loadedGroupIdSnapshots : undefined,
524
514
  savedOps: this.processedOps,
525
515
  url: resolvedUrl.url,
@@ -532,6 +522,17 @@ export class SerializedStateManager implements IDisposable {
532
522
  }
533
523
  }
534
524
 
525
+ async function convertSnapshotTreeToSnapshotWithBlobs(
526
+ snapshot: ISnapshotTree,
527
+ storageAdapter: ISerializedStateManagerDocumentStorageService,
528
+ ): Promise<SnapshotWithBlobs> {
529
+ const snapshotBlobs = await getBlobContentsFromTree(snapshot, storageAdapter);
530
+ return {
531
+ baseSnapshot: snapshot,
532
+ snapshotBlobs,
533
+ };
534
+ }
535
+
535
536
  /**
536
537
  * Retrieves the most recent snapshot and returns its info.
537
538
  *
@@ -545,41 +546,46 @@ export async function getLatestSnapshotInfo(
545
546
  storageAdapter: ISerializedStateManagerDocumentStorageService,
546
547
  supportGetSnapshotApi: boolean,
547
548
  ): Promise<ISnapshotInfo | undefined> {
548
- return PerformanceEvent.timedExecAsync(
549
+ return PerformanceEvent.timedExecAsync<ISnapshotInfo | undefined>(
549
550
  mc.logger,
550
551
  { eventName: "GetLatestSnapshotInfo" },
551
- async () => {
552
- // get the latest non cached snapshot version
553
- const specifiedVersion: IVersion[] = await storageAdapter.getVersions(
554
- // eslint-disable-next-line unicorn/no-null
555
- null,
556
- 1,
557
- "getLatestSnapshotInfo",
558
- FetchSource.noCache,
559
- );
560
- const { baseSnapshot } = await getSnapshot(
561
- mc,
562
- storageAdapter,
563
- supportGetSnapshotApi,
564
- specifiedVersion[0]?.id,
565
- );
552
+ async (event) => {
553
+ try {
554
+ // get the latest non cached snapshot version
555
+ const specifiedVersion: IVersion[] = await storageAdapter.getVersions(
556
+ // eslint-disable-next-line unicorn/no-null
557
+ null,
558
+ 1,
559
+ "getLatestSnapshotInfo",
560
+ FetchSource.noCache,
561
+ );
562
+ const { snapshot: baseSnapshot } = await getSnapshot(
563
+ mc,
564
+ storageAdapter,
565
+ supportGetSnapshotApi,
566
+ specifiedVersion[0]?.id,
567
+ );
566
568
 
567
- const baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);
568
- const snapshotFetchedTime = Date.now();
569
- const snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, storageAdapter);
570
- const attributes: IDocumentAttributes = await getDocumentAttributes(
571
- storageAdapter,
572
- baseSnapshotTree,
573
- );
574
- const snapshotSequenceNumber = attributes.sequenceNumber;
575
- return {
576
- baseSnapshot: baseSnapshotTree,
577
- snapshotBlobs,
578
- snapshotSequenceNumber,
579
- snapshotFetchedTime,
580
- };
569
+ const { sequenceNumber, snapshotTree } = isInstanceOfISnapshot(baseSnapshot)
570
+ ? baseSnapshot
571
+ : { snapshotTree: baseSnapshot, sequenceNumber: undefined };
572
+
573
+ const snapshotSequenceNumber: number =
574
+ sequenceNumber ??
575
+ (await getDocumentAttributes(storageAdapter, snapshotTree).then(
576
+ (a) => a.sequenceNumber,
577
+ ));
578
+ return {
579
+ snapshot: baseSnapshot,
580
+ snapshotSequenceNumber,
581
+ snapshotFetchedTime: Date.now(),
582
+ };
583
+ } catch (error) {
584
+ event.cancel(undefined, error);
585
+ }
586
+ return undefined;
581
587
  },
582
- ).catch(() => undefined);
588
+ );
583
589
  }
584
590
 
585
591
  /**
@@ -599,12 +605,12 @@ async function getSnapshot(
599
605
  >,
600
606
  supportGetSnapshotApi: boolean,
601
607
  specifiedVersion: string | undefined,
602
- ): Promise<{ baseSnapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {
608
+ ): Promise<{ snapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {
603
609
  const { snapshot, version } = supportGetSnapshotApi
604
610
  ? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
605
611
  : await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
606
612
  assert(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);
607
- return { baseSnapshot: snapshot, version };
613
+ return { snapshot, version };
608
614
  }
609
615
 
610
616
  /**
package/src/utils.ts CHANGED
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import {
7
- Uint8ArrayToArrayBuffer,
8
7
  bufferToString,
9
8
  stringToBuffer,
9
+ Uint8ArrayToArrayBuffer,
10
10
  } from "@fluid-internal/client-utils";
11
11
  import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils/internal";
12
12
  import { type ISummaryTree, SummaryType } from "@fluidframework/driver-definitions";
@@ -34,7 +34,7 @@ import type { ISerializableBlobContents } from "./containerStorageAdapter.js";
34
34
  import type {
35
35
  IPendingContainerState,
36
36
  IPendingDetachedContainerState,
37
- ISnapshotInfo,
37
+ SerializedSnapshotInfo,
38
38
  SnapshotWithBlobs,
39
39
  } from "./serializedStateManager.js";
40
40
 
@@ -195,7 +195,7 @@ function convertSummaryToISnapshot(
195
195
  * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown
196
196
  * @param snapshot - ISnapshot
197
197
  */
198
- export function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): ISnapshotInfo {
198
+ export function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): SerializedSnapshotInfo {
199
199
  assert(
200
200
  snapshot.sequenceNumber !== undefined,
201
201
  0x93a /* Snapshot sequence number is missing */,
@@ -219,8 +219,7 @@ export function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): ISnapshotInf
219
219
  * @param snapshot - ISnapshot
220
220
  */
221
221
  export function convertSnapshotInfoToSnapshot(
222
- snapshotInfo: ISnapshotInfo,
223
- snapshotSequenceNumber: number,
222
+ snapshotInfo: SerializedSnapshotInfo,
224
223
  ): ISnapshot {
225
224
  const blobContents = new Map<string, ArrayBuffer>();
226
225
  for (const [blobId, serializedContent] of Object.entries(snapshotInfo.snapshotBlobs)) {
@@ -230,7 +229,7 @@ export function convertSnapshotInfoToSnapshot(
230
229
  snapshotTree: snapshotInfo.baseSnapshot,
231
230
  blobContents,
232
231
  ops: [],
233
- sequenceNumber: snapshotSequenceNumber,
232
+ sequenceNumber: snapshotInfo.snapshotSequenceNumber,
234
233
  latestSequenceNumber: undefined,
235
234
  snapshotFormatV: 1,
236
235
  };