@fluidframework/container-loader 2.0.0-dev-rc.3.0.0.250606 → 2.0.0-dev-rc.3.0.0.254274

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 (220) hide show
  1. package/api-report/container-loader.api.md +11 -11
  2. package/dist/attachment.d.ts +2 -2
  3. package/dist/attachment.d.ts.map +1 -1
  4. package/dist/attachment.js +3 -3
  5. package/dist/attachment.js.map +1 -1
  6. package/dist/audience.d.ts +1 -1
  7. package/dist/audience.d.ts.map +1 -1
  8. package/dist/audience.js +2 -2
  9. package/dist/audience.js.map +1 -1
  10. package/dist/catchUpMonitor.js +2 -2
  11. package/dist/catchUpMonitor.js.map +1 -1
  12. package/dist/connectionManager.d.ts +1 -1
  13. package/dist/connectionManager.d.ts.map +1 -1
  14. package/dist/connectionManager.js +41 -41
  15. package/dist/connectionManager.js.map +1 -1
  16. package/dist/connectionStateHandler.d.ts.map +1 -1
  17. package/dist/connectionStateHandler.js +27 -27
  18. package/dist/connectionStateHandler.js.map +1 -1
  19. package/dist/container-loader-alpha.d.ts +11 -11
  20. package/dist/container-loader-beta.d.ts +11 -11
  21. package/dist/container-loader-public.d.ts +11 -11
  22. package/dist/container-loader-untrimmed.d.ts +11 -11
  23. package/dist/container.d.ts +5 -3
  24. package/dist/container.d.ts.map +1 -1
  25. package/dist/container.js +83 -81
  26. package/dist/container.js.map +1 -1
  27. package/dist/containerContext.d.ts +5 -3
  28. package/dist/containerContext.d.ts.map +1 -1
  29. package/dist/containerContext.js.map +1 -1
  30. package/dist/containerStorageAdapter.d.ts +2 -2
  31. package/dist/containerStorageAdapter.d.ts.map +1 -1
  32. package/dist/containerStorageAdapter.js +6 -6
  33. package/dist/containerStorageAdapter.js.map +1 -1
  34. package/dist/contracts.d.ts +3 -2
  35. package/dist/contracts.d.ts.map +1 -1
  36. package/dist/contracts.js +2 -2
  37. package/dist/contracts.js.map +1 -1
  38. package/dist/debugLogger.d.ts +2 -1
  39. package/dist/debugLogger.d.ts.map +1 -1
  40. package/dist/debugLogger.js +4 -4
  41. package/dist/debugLogger.js.map +1 -1
  42. package/dist/deltaManager.d.ts +7 -3
  43. package/dist/deltaManager.d.ts.map +1 -1
  44. package/dist/deltaManager.js +52 -49
  45. package/dist/deltaManager.js.map +1 -1
  46. package/dist/deltaQueue.js +5 -5
  47. package/dist/deltaQueue.js.map +1 -1
  48. package/dist/error.d.ts +3 -2
  49. package/dist/error.d.ts.map +1 -1
  50. package/dist/error.js +5 -5
  51. package/dist/error.js.map +1 -1
  52. package/dist/loader.d.ts +2 -2
  53. package/dist/loader.d.ts.map +1 -1
  54. package/dist/loader.js +21 -21
  55. package/dist/loader.js.map +1 -1
  56. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +1 -1
  57. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  58. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +2 -2
  59. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  60. package/dist/noopHeuristic.js +6 -6
  61. package/dist/noopHeuristic.js.map +1 -1
  62. package/dist/packageVersion.d.ts +1 -1
  63. package/dist/packageVersion.js +1 -1
  64. package/dist/packageVersion.js.map +1 -1
  65. package/dist/protocol.d.ts +1 -1
  66. package/dist/protocol.d.ts.map +1 -1
  67. package/dist/protocol.js +2 -2
  68. package/dist/protocol.js.map +1 -1
  69. package/dist/protocolTreeDocumentStorageService.d.ts +4 -4
  70. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  71. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  72. package/dist/quorum.d.ts +1 -1
  73. package/dist/quorum.d.ts.map +1 -1
  74. package/dist/quorum.js.map +1 -1
  75. package/dist/retriableDocumentStorageService.d.ts +1 -1
  76. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  77. package/dist/retriableDocumentStorageService.js +7 -7
  78. package/dist/retriableDocumentStorageService.js.map +1 -1
  79. package/dist/serializedStateManager.d.ts +40 -9
  80. package/dist/serializedStateManager.d.ts.map +1 -1
  81. package/dist/serializedStateManager.js +133 -24
  82. package/dist/serializedStateManager.js.map +1 -1
  83. package/dist/utils.d.ts +19 -3
  84. package/dist/utils.d.ts.map +1 -1
  85. package/dist/utils.js +57 -15
  86. package/dist/utils.js.map +1 -1
  87. package/lib/attachment.d.ts +2 -2
  88. package/lib/attachment.d.ts.map +1 -1
  89. package/lib/attachment.js +1 -1
  90. package/lib/attachment.js.map +1 -1
  91. package/lib/audience.d.ts +1 -1
  92. package/lib/audience.d.ts.map +1 -1
  93. package/lib/audience.js +1 -1
  94. package/lib/audience.js.map +1 -1
  95. package/lib/catchUpMonitor.js +1 -1
  96. package/lib/catchUpMonitor.js.map +1 -1
  97. package/lib/connectionManager.d.ts +1 -1
  98. package/lib/connectionManager.d.ts.map +1 -1
  99. package/lib/connectionManager.js +4 -4
  100. package/lib/connectionManager.js.map +1 -1
  101. package/lib/connectionStateHandler.d.ts.map +1 -1
  102. package/lib/connectionStateHandler.js +2 -2
  103. package/lib/connectionStateHandler.js.map +1 -1
  104. package/lib/container-loader-alpha.d.ts +11 -11
  105. package/lib/container-loader-beta.d.ts +11 -11
  106. package/lib/container-loader-public.d.ts +11 -11
  107. package/lib/container-loader-untrimmed.d.ts +11 -11
  108. package/lib/container.d.ts +5 -3
  109. package/lib/container.d.ts.map +1 -1
  110. package/lib/container.js +14 -12
  111. package/lib/container.js.map +1 -1
  112. package/lib/containerContext.d.ts +5 -3
  113. package/lib/containerContext.d.ts.map +1 -1
  114. package/lib/containerContext.js.map +1 -1
  115. package/lib/containerStorageAdapter.d.ts +2 -2
  116. package/lib/containerStorageAdapter.d.ts.map +1 -1
  117. package/lib/containerStorageAdapter.js +2 -2
  118. package/lib/containerStorageAdapter.js.map +1 -1
  119. package/lib/contracts.d.ts +3 -2
  120. package/lib/contracts.d.ts.map +1 -1
  121. package/lib/contracts.js +1 -1
  122. package/lib/contracts.js.map +1 -1
  123. package/lib/debugLogger.d.ts +2 -1
  124. package/lib/debugLogger.d.ts.map +1 -1
  125. package/lib/debugLogger.js +1 -1
  126. package/lib/debugLogger.js.map +1 -1
  127. package/lib/deltaManager.d.ts +7 -3
  128. package/lib/deltaManager.d.ts.map +1 -1
  129. package/lib/deltaManager.js +12 -9
  130. package/lib/deltaManager.js.map +1 -1
  131. package/lib/deltaQueue.js +1 -1
  132. package/lib/deltaQueue.js.map +1 -1
  133. package/lib/error.d.ts +3 -2
  134. package/lib/error.d.ts.map +1 -1
  135. package/lib/error.js +2 -2
  136. package/lib/error.js.map +1 -1
  137. package/lib/loader.d.ts +2 -2
  138. package/lib/loader.d.ts.map +1 -1
  139. package/lib/loader.js +2 -2
  140. package/lib/loader.js.map +1 -1
  141. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +1 -1
  142. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  143. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +2 -2
  144. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  145. package/lib/noopHeuristic.js +2 -2
  146. package/lib/noopHeuristic.js.map +1 -1
  147. package/lib/packageVersion.d.ts +1 -1
  148. package/lib/packageVersion.js +1 -1
  149. package/lib/packageVersion.js.map +1 -1
  150. package/lib/protocol.d.ts +1 -1
  151. package/lib/protocol.d.ts.map +1 -1
  152. package/lib/protocol.js +1 -1
  153. package/lib/protocol.js.map +1 -1
  154. package/lib/protocolTreeDocumentStorageService.d.ts +4 -4
  155. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  156. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  157. package/lib/quorum.d.ts +1 -1
  158. package/lib/quorum.d.ts.map +1 -1
  159. package/lib/quorum.js.map +1 -1
  160. package/lib/retriableDocumentStorageService.d.ts +1 -1
  161. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  162. package/lib/retriableDocumentStorageService.js +3 -3
  163. package/lib/retriableDocumentStorageService.js.map +1 -1
  164. package/lib/serializedStateManager.d.ts +40 -9
  165. package/lib/serializedStateManager.d.ts.map +1 -1
  166. package/lib/serializedStateManager.js +127 -19
  167. package/lib/serializedStateManager.js.map +1 -1
  168. package/lib/tsdoc-metadata.json +11 -0
  169. package/lib/utils.d.ts +19 -3
  170. package/lib/utils.d.ts.map +1 -1
  171. package/lib/utils.js +44 -4
  172. package/lib/utils.js.map +1 -1
  173. package/package.json +19 -31
  174. package/src/attachment.ts +4 -3
  175. package/src/audience.ts +2 -2
  176. package/src/catchUpMonitor.ts +1 -1
  177. package/src/connectionManager.ts +7 -7
  178. package/src/connectionStateHandler.ts +4 -4
  179. package/src/container.ts +26 -22
  180. package/src/containerContext.ts +8 -5
  181. package/src/containerStorageAdapter.ts +5 -4
  182. package/src/contracts.ts +2 -3
  183. package/src/debugLogger.ts +2 -3
  184. package/src/deltaManager.ts +23 -13
  185. package/src/deltaQueue.ts +1 -1
  186. package/src/error.ts +5 -4
  187. package/src/loader.ts +5 -4
  188. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +3 -3
  189. package/src/noopHeuristic.ts +2 -2
  190. package/src/packageVersion.ts +1 -1
  191. package/src/protocol.ts +2 -2
  192. package/src/protocolTreeDocumentStorageService.ts +4 -1
  193. package/src/quorum.ts +1 -1
  194. package/src/retriableDocumentStorageService.ts +5 -4
  195. package/src/serializedStateManager.ts +188 -23
  196. package/src/utils.ts +56 -5
  197. package/lib/test/attachment.spec.js +0 -380
  198. package/lib/test/attachment.spec.js.map +0 -1
  199. package/lib/test/catchUpMonitor.spec.js +0 -88
  200. package/lib/test/catchUpMonitor.spec.js.map +0 -1
  201. package/lib/test/connectionManager.spec.js +0 -201
  202. package/lib/test/connectionManager.spec.js.map +0 -1
  203. package/lib/test/connectionStateHandler.spec.js +0 -555
  204. package/lib/test/connectionStateHandler.spec.js.map +0 -1
  205. package/lib/test/container.spec.js +0 -64
  206. package/lib/test/container.spec.js.map +0 -1
  207. package/lib/test/deltaManager.spec.js +0 -405
  208. package/lib/test/deltaManager.spec.js.map +0 -1
  209. package/lib/test/loader.spec.js +0 -212
  210. package/lib/test/loader.spec.js.map +0 -1
  211. package/lib/test/locationRedirectionTests.spec.js +0 -44
  212. package/lib/test/locationRedirectionTests.spec.js.map +0 -1
  213. package/lib/test/serializedStateManager.spec.js +0 -151
  214. package/lib/test/serializedStateManager.spec.js.map +0 -1
  215. package/lib/test/snapshotConversionTest.spec.js +0 -79
  216. package/lib/test/snapshotConversionTest.spec.js.map +0 -1
  217. package/lib/test/types/validateContainerLoaderPrevious.generated.js +0 -38
  218. package/lib/test/types/validateContainerLoaderPrevious.generated.js.map +0 -1
  219. package/lib/test/utils.spec.js +0 -96
  220. package/lib/test/utils.spec.js.map +0 -1
