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

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 (177) hide show
  1. package/dist/attachment.d.ts +5 -8
  2. package/dist/attachment.d.ts.map +1 -1
  3. package/dist/attachment.js.map +1 -1
  4. package/dist/audience.d.ts.map +1 -1
  5. package/dist/audience.js.map +1 -1
  6. package/dist/catchUpMonitor.d.ts +1 -1
  7. package/dist/catchUpMonitor.d.ts.map +1 -1
  8. package/dist/catchUpMonitor.js.map +1 -1
  9. package/dist/connectionManager.d.ts +3 -3
  10. package/dist/connectionManager.d.ts.map +1 -1
  11. package/dist/connectionManager.js +1 -1
  12. package/dist/connectionManager.js.map +1 -1
  13. package/dist/connectionStateHandler.d.ts +3 -3
  14. package/dist/connectionStateHandler.d.ts.map +1 -1
  15. package/dist/connectionStateHandler.js.map +1 -1
  16. package/dist/container.d.ts +4 -43
  17. package/dist/container.d.ts.map +1 -1
  18. package/dist/container.js +24 -37
  19. package/dist/container.js.map +1 -1
  20. package/dist/containerContext.d.ts +17 -7
  21. package/dist/containerContext.d.ts.map +1 -1
  22. package/dist/containerContext.js +7 -2
  23. package/dist/containerContext.js.map +1 -1
  24. package/dist/containerStorageAdapter.d.ts +2 -2
  25. package/dist/containerStorageAdapter.d.ts.map +1 -1
  26. package/dist/containerStorageAdapter.js.map +1 -1
  27. package/dist/contracts.d.ts +3 -3
  28. package/dist/contracts.d.ts.map +1 -1
  29. package/dist/contracts.js.map +1 -1
  30. package/dist/debugLogger.d.ts.map +1 -1
  31. package/dist/debugLogger.js.map +1 -1
  32. package/dist/deltaManager.d.ts +5 -5
  33. package/dist/deltaManager.d.ts.map +1 -1
  34. package/dist/deltaManager.js +3 -3
  35. package/dist/deltaManager.js.map +1 -1
  36. package/dist/deltaQueue.d.ts +1 -1
  37. package/dist/deltaQueue.d.ts.map +1 -1
  38. package/dist/deltaQueue.js +1 -1
  39. package/dist/deltaQueue.js.map +1 -1
  40. package/dist/error.d.ts.map +1 -1
  41. package/dist/error.js.map +1 -1
  42. package/dist/loader.d.ts +3 -3
  43. package/dist/loader.d.ts.map +1 -1
  44. package/dist/loader.js +4 -4
  45. package/dist/loader.js.map +1 -1
  46. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +1 -1
  47. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  48. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  49. package/dist/noopHeuristic.d.ts +1 -1
  50. package/dist/noopHeuristic.d.ts.map +1 -1
  51. package/dist/noopHeuristic.js.map +1 -1
  52. package/dist/packageVersion.d.ts +1 -1
  53. package/dist/packageVersion.js +1 -1
  54. package/dist/packageVersion.js.map +1 -1
  55. package/dist/quorum.d.ts +1 -1
  56. package/dist/quorum.d.ts.map +1 -1
  57. package/dist/quorum.js.map +1 -1
  58. package/dist/retriableDocumentStorageService.d.ts +1 -1
  59. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  60. package/dist/retriableDocumentStorageService.js +1 -1
  61. package/dist/retriableDocumentStorageService.js.map +1 -1
  62. package/dist/serializedStateManager.d.ts +57 -18
  63. package/dist/serializedStateManager.d.ts.map +1 -1
  64. package/dist/serializedStateManager.js +71 -80
  65. package/dist/serializedStateManager.js.map +1 -1
  66. package/dist/utils.d.ts +5 -6
  67. package/dist/utils.d.ts.map +1 -1
  68. package/dist/utils.js +27 -11
  69. package/dist/utils.js.map +1 -1
  70. package/lib/attachment.d.ts +5 -8
  71. package/lib/attachment.d.ts.map +1 -1
  72. package/lib/attachment.js.map +1 -1
  73. package/lib/audience.d.ts.map +1 -1
  74. package/lib/audience.js.map +1 -1
  75. package/lib/catchUpMonitor.d.ts +1 -1
  76. package/lib/catchUpMonitor.d.ts.map +1 -1
  77. package/lib/catchUpMonitor.js.map +1 -1
  78. package/lib/connectionManager.d.ts +3 -3
  79. package/lib/connectionManager.d.ts.map +1 -1
  80. package/lib/connectionManager.js +3 -3
  81. package/lib/connectionManager.js.map +1 -1
  82. package/lib/connectionStateHandler.d.ts +3 -3
  83. package/lib/connectionStateHandler.d.ts.map +1 -1
  84. package/lib/connectionStateHandler.js.map +1 -1
  85. package/lib/container.d.ts +4 -43
  86. package/lib/container.d.ts.map +1 -1
  87. package/lib/container.js +27 -40
  88. package/lib/container.js.map +1 -1
  89. package/lib/containerContext.d.ts +17 -7
  90. package/lib/containerContext.d.ts.map +1 -1
  91. package/lib/containerContext.js +7 -2
  92. package/lib/containerContext.js.map +1 -1
  93. package/lib/containerStorageAdapter.d.ts +2 -2
  94. package/lib/containerStorageAdapter.d.ts.map +1 -1
  95. package/lib/containerStorageAdapter.js.map +1 -1
  96. package/lib/contracts.d.ts +3 -3
  97. package/lib/contracts.d.ts.map +1 -1
  98. package/lib/contracts.js.map +1 -1
  99. package/lib/debugLogger.d.ts.map +1 -1
  100. package/lib/debugLogger.js.map +1 -1
  101. package/lib/deltaManager.d.ts +5 -5
  102. package/lib/deltaManager.d.ts.map +1 -1
  103. package/lib/deltaManager.js +3 -3
  104. package/lib/deltaManager.js.map +1 -1
  105. package/lib/deltaQueue.d.ts +1 -1
  106. package/lib/deltaQueue.d.ts.map +1 -1
  107. package/lib/deltaQueue.js +1 -1
  108. package/lib/deltaQueue.js.map +1 -1
  109. package/lib/error.d.ts.map +1 -1
  110. package/lib/error.js.map +1 -1
  111. package/lib/loader.d.ts +3 -3
  112. package/lib/loader.d.ts.map +1 -1
  113. package/lib/loader.js +4 -4
  114. package/lib/loader.js.map +1 -1
  115. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +1 -1
  116. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -1
  117. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  118. package/lib/noopHeuristic.d.ts +1 -1
  119. package/lib/noopHeuristic.d.ts.map +1 -1
  120. package/lib/noopHeuristic.js.map +1 -1
  121. package/lib/packageVersion.d.ts +1 -1
  122. package/lib/packageVersion.js +1 -1
  123. package/lib/packageVersion.js.map +1 -1
  124. package/lib/quorum.d.ts +1 -1
  125. package/lib/quorum.d.ts.map +1 -1
  126. package/lib/quorum.js.map +1 -1
  127. package/lib/retriableDocumentStorageService.d.ts +1 -1
  128. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  129. package/lib/retriableDocumentStorageService.js +1 -1
  130. package/lib/retriableDocumentStorageService.js.map +1 -1
  131. package/lib/serializedStateManager.d.ts +57 -18
  132. package/lib/serializedStateManager.d.ts.map +1 -1
  133. package/lib/serializedStateManager.js +68 -79
  134. package/lib/serializedStateManager.js.map +1 -1
  135. package/lib/test/attachment.spec.js +2 -2
  136. package/lib/test/attachment.spec.js.map +1 -1
  137. package/lib/test/catchUpMonitor.spec.js.map +1 -1
  138. package/lib/test/connectionStateHandler.spec.js +1 -1
  139. package/lib/test/connectionStateHandler.spec.js.map +1 -1
  140. package/lib/test/container.spec.js +1 -1
  141. package/lib/test/container.spec.js.map +1 -1
  142. package/lib/test/deltaManager.spec.js +2 -2
  143. package/lib/test/deltaManager.spec.js.map +1 -1
  144. package/lib/test/loader.spec.js +4 -4
  145. package/lib/test/loader.spec.js.map +1 -1
  146. package/lib/test/serializedStateManager.spec.js +10 -7
  147. package/lib/test/serializedStateManager.spec.js.map +1 -1
  148. package/lib/test/snapshotConversionTest.spec.js +12 -12
  149. package/lib/test/snapshotConversionTest.spec.js.map +1 -1
  150. package/lib/test/utils.spec.js +66 -1
  151. package/lib/test/utils.spec.js.map +1 -1
  152. package/lib/utils.d.ts +5 -6
  153. package/lib/utils.d.ts.map +1 -1
  154. package/lib/utils.js +26 -11
  155. package/lib/utils.js.map +1 -1
  156. package/package.json +18 -15
  157. package/src/attachment.ts +6 -6
  158. package/src/audience.ts +1 -1
  159. package/src/catchUpMonitor.ts +1 -1
  160. package/src/connectionManager.ts +15 -15
  161. package/src/connectionStateHandler.ts +4 -4
  162. package/src/container.ts +68 -125
  163. package/src/containerContext.ts +16 -9
  164. package/src/containerStorageAdapter.ts +3 -3
  165. package/src/contracts.ts +4 -4
  166. package/src/debugLogger.ts +1 -1
  167. package/src/deltaManager.ts +24 -24
  168. package/src/deltaQueue.ts +1 -1
  169. package/src/error.ts +1 -1
  170. package/src/loader.ts +22 -21
  171. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +1 -1
  172. package/src/noopHeuristic.ts +2 -2
  173. package/src/packageVersion.ts +1 -1
  174. package/src/quorum.ts +1 -1
  175. package/src/retriableDocumentStorageService.ts +2 -2
  176. package/src/serializedStateManager.ts +133 -116
  177. package/src/utils.ts +47 -18