package/src/protocol.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IAudienceOwner } from "@fluidframework/container-definitions";
7
- import { canBeCoalescedByService } from "@fluidframework/driver-utils";
6
+ import { IAudienceOwner } from "@fluidframework/container-definitions/internal";
7
+ import { canBeCoalescedByService } from "@fluidframework/driver-utils/internal";
8
8
  import {
9
9
  IProtocolHandler as IBaseProtocolHandler,
10
10
  IQuorumSnapshot,
@@ -4,7 +4,10 @@
4
4
  */
5
5
 
6
6
  import { IDisposable } from "@fluidframework/core-interfaces";
7
- import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
7
+ import {
8
+ IDocumentStorageService,
9
+ ISummaryContext,
10
+ } from "@fluidframework/driver-definitions/internal";
8
11
  import { ISummaryTree } from "@fluidframework/protocol-definitions";
9
12
 
10
13
  /**
package/src/quorum.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IFluidCodeDetails } from "@fluidframework/container-definitions";
6
+ import { IFluidCodeDetails } from "@fluidframework/container-definitions/internal";
7
7
  import { ICommittedProposal } from "@fluidframework/protocol-definitions";
8
8
 
9
9
  export function initQuorumValuesFromCodeDetails(
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { IDisposable } from "@fluidframework/core-interfaces";
7
- import { assert } from "@fluidframework/core-utils";
7
+ import { assert } from "@fluidframework/core-utils/internal";
8
8
  import {
9
9
  FetchSource,
10
10
  IDocumentStorageService,
@@ -12,8 +12,8 @@ import {
12
12
  ISnapshot,
13
13
  ISnapshotFetchOptions,
14
14
  ISummaryContext,
15
- } from "@fluidframework/driver-definitions";
16
- import { runWithRetry } from "@fluidframework/driver-utils";
15
+ } from "@fluidframework/driver-definitions/internal";
16
+ import { runWithRetry } from "@fluidframework/driver-utils/internal";
17
17
  import {
18
18
  ICreateBlobResponse,
19
19
  ISnapshotTree,
@@ -21,7 +21,8 @@ import {
21
21
  ISummaryTree,
22
22
  IVersion,
23
23
  } from "@fluidframework/protocol-definitions";
24
- import { GenericError, ITelemetryLoggerExt, UsageError } from "@fluidframework/telemetry-utils";
24
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
25
+ import { GenericError, UsageError } from "@fluidframework/telemetry-utils/internal";
25
26
 
26
27
  export class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {
27
28
  private _disposed = false;
@@ -3,27 +3,33 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IGetPendingLocalStateProps, IRuntime } from "@fluidframework/container-definitions";
7
- import { assert } from "@fluidframework/core-utils";
6
+ import {
7
+ IGetPendingLocalStateProps,
8
+ IRuntime,
9
+ } from "@fluidframework/container-definitions/internal";
10
+ import { assert } from "@fluidframework/core-utils/internal";
8
11
  import {
9
12
  IDocumentStorageService,
10
13
  IResolvedUrl,
11
14
  ISnapshot,
12
- } from "@fluidframework/driver-definitions";
13
- import { isInstanceOfISnapshot } from "@fluidframework/driver-utils";
15
+ } from "@fluidframework/driver-definitions/internal";
16
+ import { isInstanceOfISnapshot } from "@fluidframework/driver-utils/internal";
14
17
  import {
18
+ type IDocumentAttributes,
15
19
  ISequencedDocumentMessage,
16
20
  ISnapshotTree,
17
21
  IVersion,
18
22
  } from "@fluidframework/protocol-definitions";
23
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
19
24
  import {
20
- ITelemetryLoggerExt,
21
25
  MonitoringContext,
22
26
  PerformanceEvent,
23
27
  UsageError,
24
28
  createChildMonitoringContext,
25
- } from "@fluidframework/telemetry-utils";
29
+ } from "@fluidframework/telemetry-utils/internal";
30
+
26
31
  import { ISerializableBlobContents, getBlobContentsFromTree } from "./containerStorageAdapter.js";
32
+ import { getDocumentAttributes } from "./utils.js";
27
33
 
28
34
  export interface SnapshotWithBlobs {
29
35
  /**
@@ -65,10 +71,16 @@ export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
65
71
  pendingRuntimeState?: unknown;
66
72
  }
67
73
 
74
+ export interface ISnapshotInfo extends SnapshotWithBlobs {
75
+ snapshotSequenceNumber: number;
76
+ }
77
+
68
78
  export class SerializedStateManager {
69
79
  private readonly processedOps: ISequencedDocumentMessage[] = [];
70
- private snapshot: SnapshotWithBlobs | undefined;
80
+ private snapshot: ISnapshotInfo | undefined;
71
81
  private readonly mc: MonitoringContext;
82
+ private latestSnapshot: ISnapshotInfo | undefined;
83
+ private refreshSnapshot: Promise<void> | undefined;
72
84
 
73
85
  constructor(
74
86
  private readonly pendingLocalState: IPendingContainerState | undefined,
@@ -78,6 +90,7 @@ export class SerializedStateManager {
78
90
  "readBlob" | "getSnapshotTree" | "getSnapshot" | "getVersions"
79
91
  >,
80
92
  private readonly _offlineLoadEnabled: boolean,
93
+ private readonly newSnapshotFetched?: () => void,
81
94
  ) {
82
95
  this.mc = createChildMonitoringContext({
83
96
  logger: subLogger,
@@ -92,6 +105,7 @@ export class SerializedStateManager {
92
105
  public addProcessedOp(message: ISequencedDocumentMessage) {
93
106
  if (this.offlineLoadEnabled) {
94
107
  this.processedOps.push(message);
108
+ this.updateSnapshotAndProcessedOpsMaybe();
95
109
  }
96
110
  }
97
111
 
@@ -100,36 +114,112 @@ export class SerializedStateManager {
100
114
  supportGetSnapshotApi: boolean,
101
115
  ) {
102
116
  if (this.pendingLocalState === undefined) {
103
- const { snapshot, version } = supportGetSnapshotApi
104
- ? await fetchISnapshot(this.mc, this.storageAdapter, specifiedVersion)
105
- : await fetchISnapshotTree(this.mc, this.storageAdapter, specifiedVersion);
106
- const baseSnapshot: ISnapshotTree | undefined = isInstanceOfISnapshot(snapshot)
107
- ? snapshot.snapshotTree
108
- : snapshot;
109
- assert(baseSnapshot !== undefined, 0x8e4 /* Snapshot should exist */);
117
+ const { baseSnapshot, version } = await getSnapshotTree(
118
+ this.mc,
119
+ this.storageAdapter,
120
+ supportGetSnapshotApi,
121
+ specifiedVersion,
122
+ );
110
123
  // non-interactive clients will not have any pending state we want to save
111
124
  if (this.offlineLoadEnabled) {
112
125
  const snapshotBlobs = await getBlobContentsFromTree(
113
126
  baseSnapshot,
114
127
  this.storageAdapter,
115
128
  );
116
- this.snapshot = { baseSnapshot, snapshotBlobs };
129
+ const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
130
+ this.snapshot = {
131
+ baseSnapshot,
132
+ snapshotBlobs,
133
+ snapshotSequenceNumber: attributes.sequenceNumber,
134
+ };
117
135
  }
118
136
  return { baseSnapshot, version };
119
137
  } else {
120
138
  const { baseSnapshot, snapshotBlobs } = this.pendingLocalState;
121
- this.snapshot = { baseSnapshot, snapshotBlobs };
139
+ const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);
140
+ this.snapshot = {
141
+ baseSnapshot,
142
+ snapshotBlobs,
143
+ snapshotSequenceNumber: attributes.sequenceNumber,
144
+ };
145
+ this.refreshSnapshot ??= (async () => {
146
+ this.latestSnapshot = await getLatestSnapshotInfo(
147
+ this.mc,
148
+ this.storageAdapter,
149
+ supportGetSnapshotApi,
150
+ );
151
+ this.newSnapshotFetched?.();
152
+ this.updateSnapshotAndProcessedOpsMaybe();
153
+ })();
154
+
122
155
  return { baseSnapshot, version: undefined };