@@ -3,6 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { IDisposable } from "@fluidframework/core-interfaces";
6
7
  import { assert } from "@fluidframework/core-utils";
7
8
  import {
8
9
  FetchSource,
@@ -12,6 +13,7 @@ import {
12
13
  ISnapshotFetchOptions,
13
14
  ISummaryContext,
14
15
  } from "@fluidframework/driver-definitions";
16
+ import { runWithRetry } from "@fluidframework/driver-utils";
15
17
  import {
16
18
  ICreateBlobResponse,
17
19
  ISnapshotTree,
@@ -19,9 +21,7 @@ import {
19
21
  ISummaryTree,
20
22
  IVersion,
21
23
  } from "@fluidframework/protocol-definitions";
22
- import { IDisposable } from "@fluidframework/core-interfaces";
23
24
  import { GenericError, ITelemetryLoggerExt, UsageError } from "@fluidframework/telemetry-utils";
24
- import { runWithRetry } from "@fluidframework/driver-utils";
25
25
 
26
26
  export class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {
27
27
  private _disposed = false;
@@ -3,12 +3,19 @@
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";
8
+ import {
9
+ IDocumentStorageService,
10
+ IResolvedUrl,
11
+ ISnapshot,
12
+ } from "@fluidframework/driver-definitions";
13
+ import { isInstanceOfISnapshot } from "@fluidframework/driver-utils";
6
14
  import {
7
15
  ISequencedDocumentMessage,
8
16
  ISnapshotTree,
9
17
  IVersion,
10
18
  } from "@fluidframework/protocol-definitions";
11
- import { IGetPendingLocalStateProps, IRuntime } from "@fluidframework/container-definitions";
12
19
  import {
13
20
  ITelemetryLoggerExt,
14
21
  MonitoringContext,
@@ -16,24 +23,51 @@ import {
16
23
  UsageError,
17
24
  createChildMonitoringContext,
18
25
  } from "@fluidframework/telemetry-utils";
19
- import { assert } from "@fluidframework/core-utils";
20
- import {
21
- IDocumentStorageService,
22
- IResolvedUrl,
23
- ISnapshot,
24
- } from "@fluidframework/driver-definitions";
25
- import { isInstanceOfISnapshot } from "@fluidframework/driver-utils";
26
26
  import { ISerializableBlobContents, getBlobContentsFromTree } from "./containerStorageAdapter.js";
27
- import { IPendingContainerState } from "./container.js";
27
+
28
+ export interface SnapshotWithBlobs {
29
+ /**
30
+ * Snapshot from which container initially loaded.
31
+ */
32
+ baseSnapshot: ISnapshotTree;
33
+ /**
34
+ * Serializable blobs from the base snapshot. Used to load offline since
35
+ * storage is not available.
36
+ */
37
+ snapshotBlobs: ISerializableBlobContents;
38
+ }
39
+ /**
40
+ * State saved by a container at close time, to be used to load a new instance
41
+ * of the container to the same state
42
+ * @internal
43
+ */
44
+ export interface IPendingContainerState extends SnapshotWithBlobs {
45
+ attached: true;
46
+ pendingRuntimeState: unknown;
47
+ /**
48
+ * All ops since base snapshot sequence number up to the latest op
49
+ * seen when the container was closed. Used to apply stashed (saved pending)
50
+ * ops at the same sequence number at which they were made.
51
+ */
52
+ savedOps: ISequencedDocumentMessage[];
53
+ url: string;
54
+ clientId?: string;
55
+ }
56
+
57
+ /**
58
+ * State saved by a container in detached state, to be used to load a new instance
59
+ * of the container to the same state (rehydrate)
60
+ * @internal
61
+ */
62
+ export interface IPendingDetachedContainerState extends SnapshotWithBlobs {
63
+ attached: false;
64
+ hasAttachmentBlobs: boolean;
65
+ pendingRuntimeState?: unknown;
66
+ }
28
67
 
29
68
  export class SerializedStateManager {
30
69
  private readonly processedOps: ISequencedDocumentMessage[] = [];
31
- private snapshot:
32
- | {
33
- tree: ISnapshotTree;
34
- blobs: ISerializableBlobContents;
35
- }
36
- | undefined;
70
+ private snapshot: SnapshotWithBlobs | undefined;
37
71
  private readonly mc: MonitoringContext;
38
72
 
39
73
  constructor(
@@ -61,104 +95,32 @@ export class SerializedStateManager {
61
95
  }
62
96
  }
63
97
 
64
- private async getVersion(version: string | null): Promise<IVersion | undefined> {
65
- const versions = await this.storageAdapter.getVersions(version, 1);
66
- return versions[0];
67
- }
68
-
69
98
  public async fetchSnapshot(
70
99
  specifiedVersion: string | undefined,
71
- supportGetSnapshotApi: boolean | undefined,
100
+ supportGetSnapshotApi: boolean,
72
101
  ) {
73
- const { snapshot, version } =
74
- this.pendingLocalState === undefined
75
- ? await this.fetchSnapshotCore(specifiedVersion, supportGetSnapshotApi)
76
- : { snapshot: this.pendingLocalState.baseSnapshot, version: undefined };
77
- const snapshotTree: ISnapshotTree | undefined = isInstanceOfISnapshot(snapshot)
78
- ? snapshot.snapshotTree
79
- : snapshot;
80
- if (this.pendingLocalState) {
81
- this.snapshot = {
82
- tree: this.pendingLocalState.baseSnapshot,
83
- blobs: this.pendingLocalState.snapshotBlobs,
84
- };
85
- } else {
86
- assert(snapshotTree !== undefined, 0x8e4 /* Snapshot should exist */);
102
+ 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 */);
87
110
  // non-interactive clients will not have any pending state we want to save
88
111
  if (this.offlineLoadEnabled) {
89
- const blobs = await getBlobContentsFromTree(snapshotTree, this.storageAdapter);
90
- this.snapshot = { tree: snapshotTree, blobs };
91
- }
92
- }
93
- return { snapshotTree, version };
94
- }
95
-
96
- private async fetchSnapshotCore(
97
- specifiedVersion: string | undefined,
98
- supportGetSnapshotApi: boolean | undefined,
99
- ): Promise<{ snapshot?: ISnapshot | ISnapshotTree; version?: IVersion }> {
100
- if (
101
- this.mc.config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch") ===
102
- true &&
103
- supportGetSnapshotApi === true
104
- ) {
105
- const snapshot =
106
- (await this.storageAdapter.getSnapshot?.({
107
- versionId: specifiedVersion,
108
- })) ?? undefined;
109
- const version: IVersion | undefined =
110
- snapshot?.snapshotTree.id === undefined
111
- ? undefined
112
- : {
113
- id: snapshot.snapshotTree.id,
114
- treeId: snapshot.snapshotTree.id,
115
- };
116
-
117
- if (snapshot === undefined && specifiedVersion !== undefined) {
118
- this.mc.logger.sendErrorEvent({
119
- eventName: "getSnapshotTreeFailed",
120
- id: specifiedVersion,
121
- });
122
- // Not sure if this should be here actually
123
- } else if (snapshot !== undefined && version?.id === undefined) {
124
- this.mc.logger.sendErrorEvent({
125
- eventName: "getSnapshotFetchedTreeWithoutVersionId",
126
- hasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.
127
- });
112
+ const snapshotBlobs = await getBlobContentsFromTree(
113
+ baseSnapshot,
114
+ this.storageAdapter,
115
+ );
116
+ this.snapshot = { baseSnapshot, snapshotBlobs };
128
117
  }
129
- return { snapshot, version };
130
- }
131
- return this.fetchSnapshotTree(specifiedVersion);
132
- }
133
-
134
- /**
135
- * Get the most recent snapshot, or a specific version.
136
- * @param specifiedVersion - The specific version of the snapshot to retrieve
137
- * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
138
- */
139
- private async fetchSnapshotTree(
140
- specifiedVersion: string | undefined,
141
- ): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {
142
- const version = await this.getVersion(specifiedVersion ?? null);
143
-
144
- if (version === undefined && specifiedVersion !== undefined) {
145
- // We should have a defined version to load from if specified version requested
146
- this.mc.logger.sendErrorEvent({
147
- eventName: "NoVersionFoundWhenSpecified",
148
- id: specifiedVersion,
149
- });
150
- }
151
- const snapshot = (await this.storageAdapter.getSnapshotTree(version)) ?? undefined;
152
-
153
- if (snapshot === undefined && version !== undefined) {
154
- this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
155
- } else if (snapshot !== undefined && version?.id === undefined) {
156
- this.mc.logger.sendErrorEvent({
157
- eventName: "getSnapshotFetchedTreeWithoutVersionId",
158
- hasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.
159
- });
118
+ return { baseSnapshot, version };
119
+ } else {
120
+ const { baseSnapshot, snapshotBlobs } = this.pendingLocalState;
121
+ this.snapshot = { baseSnapshot, snapshotBlobs };
122
+ return { baseSnapshot, version: undefined };
160
123
  }
161
- return { snapshot, version };
162
124
  }
163
125
 
164
126
  /**
@@ -166,14 +128,7 @@ export class SerializedStateManager {
166
128
  * base snapshot when attaching.
167
129
  * @param snapshot - snapshot and blobs collected while attaching
168
130
  */
169
- public setSnapshot(
170
- snapshot:
171
- | {
172
- tree: ISnapshotTree;
173
- blobs: ISerializableBlobContents;
174
- }
175
- | undefined,
176
- ) {
131
+ public setSnapshot(snapshot: SnapshotWithBlobs | undefined) {
177
132
  this.snapshot = snapshot;
178
133
  }
179
134
 
@@ -202,8 +157,8 @@ export class SerializedStateManager {
202
157
  const pendingState: IPendingContainerState = {
203
158
  attached: true,
204
159
  pendingRuntimeState,
205
- baseSnapshot: this.snapshot.tree,
206
- snapshotBlobs: this.snapshot.blobs,
160
+ baseSnapshot: this.snapshot.baseSnapshot,
161
+ snapshotBlobs: this.snapshot.snapshotBlobs,
207
162
  savedOps: this.processedOps,
208
163
  url: resolvedUrl.url,
209
164
  // no need to save this if there is no pending runtime state
@@ -215,3 +170,65 @@ export class SerializedStateManager {
215
170
  );
216
171
  }
217
172
  }
173
+
174
+ export async function fetchISnapshot(
175
+ mc: MonitoringContext,
176
+ storageAdapter: Pick<IDocumentStorageService, "getSnapshot">,
177
+ specifiedVersion: string | undefined,
178
+ ): Promise<{ snapshot?: ISnapshot | ISnapshotTree; version?: IVersion }> {
179
+ const snapshot = await storageAdapter.getSnapshot?.({ versionId: specifiedVersion });
180
+ const version: IVersion | undefined =
181
+ snapshot?.snapshotTree.id === undefined
182
+ ? undefined
183
+ : {
184
+ id: snapshot.snapshotTree.id,
185
+ treeId: snapshot.snapshotTree.id,
186
+ };
187
+
188
+ if (snapshot === undefined && specifiedVersion !== undefined) {
189
+ mc.logger.sendErrorEvent({
190
+ eventName: "getSnapshotTreeFailed",
191
+ id: specifiedVersion,
192
+ });
193
+ // Not sure if this should be here actually
194
+ } else if (snapshot !== undefined && version?.id === undefined) {
195
+ mc.logger.sendErrorEvent({
196
+ eventName: "getSnapshotFetchedTreeWithoutVersionId",
197
+ hasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.
198
+ });
199
+ }
200
+ return { snapshot, version };
201
+ }
202
+
203
+ /**
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
207
+ */
208
+ export async function fetchISnapshotTree(
209
+ mc: MonitoringContext,
210
+ storageAdapter: Pick<IDocumentStorageService, "getSnapshotTree" | "getVersions">,
211
+ specifiedVersion: string | undefined,
212
+ ): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {
213
+ const versions = await storageAdapter.getVersions(specifiedVersion ?? null, 1);
214
+ const version = versions[0];
215
+
216
+ if (version === undefined && specifiedVersion !== undefined) {
217
+ // We should have a defined version to load from if specified version requested
218
+ mc.logger.sendErrorEvent({
219
+ eventName: "NoVersionFoundWhenSpecified",
220
+ id: specifiedVersion,
221
+ });
222
+ }
223
+ const snapshot = (await storageAdapter.getSnapshotTree(version)) ?? undefined;
224
+
225
+ if (snapshot === undefined && version !== undefined) {
226
+ mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
227
+ } else if (snapshot !== undefined && version?.id === undefined) {
228
+ mc.logger.sendErrorEvent({
229
+ eventName: "getSnapshotFetchedTreeWithoutVersionId",
230
+ hasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.
231
+ });
232
+ }
233
+ return { snapshot, version };
234
+ }
package/src/utils.ts CHANGED
@@ -3,19 +3,28 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { v4 as uuid } from "uuid";
7
6
  import { Uint8ArrayToString, stringToBuffer } from "@fluid-internal/client-utils";
8
7
  import { assert, compareArrays, unreachableCase } from "@fluidframework/core-utils";
9
- import { ISummaryTree, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
10
- import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
8
+ import { DriverErrorTypes, IDocumentStorageService } from "@fluidframework/driver-definitions";
11
9
  import {
12
10
  CombinedAppAndProtocolSummary,
13
11
  DeltaStreamConnectionForbiddenError,
14
12
  isCombinedAppAndProtocolSummary,
13
+ readAndParse,
15
14
  } from "@fluidframework/driver-utils";
16
- import { DriverErrorTypes } from "@fluidframework/driver-definitions";
15
+ import {
16
+ IDocumentAttributes,
17
+ ISnapshotTree,
18
+ ISummaryTree,
19
+ SummaryType,
20
+ } from "@fluidframework/protocol-definitions";
21
+ import { LoggingError, UsageError } from "@fluidframework/telemetry-utils";
22
+ import { v4 as uuid } from "uuid";
17
23
  import { ISerializableBlobContents } from "./containerStorageAdapter.js";
18
- import { IPendingDetachedContainerState } from "./container.js";
24
+ import type {
25
+ IPendingDetachedContainerState,
26
+ SnapshotWithBlobs,
27
+ } from "./serializedStateManager.js";
19
28
 
20
29
  // This is used when we rehydrate a container from the snapshot. Here we put the blob contents
21
30
  // in separate property: blobContents.
@@ -111,10 +120,7 @@ export function combineAppAndProtocolSummary(
111
120
  * to align detached container format with IPendingContainerState
112
121
  * @param summary - ISummaryTree
113
122
  */
114
- function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {
115
- tree: ISnapshotTree;
116
- blobs: ISerializableBlobContents;
117
- } {
123
+ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): SnapshotWithBlobs {
118
124
  let blobContents: ISerializableBlobContents = {};
119
125
  const treeNode: ISnapshotTree = {
120
126
  blobs: {},
@@ -129,9 +135,9 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {
129
135
 
130
136
  switch (summaryObject.type) {
131
137
  case SummaryType.Tree: {
132
- const { tree, blobs } = convertSummaryToSnapshotAndBlobs(summaryObject);
133
- treeNode.trees[key] = tree;
134
- blobContents = { ...blobContents, ...blobs };
138
+ const innerSnapshot = convertSummaryToSnapshotAndBlobs(summaryObject);
139
+ treeNode.trees[key] = innerSnapshot.baseSnapshot;
140
+ blobContents = { ...blobContents, ...innerSnapshot.snapshotBlobs };
135
141
  break;
136
142
  }
137
143
  case SummaryType.Attachment:
@@ -157,7 +163,8 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {
157
163
  }
158
164
  }
159
165
  }
160
- return { tree: treeNode, blobs: blobContents };
166
+ const pendingSnapshot = { baseSnapshot: treeNode, snapshotBlobs: blobContents };
167
+ return pendingSnapshot;
161
168
  }
162
169
 
163
170
  /**
@@ -168,7 +175,7 @@ function convertSummaryToSnapshotAndBlobs(summary: ISummaryTree): {
168
175
  function convertProtocolAndAppSummaryToSnapshotAndBlobs(
169
176
  protocolSummaryTree: ISummaryTree,
170
177
  appSummaryTree: ISummaryTree,
171
- ): { tree: ISnapshotTree; blobs: ISerializableBlobContents } {
178
+ ): SnapshotWithBlobs {
172
179
  const combinedSummary: ISummaryTree = {
173
180
  type: SummaryType.Tree,
174
181
  tree: { ...appSummaryTree.tree },
@@ -181,7 +188,7 @@ function convertProtocolAndAppSummaryToSnapshotAndBlobs(
181
188
 
182
189
  export const getSnapshotTreeAndBlobsFromSerializedContainer = (
183
190
  detachedContainerSnapshot: ISummaryTree,
184
- ): { tree: ISnapshotTree; blobs: ISerializableBlobContents } => {
191
+ ): SnapshotWithBlobs => {
185
192
  assert(
186
193
  isCombinedAppAndProtocolSummary(detachedContainerSnapshot),
187
194
  0x8e6 /* Protocol and App summary trees should be present */,
@@ -264,12 +271,12 @@ export function getDetachedContainerStateFromSerializedContainer(
264
271
  if (isPendingDetachedContainerState(parsedContainerState)) {
265
272
  return parsedContainerState;
266
273
  } else if (isCombinedAppAndProtocolSummary(parsedContainerState)) {
267
- const { tree, blobs } =
274
+ const { baseSnapshot, snapshotBlobs } =
268
275
  getSnapshotTreeAndBlobsFromSerializedContainer(parsedContainerState);
269
276
  const detachedContainerState: IPendingDetachedContainerState = {
270
277
  attached: false,
271
- baseSnapshot: tree,
272
- snapshotBlobs: blobs,
278
+ baseSnapshot,
279
+ snapshotBlobs,
273
280
  hasAttachmentBlobs: parsedContainerState.tree[hasBlobsSummaryTree] !== undefined,
274
281
  };
275
282
  return detachedContainerState;
@@ -305,3 +312,25 @@ export const runSingle = <A extends any[], R>(func: (...args: A) => Promise<R>)
305
312
  return running.result;
306
313
  };
307
314
  };
315
+
316
+ export async function getDocumentAttributes(
317
+ storage: Pick<IDocumentStorageService, "readBlob">,
318
+ tree: ISnapshotTree | undefined,
319
+ ): Promise<IDocumentAttributes> {
320
+ if (tree === undefined) {
321
+ return {
322
+ minimumSequenceNumber: 0,
323
+ sequenceNumber: 0,
324
+ };
325
+ }
326
+
327
+ // Backward compatibility: old docs would have ".attributes" instead of "attributes"
328
+ const attributesHash =
329
+ ".protocol" in tree.trees
330
+ ? tree.trees[".protocol"].blobs.attributes
331
+ : tree.blobs[".attributes"];
332
+
333
+ const attributes = await readAndParse<IDocumentAttributes>(storage, attributesHash);
334
+
335
+ return attributes;
336
+ }