123
156
  }
124
157
  }
125
158
 
159
+ /**
160
+ * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.
161
+ */
162
+ private updateSnapshotAndProcessedOpsMaybe() {
163
+ if (this.latestSnapshot === undefined || this.processedOps.length === 0) {
164
+ // can't refresh latest snapshot until we have processed the ops up to it.
165
+ // Pending state would be behind the latest snapshot.
166
+ return;
167
+ }
168
+ const snapshotSequenceNumber = this.latestSnapshot.snapshotSequenceNumber;
169
+ const firstProcessedOpSequenceNumber = this.processedOps[0].sequenceNumber;
170
+ const lastProcessedOpSequenceNumber =
171
+ this.processedOps[this.processedOps.length - 1].sequenceNumber;
172
+
173
+ if (snapshotSequenceNumber < firstProcessedOpSequenceNumber) {
174
+ // Snapshot seq number is older than our first processed op, which could mean we're fetching
175
+ // the same snapshot that we already have or snapshot is too old, implicating an unexpected behavior.
176
+ this.mc.logger.sendTelemetryEvent({
177
+ eventName: "OldSnapshotFetchWhileRefreshing",
178
+ snapshotSequenceNumber,
179
+ firstProcessedOpSequenceNumber,
180
+ lastProcessedOpSequenceNumber,
181
+ stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
182
+ });
183
+ this.latestSnapshot = undefined;
184
+ } else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
185
+ // Snapshot seq num is between the first and last processed op.
186
+ // Remove the ops that are already part of the snapshot
187
+ this.processedOps.splice(
188
+ 0,
189
+ snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1,
190
+ );
191
+ this.snapshot = this.latestSnapshot;
192
+ this.latestSnapshot = undefined;
193
+ this.mc.logger.sendTelemetryEvent({
194
+ eventName: "SnapshotRefreshed",
195
+ snapshotSequenceNumber,
196
+ firstProcessedOpSequenceNumber,
197
+ newFirstProcessedOpSequenceNumber:
198
+ this.processedOps.length === 0
199
+ ? undefined
200
+ : this.processedOps[0].sequenceNumber,
201
+ });
202
+ }
203
+ }
204
+
126
205
  /**
127
206
  * This method is only meant to be used by Container.attach() to set the initial
128
207
  * base snapshot when attaching.
129
208
  * @param snapshot - snapshot and blobs collected while attaching
130
209
  */
131
- public setSnapshot(snapshot: SnapshotWithBlobs | undefined) {
132
- this.snapshot = snapshot;
210
+ public setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined) {
211
+ if (this.offlineLoadEnabled) {
212
+ assert(this.snapshot === undefined, "inital snapshot should only be defined once");
213
+ assert(snapshot !== undefined, "attachment snapshot should be defined");
214
+ const { baseSnapshot, snapshotBlobs } = snapshot;
215
+ const attributesHash =
216
+ ".protocol" in baseSnapshot.trees
217
+ ? baseSnapshot.trees[".protocol"].blobs.attributes
218
+ : baseSnapshot.blobs[".attributes"];
219
+ const attributes = JSON.parse(snapshotBlobs[attributesHash]);
220
+ assert(attributes.sequenceNumber === 0, "trying to set a non attachment snapshot");
221
+ this.snapshot = { ...snapshot, snapshotSequenceNumber: attributes.sequenceNumber };
222
+ }
133
223
  }
134
224
 
135
225
  public async getPendingLocalStateCore(
@@ -171,11 +261,84 @@ export class SerializedStateManager {
171
261
  }
172
262
  }
173
263
 
264
+ /**
265
+ * Retrieves the most recent snapshot and returns its info.
266
+ *
267
+ * @param mc - The monitoring context.
268
+ * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.
269
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
270
+ * @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.
271
+ */
272
+ export async function getLatestSnapshotInfo(
273
+ mc: MonitoringContext,
274
+ storageAdapter: Pick<
275
+ IDocumentStorageService,
276
+ "getSnapshot" | "getSnapshotTree" | "getVersions" | "readBlob"
277
+ >,
278
+ supportGetSnapshotApi: boolean,
279
+ ): Promise<ISnapshotInfo | undefined> {
280
+ return PerformanceEvent.timedExecAsync(
281
+ mc.logger,
282
+ { eventName: "GetLatestSnapshotInfo" },
283
+ async () => {
284
+ const { baseSnapshot } = await getSnapshotTree(
285
+ mc,
286
+ storageAdapter,
287
+ supportGetSnapshotApi,
288
+ undefined,
289
+ );
290
+ const snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, storageAdapter);
291
+ const attributes: IDocumentAttributes = await getDocumentAttributes(
292
+ storageAdapter,
293
+ baseSnapshot,
294
+ );
295
+ const snapshotSequenceNumber = attributes.sequenceNumber;
296
+ return { baseSnapshot, snapshotBlobs, snapshotSequenceNumber };
297
+ },
298
+ ).catch(() => undefined);
299
+ }
300
+
301
+ /**
302
+ * Retrieves a snapshot from the storage adapter and transforms it into an ISnapshotTree object.
303
+ *
304
+ * @param mc - The monitoring context.
305
+ * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.
306
+ * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.
307
+ * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
308
+ * @returns - An ISnapshotTree and its version.
309
+ */
310
+ async function getSnapshotTree(
311
+ mc: MonitoringContext,
312
+ storageAdapter: Pick<
313
+ IDocumentStorageService,
314
+ "getSnapshot" | "getSnapshotTree" | "getVersions"
315
+ >,
316
+ supportGetSnapshotApi: boolean,
317
+ specifiedVersion: string | undefined,
318
+ ): Promise<{ baseSnapshot: ISnapshotTree; version?: IVersion }> {
319
+ const { snapshot, version } = supportGetSnapshotApi
320
+ ? await fetchISnapshot(mc, storageAdapter, specifiedVersion)
321
+ : await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);
322
+ const baseSnapshot: ISnapshotTree | undefined = isInstanceOfISnapshot(snapshot)
323
+ ? snapshot.snapshotTree
324
+ : snapshot;
325
+ assert(baseSnapshot !== undefined, 0x8e4 /* Snapshot should exist */);
326
+ return { baseSnapshot, version };
327
+ }
328
+
329
+ /**
330
+ * Fetches an ISnapshot from a storage adapter based on the specified version.
331
+ *
332
+ * @param mc - The monitoring context.
333
+ * @param storageAdapter - The storage adapter providing a getSnapshot method to retrieve the ISnapshot and version.
334
+ * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
335
+ * @returns - The fetched snapshot tree and its version.
336
+ */
174
337
  export async function fetchISnapshot(
175
338
  mc: MonitoringContext,
176
339
  storageAdapter: Pick<IDocumentStorageService, "getSnapshot">,
177
340
  specifiedVersion: string | undefined,
178
- ): Promise<{ snapshot?: ISnapshot | ISnapshotTree; version?: IVersion }> {
341
+ ): Promise<{ snapshot?: ISnapshot; version?: IVersion }> {
179
342
  const snapshot = await storageAdapter.getSnapshot?.({ versionId: specifiedVersion });
180
343
  const version: IVersion | undefined =
181
344
  snapshot?.snapshotTree.id === undefined
@@ -190,7 +353,6 @@ export async function fetchISnapshot(
190
353
  eventName: "getSnapshotTreeFailed",
191
354
  id: specifiedVersion,
192
355
  });
193
- // Not sure if this should be here actually
194
356
  } else if (snapshot !== undefined && version?.id === undefined) {
195
357
  mc.logger.sendErrorEvent({
196
358
  eventName: "getSnapshotFetchedTreeWithoutVersionId",
@@ -201,9 +363,12 @@ export async function fetchISnapshot(
201
363
  }
202
364
 
203
365
  /**
204
- * Get the most recent snapshot, or a specific version.
205
- * @param specifiedVersion - The specific version of the snapshot to retrieve
206
- * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
366
+ * Fetches an ISnapshotTree from a storage adapter based on the specified version.
367
+ *
368
+ * @param mc - The monitoring context.
369
+ * @param storageAdapter - The storage adapter providing methods to retrieve the ISnapshotTree and version.
370
+ * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.
371
+ * @returns - The fetched snapshot tree and its version.
207
372
  */
208
373
  export async function fetchISnapshotTree(
209
374
  mc: MonitoringContext,
package/src/utils.ts CHANGED
@@ -3,26 +3,32 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { Uint8ArrayToString, stringToBuffer } from "@fluid-internal/client-utils";
7
- import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils";
8
- import { DriverErrorTypes, IDocumentStorageService } from "@fluidframework/driver-definitions";
6
+ import { Uint8ArrayToString, bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
7
+ import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils/internal";
8
+ import { DriverErrorTypes } from "@fluidframework/driver-definitions";
9
+ import {
10
+ IDocumentStorageService,
11
+ type ISnapshot,
12
+ } from "@fluidframework/driver-definitions/internal";
9
13
  import {
10
14
  CombinedAppAndProtocolSummary,
11
15
  DeltaStreamConnectionForbiddenError,
12
16
  isCombinedAppAndProtocolSummary,
13
17
  readAndParse,
14
- } from "@fluidframework/driver-utils";
18
+ } from "@fluidframework/driver-utils/internal";
15
19
  import {
16
20
  IDocumentAttributes,
17
21
  ISnapshotTree,
18
22
  ISummaryTree,
19
23
  SummaryType,
20
24
  } from "@fluidframework/protocol-definitions";
21
- import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
25
+ import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
22
26
  import { v4 as uuid } from "uuid";
27
+
23
28
  import { ISerializableBlobContents } from "./containerStorageAdapter.js";
24
29
  import type {
25
30
  IPendingDetachedContainerState,
31
+ ISnapshotInfo,
26
32
  SnapshotWithBlobs,
27
33
  } from "./serializedStateManager.js";
28
34
 
@@ -167,6 +173,51 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBl
167
173
  return pendingSnapshot;
168
174
  }
169
175
 
176
+ /**
177
+ * Converts a snapshot to snapshotInfo with its blob contents
178
+ * to align detached container format with IPendingContainerState
179
+ *
180
+ * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown
181
+ * @param snapshot - ISnapshot
182
+ */
183
+ export function convertSnapshotToSnapshotInfo(snapshot: ISnapshot): ISnapshotInfo {
184
+ assert(snapshot.sequenceNumber !== undefined, "Snapshot sequence number is missing");
185
+ const snapshotBlobs: ISerializableBlobContents = {};
186
+ for (const [blobId, arrayBufferLike] of snapshot.blobContents.entries()) {
187
+ snapshotBlobs[blobId] = bufferToString(arrayBufferLike, "utf8");
188
+ }
189
+ return {
190
+ baseSnapshot: snapshot.snapshotTree,
191
+ snapshotBlobs,
192
+ snapshotSequenceNumber: snapshot.sequenceNumber,
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Converts a snapshot to snapshotInfo with its blob contents
198
+ * to align detached container format with IPendingContainerState
199
+ *
200
+ * Note, this assumes the ISnapshot sequence number is defined. Otherwise an assert will be thrown
201
+ * @param snapshot - ISnapshot
202
+ */
203
+ export function convertSnapshotInfoToSnapshot(
204
+ snapshotInfo: ISnapshotInfo,
205
+ snapshotSequenceNumber: number,
206
+ ): ISnapshot {
207
+ const blobContents = new Map<string, ArrayBufferLike>();
208
+ for (const [blobId, serializedContent] of Object.entries(snapshotInfo.snapshotBlobs)) {
209
+ blobContents.set(blobId, stringToBuffer(serializedContent, "utf8"));
210
+ }
211
+ return {
212
+ snapshotTree: snapshotInfo.baseSnapshot,
213
+ blobContents,
214
+ ops: [],
215
+ sequenceNumber: snapshotSequenceNumber,
216
+ latestSequenceNumber: undefined,
217
+ snapshotFormatV: 1,
218
+ };
219
+ }
220
+
170
221
  /**
171
222
  * Converts summary parts into a SnapshotTree and its blob contents.
172
223
  * @param protocolSummaryTree - Protocol Summary Tree