@fluidframework/container-loader 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.4.1.0.148229

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 (168) hide show
  1. package/.eslintrc.js +18 -21
  2. package/.mocharc.js +2 -2
  3. package/README.md +65 -44
  4. package/api-extractor.json +2 -2
  5. package/closeAndGetPendingLocalState.md +51 -0
  6. package/dist/audience.d.ts +0 -1
  7. package/dist/audience.d.ts.map +1 -1
  8. package/dist/audience.js.map +1 -1
  9. package/dist/catchUpMonitor.d.ts.map +1 -1
  10. package/dist/catchUpMonitor.js.map +1 -1
  11. package/dist/collabWindowTracker.d.ts.map +1 -1
  12. package/dist/collabWindowTracker.js.map +1 -1
  13. package/dist/connectionManager.d.ts +5 -5
  14. package/dist/connectionManager.d.ts.map +1 -1
  15. package/dist/connectionManager.js +107 -44
  16. package/dist/connectionManager.js.map +1 -1
  17. package/dist/connectionState.d.ts.map +1 -1
  18. package/dist/connectionState.js.map +1 -1
  19. package/dist/connectionStateHandler.d.ts +7 -7
  20. package/dist/connectionStateHandler.d.ts.map +1 -1
  21. package/dist/connectionStateHandler.js +50 -21
  22. package/dist/connectionStateHandler.js.map +1 -1
  23. package/dist/container.d.ts +64 -5
  24. package/dist/container.d.ts.map +1 -1
  25. package/dist/container.js +329 -137
  26. package/dist/container.js.map +1 -1
  27. package/dist/containerContext.d.ts +19 -8
  28. package/dist/containerContext.d.ts.map +1 -1
  29. package/dist/containerContext.js +58 -14
  30. package/dist/containerContext.js.map +1 -1
  31. package/dist/containerStorageAdapter.d.ts +41 -2
  32. package/dist/containerStorageAdapter.d.ts.map +1 -1
  33. package/dist/containerStorageAdapter.js +88 -14
  34. package/dist/containerStorageAdapter.js.map +1 -1
  35. package/dist/contracts.d.ts +3 -3
  36. package/dist/contracts.d.ts.map +1 -1
  37. package/dist/contracts.js.map +1 -1
  38. package/dist/deltaManager.d.ts +21 -8
  39. package/dist/deltaManager.d.ts.map +1 -1
  40. package/dist/deltaManager.js +112 -37
  41. package/dist/deltaManager.js.map +1 -1
  42. package/dist/deltaManagerProxy.d.ts +10 -22
  43. package/dist/deltaManagerProxy.d.ts.map +1 -1
  44. package/dist/deltaManagerProxy.js +14 -50
  45. package/dist/deltaManagerProxy.js.map +1 -1
  46. package/dist/deltaQueue.d.ts.map +1 -1
  47. package/dist/deltaQueue.js +4 -2
  48. package/dist/deltaQueue.js.map +1 -1
  49. package/dist/index.d.ts +4 -3
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -3
  52. package/dist/index.js.map +1 -1
  53. package/dist/loader.d.ts +13 -4
  54. package/dist/loader.d.ts.map +1 -1
  55. package/dist/loader.js +38 -24
  56. package/dist/loader.js.map +1 -1
  57. package/dist/packageVersion.d.ts +1 -1
  58. package/dist/packageVersion.js +1 -1
  59. package/dist/packageVersion.js.map +1 -1
  60. package/dist/protocol.d.ts.map +1 -1
  61. package/dist/protocol.js +2 -1
  62. package/dist/protocol.js.map +1 -1
  63. package/dist/protocolTreeDocumentStorageService.d.ts +6 -2
  64. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  65. package/dist/protocolTreeDocumentStorageService.js +7 -4
  66. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  67. package/dist/quorum.d.ts.map +1 -1
  68. package/dist/quorum.js.map +1 -1
  69. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  70. package/dist/retriableDocumentStorageService.js +6 -2
  71. package/dist/retriableDocumentStorageService.js.map +1 -1
  72. package/dist/utils.d.ts.map +1 -1
  73. package/dist/utils.js +8 -5
  74. package/dist/utils.js.map +1 -1
  75. package/lib/audience.d.ts +0 -1
  76. package/lib/audience.d.ts.map +1 -1
  77. package/lib/audience.js.map +1 -1
  78. package/lib/catchUpMonitor.d.ts.map +1 -1
  79. package/lib/catchUpMonitor.js.map +1 -1
  80. package/lib/collabWindowTracker.d.ts.map +1 -1
  81. package/lib/collabWindowTracker.js.map +1 -1
  82. package/lib/connectionManager.d.ts +5 -5
  83. package/lib/connectionManager.d.ts.map +1 -1
  84. package/lib/connectionManager.js +110 -47
  85. package/lib/connectionManager.js.map +1 -1
  86. package/lib/connectionState.d.ts.map +1 -1
  87. package/lib/connectionState.js.map +1 -1
  88. package/lib/connectionStateHandler.d.ts +7 -7
  89. package/lib/connectionStateHandler.d.ts.map +1 -1
  90. package/lib/connectionStateHandler.js +50 -21
  91. package/lib/connectionStateHandler.js.map +1 -1
  92. package/lib/container.d.ts +64 -5
  93. package/lib/container.d.ts.map +1 -1
  94. package/lib/container.js +336 -144
  95. package/lib/container.js.map +1 -1
  96. package/lib/containerContext.d.ts +19 -8
  97. package/lib/containerContext.d.ts.map +1 -1
  98. package/lib/containerContext.js +59 -15
  99. package/lib/containerContext.js.map +1 -1
  100. package/lib/containerStorageAdapter.d.ts +41 -2
  101. package/lib/containerStorageAdapter.d.ts.map +1 -1
  102. package/lib/containerStorageAdapter.js +86 -14
  103. package/lib/containerStorageAdapter.js.map +1 -1
  104. package/lib/contracts.d.ts +3 -3
  105. package/lib/contracts.d.ts.map +1 -1
  106. package/lib/contracts.js.map +1 -1
  107. package/lib/deltaManager.d.ts +21 -8
  108. package/lib/deltaManager.d.ts.map +1 -1
  109. package/lib/deltaManager.js +114 -39
  110. package/lib/deltaManager.js.map +1 -1
  111. package/lib/deltaManagerProxy.d.ts +10 -22
  112. package/lib/deltaManagerProxy.d.ts.map +1 -1
  113. package/lib/deltaManagerProxy.js +14 -50
  114. package/lib/deltaManagerProxy.js.map +1 -1
  115. package/lib/deltaQueue.d.ts.map +1 -1
  116. package/lib/deltaQueue.js +4 -2
  117. package/lib/deltaQueue.js.map +1 -1
  118. package/lib/index.d.ts +4 -3
  119. package/lib/index.d.ts.map +1 -1
  120. package/lib/index.js +2 -2
  121. package/lib/index.js.map +1 -1
  122. package/lib/loader.d.ts +13 -4
  123. package/lib/loader.d.ts.map +1 -1
  124. package/lib/loader.js +37 -24
  125. package/lib/loader.js.map +1 -1
  126. package/lib/packageVersion.d.ts +1 -1
  127. package/lib/packageVersion.js +1 -1
  128. package/lib/packageVersion.js.map +1 -1
  129. package/lib/protocol.d.ts.map +1 -1
  130. package/lib/protocol.js +2 -1
  131. package/lib/protocol.js.map +1 -1
  132. package/lib/protocolTreeDocumentStorageService.d.ts +6 -2
  133. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  134. package/lib/protocolTreeDocumentStorageService.js +7 -4
  135. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  136. package/lib/quorum.d.ts.map +1 -1
  137. package/lib/quorum.js.map +1 -1
  138. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  139. package/lib/retriableDocumentStorageService.js +6 -2
  140. package/lib/retriableDocumentStorageService.js.map +1 -1
  141. package/lib/utils.d.ts.map +1 -1
  142. package/lib/utils.js +8 -5
  143. package/lib/utils.js.map +1 -1
  144. package/package.json +67 -56
  145. package/prettier.config.cjs +1 -1
  146. package/src/audience.ts +51 -46
  147. package/src/catchUpMonitor.ts +39 -37
  148. package/src/collabWindowTracker.ts +75 -70
  149. package/src/connectionManager.ts +1040 -941
  150. package/src/connectionState.ts +19 -19
  151. package/src/connectionStateHandler.ts +557 -463
  152. package/src/container.ts +2147 -1784
  153. package/src/containerContext.ts +417 -345
  154. package/src/containerStorageAdapter.ts +268 -154
  155. package/src/contracts.ts +155 -153
  156. package/src/deltaManager.ts +1074 -945
  157. package/src/deltaManagerProxy.ts +88 -137
  158. package/src/deltaQueue.ts +155 -151
  159. package/src/index.ts +13 -17
  160. package/src/loader.ts +434 -427
  161. package/src/packageVersion.ts +1 -1
  162. package/src/protocol.ts +93 -87
  163. package/src/protocolTreeDocumentStorageService.ts +34 -34
  164. package/src/quorum.ts +34 -34
  165. package/src/retriableDocumentStorageService.ts +118 -102
  166. package/src/utils.ts +93 -83
  167. package/tsconfig.esnext.json +6 -6
  168. package/tsconfig.json +8 -12
@@ -4,129 +4,176 @@
4
4
  */
5
5
 
6
6
  import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert } from "@fluidframework/common-utils";
7
+ import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
8
8
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
9
9
  import {
10
- FetchSource,
11
- IDocumentService,
12
- IDocumentStorageService,
13
- IDocumentStorageServicePolicies,
14
- ISummaryContext,
10
+ FetchSource,
11
+ IDocumentService,
12
+ IDocumentStorageService,
13
+ IDocumentStorageServicePolicies,
14
+ ISummaryContext,
15
15
  } from "@fluidframework/driver-definitions";
16
16
  import { UsageError } from "@fluidframework/driver-utils";
17
17
  import {
18
- ICreateBlobResponse,
19
- ISnapshotTree,
20
- ISummaryHandle,
21
- ISummaryTree,
22
- IVersion,
18
+ ICreateBlobResponse,
19
+ ISnapshotTree,
20
+ ISummaryHandle,
21
+ ISummaryTree,
22
+ IVersion,
23
23
  } from "@fluidframework/protocol-definitions";
24
24
  import { IDetachedBlobStorage } from "./loader";
25
25
  import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
26
26
  import { RetriableDocumentStorageService } from "./retriableDocumentStorageService";
27
27
 
28
+ /**
29
+ * Stringified blobs from a summary/snapshot tree.
30
+ * @deprecated this is an internal interface and will not longer be exported in future versions
31
+ * @internal
32
+ */
33
+ export interface ISerializableBlobContents {
34
+ [id: string]: string;
35
+ }
36
+
28
37
  /**
29
38
  * This class wraps the actual storage and make sure no wrong apis are called according to
30
39
  * container attach state.
31
40
  */
32
41
  export class ContainerStorageAdapter implements IDocumentStorageService, IDisposable {
33
- private readonly blobContents: { [id: string]: ArrayBufferLike; } = {};
34
- private _storageService: IDocumentStorageService & Partial<IDisposable>;
35
-
36
- constructor(
37
- detachedBlobStorage: IDetachedBlobStorage | undefined,
38
- private readonly logger: ITelemetryLogger,
39
- private readonly captureProtocolSummary?: () => ISummaryTree,
40
- ) {
41
- this._storageService = new BlobOnlyStorage(detachedBlobStorage, logger);
42
- }
43
-
44
- disposed: boolean = false;
45
- dispose(error?: Error): void {
46
- this._storageService?.dispose?.(error);
47
- this.disposed = true;
48
- }
49
-
50
- public async connectToService(service: IDocumentService): Promise<void> {
51
- if (!(this._storageService instanceof BlobOnlyStorage)) {
52
- return;
53
- }
54
-
55
- const storageService = await service.connectToStorage();
56
- const retriableStorage = this._storageService =
57
- new RetriableDocumentStorageService(
58
- storageService,
59
- this.logger);
60
-
61
- if (this.captureProtocolSummary !== undefined) {
62
- this.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
63
- this._storageService =
64
- new ProtocolTreeStorageService(retriableStorage, this.captureProtocolSummary);
65
- }
66
-
67
- // ensure we did not lose that policy in the process of wrapping
68
- assert(storageService.policies?.minBlobSize === this._storageService.policies?.minBlobSize,
69
- 0x0e0 /* "lost minBlobSize policy" */);
70
- }
71
-
72
- public loadSnapshotForRehydratingContainer(snapshotTree: ISnapshotTreeWithBlobContents) {
73
- this.getBlobContents(snapshotTree);
74
- }
75
-
76
- private getBlobContents(snapshotTree: ISnapshotTreeWithBlobContents) {
77
- for (const [id, value] of Object.entries(snapshotTree.blobsContents)) {
78
- this.blobContents[id] = value;
79
- }
80
- for (const [_, tree] of Object.entries(snapshotTree.trees)) {
81
- this.getBlobContents(tree);
82
- }
83
- }
84
-
85
- public get policies(): IDocumentStorageServicePolicies | undefined {
86
- // back-compat 0.40 containerRuntime requests policies even in detached container if storage is present
87
- // and storage is always present in >=0.41.
88
- try {
89
- return this._storageService.policies;
90
- } catch (e) {}
91
- return undefined;
92
- }
93
-
94
- public get repositoryUrl(): string {
95
- return this._storageService.repositoryUrl;
96
- }
97
-
98
- public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {
99
- return this._storageService.getSnapshotTree(version, scenarioName);
100
- }
101
-
102
- public async readBlob(id: string): Promise<ArrayBufferLike> {
103
- const blob = this.blobContents[id];
104
- if (blob !== undefined) {
105
- return blob;
106
- }
107
- return this._storageService.readBlob(id);
108
- }
109
-
110
- public async getVersions(
111
- versionId: string | null,
112
- count: number,
113
- scenarioName?: string,
114
- fetchSource?: FetchSource,
115
- ): Promise<IVersion[]> {
116
- return this._storageService.getVersions(versionId, count, scenarioName, fetchSource);
117
- }
118
-
119
- public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
120
- return this._storageService.uploadSummaryWithContext(summary, context);
121
- }
122
-
123
- public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
124
- return this._storageService.downloadSummary(handle);
125
- }
126
-
127
- public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
128
- return this._storageService.createBlob(file);
129
- }
42
+ private _storageService: IDocumentStorageService & Partial<IDisposable>;
43
+
44
+ private _summarizeProtocolTree: boolean | undefined;
45
+ /**
46
+ * Whether the adapter will enforce sending combined summary trees.
47
+ */
48
+ public get summarizeProtocolTree() {
49
+ return this._summarizeProtocolTree === true;
50
+ }
51
+
52
+ /**
53
+ * An adapter that ensures we're using detachedBlobStorage up until we connect to a real service, and then
54
+ * after connecting to a real service augments it with retry and combined summary tree enforcement.
55
+ * @param detachedBlobStorage - The detached blob storage to use up until we connect to a real service
56
+ * @param logger - Telemetry logger
57
+ * @param addProtocolSummaryIfMissing - a callback to permit the container to inspect the summary we're about to
58
+ * upload, and fix it up with a protocol tree if needed
59
+ * @param forceEnableSummarizeProtocolTree - Enforce uploading a protocol summary regardless of the service's policy
60
+ */
61
+ public constructor(
62
+ detachedBlobStorage: IDetachedBlobStorage | undefined,
63
+ private readonly logger: ITelemetryLogger,
64
+ /**
65
+ * ArrayBufferLikes or utf8 encoded strings, containing blobs from a snapshot
66
+ */
67
+ private readonly blobContents: { [id: string]: ArrayBufferLike | string } = {},
68
+ private readonly addProtocolSummaryIfMissing: (summaryTree: ISummaryTree) => ISummaryTree,
69
+ forceEnableSummarizeProtocolTree: boolean | undefined,
70
+ ) {
71
+ this._storageService = new BlobOnlyStorage(detachedBlobStorage, logger);
72
+ this._summarizeProtocolTree = forceEnableSummarizeProtocolTree;
73
+ }
74
+
75
+ disposed: boolean = false;
76
+ dispose(error?: Error): void {
77
+ this._storageService?.dispose?.(error);
78
+ this.disposed = true;
79
+ }
80
+
81
+ public async connectToService(service: IDocumentService): Promise<void> {
82
+ if (!(this._storageService instanceof BlobOnlyStorage)) {
83
+ return;
84
+ }
85
+
86
+ const storageService = await service.connectToStorage();
87
+ const retriableStorage = (this._storageService = new RetriableDocumentStorageService(
88
+ storageService,
89
+ this.logger,
90
+ ));
91
+
92
+ this._summarizeProtocolTree =
93
+ this._summarizeProtocolTree ?? service.policies?.summarizeProtocolTree;
94
+ if (this.summarizeProtocolTree) {
95
+ this.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
96
+ this._storageService = new ProtocolTreeStorageService(
97
+ retriableStorage,
98
+ this.addProtocolSummaryIfMissing,
99
+ );
100
+ }
101
+
102
+ // ensure we did not lose that policy in the process of wrapping
103
+ assert(
104
+ storageService.policies?.minBlobSize === this._storageService.policies?.minBlobSize,
105
+ 0x0e0 /* "lost minBlobSize policy" */,
106
+ );
107
+ }
108
+
109
+ public loadSnapshotForRehydratingContainer(snapshotTree: ISnapshotTreeWithBlobContents) {
110
+ this.getBlobContents(snapshotTree);
111
+ }
112
+
113
+ private getBlobContents(snapshotTree: ISnapshotTreeWithBlobContents) {
114
+ for (const [id, value] of Object.entries(snapshotTree.blobsContents)) {
115
+ this.blobContents[id] = value;
116
+ }
117
+ for (const [_, tree] of Object.entries(snapshotTree.trees)) {
118
+ this.getBlobContents(tree);
119
+ }
120
+ }
121
+
122
+ public get policies(): IDocumentStorageServicePolicies | undefined {
123
+ // back-compat 0.40 containerRuntime requests policies even in detached container if storage is present
124
+ // and storage is always present in >=0.41.
125
+ try {
126
+ return this._storageService.policies;
127
+ } catch (e) {}
128
+ return undefined;
129
+ }
130
+
131
+ public get repositoryUrl(): string {
132
+ return this._storageService.repositoryUrl;
133
+ }
134
+
135
+ public async getSnapshotTree(
136
+ version?: IVersion,
137
+ scenarioName?: string,
138
+ ): Promise<ISnapshotTree | null> {
139
+ return this._storageService.getSnapshotTree(version, scenarioName);
140
+ }
141
+
142
+ public async readBlob(id: string): Promise<ArrayBufferLike> {
143
+ const maybeBlob = this.blobContents[id];
144
+ if (maybeBlob !== undefined) {
145
+ if (typeof maybeBlob === "string") {
146
+ const blob = stringToBuffer(maybeBlob, "utf8");
147
+ return blob;
148
+ }
149
+ return maybeBlob;
150
+ }
151
+ return this._storageService.readBlob(id);
152
+ }
153
+
154
+ public async getVersions(
155
+ versionId: string | null,
156
+ count: number,
157
+ scenarioName?: string,
158
+ fetchSource?: FetchSource,
159
+ ): Promise<IVersion[]> {
160
+ return this._storageService.getVersions(versionId, count, scenarioName, fetchSource);
161
+ }
162
+
163
+ public async uploadSummaryWithContext(
164
+ summary: ISummaryTree,
165
+ context: ISummaryContext,
166
+ ): Promise<string> {
167
+ return this._storageService.uploadSummaryWithContext(summary, context);
168
+ }
169
+
170
+ public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
171
+ return this._storageService.downloadSummary(handle);
172
+ }
173
+
174
+ public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
175
+ return this._storageService.createBlob(file);
176
+ }
130
177
  }
131
178
 
132
179
  /**
@@ -134,50 +181,117 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
134
181
  * blobs in detached containers.
135
182
  */
136
183
  class BlobOnlyStorage implements IDocumentStorageService {
137
- constructor(
138
- private readonly detachedStorage: IDetachedBlobStorage | undefined,
139
- private readonly logger: ITelemetryLogger,
140
- ) { }
141
-
142
- public async createBlob(content: ArrayBufferLike): Promise<ICreateBlobResponse> {
143
- return this.verifyStorage().createBlob(content);
144
- }
145
-
146
- public async readBlob(blobId: string): Promise<ArrayBufferLike> {
147
- return this.verifyStorage().readBlob(blobId);
148
- }
149
-
150
- private verifyStorage(): IDetachedBlobStorage {
151
- if (this.detachedStorage === undefined) {
152
- throw new UsageError("Real storage calls not allowed in Unattached container");
153
- }
154
- return this.detachedStorage;
155
- }
156
-
157
- public get policies(): IDocumentStorageServicePolicies | undefined {
158
- return this.notCalled();
159
- }
160
-
161
- public get repositoryUrl(): string {
162
- return this.notCalled();
163
- }
164
-
165
- /* eslint-disable @typescript-eslint/unbound-method */
166
- public getSnapshotTree: () => Promise<ISnapshotTree | null> = this.notCalled;
167
- public getVersions: () => Promise<IVersion[]> = this.notCalled;
168
- public write: () => Promise<IVersion> = this.notCalled;
169
- public uploadSummaryWithContext: () => Promise<string> = this.notCalled;
170
- public downloadSummary: () => Promise<ISummaryTree> = this.notCalled;
171
- /* eslint-enable @typescript-eslint/unbound-method */
172
-
173
- private notCalled(): never {
174
- this.verifyStorage();
175
- try {
176
- // some browsers may not populate stack unless exception is thrown
177
- throw new Error("BlobOnlyStorage not implemented method used");
178
- } catch (err) {
179
- this.logger.sendTelemetryEvent({ eventName: "BlobOnlyStorageWrongCall" }, err);
180
- throw err;
181
- }
182
- }
184
+ constructor(
185
+ private readonly detachedStorage: IDetachedBlobStorage | undefined,
186
+ private readonly logger: ITelemetryLogger,
187
+ ) {}
188
+
189
+ public async createBlob(content: ArrayBufferLike): Promise<ICreateBlobResponse> {
190
+ return this.verifyStorage().createBlob(content);
191
+ }
192
+
193
+ public async readBlob(blobId: string): Promise<ArrayBufferLike> {
194
+ return this.verifyStorage().readBlob(blobId);
195
+ }
196
+
197
+ private verifyStorage(): IDetachedBlobStorage {
198
+ if (this.detachedStorage === undefined) {
199
+ throw new UsageError("Real storage calls not allowed in Unattached container");
200
+ }
201
+ return this.detachedStorage;
202
+ }
203
+
204
+ public get policies(): IDocumentStorageServicePolicies | undefined {
205
+ return this.notCalled();
206
+ }
207
+
208
+ public get repositoryUrl(): string {
209
+ return this.notCalled();
210
+ }
211
+
212
+ /* eslint-disable @typescript-eslint/unbound-method */
213
+ public getSnapshotTree: () => Promise<ISnapshotTree | null> = this.notCalled;
214
+ public getVersions: () => Promise<IVersion[]> = this.notCalled;
215
+ public write: () => Promise<IVersion> = this.notCalled;
216
+ public uploadSummaryWithContext: () => Promise<string> = this.notCalled;
217
+ public downloadSummary: () => Promise<ISummaryTree> = this.notCalled;
218
+ /* eslint-enable @typescript-eslint/unbound-method */
219
+
220
+ private notCalled(): never {
221
+ this.verifyStorage();
222
+ try {
223
+ // some browsers may not populate stack unless exception is thrown
224
+ throw new Error("BlobOnlyStorage not implemented method used");
225
+ } catch (err) {
226
+ this.logger.sendTelemetryEvent({ eventName: "BlobOnlyStorageWrongCall" }, err);
227
+ throw err;
228
+ }
229
+ }
230
+ }
231
+
232
+ // runtime will write a tree to the summary containing only "attachment" type entries
233
+ // which reference attachment blobs by ID. However, some drivers do not support this type
234
+ // and will convert them to "blob" type entries. We want to avoid saving these to reduce
235
+ // the size of stashed change blobs.
236
+ const blobsTreeName = ".blobs";
237
+
238
+ /**
239
+ * Get blob contents of a snapshot tree from storage (or, ideally, cache)
240
+ */
241
+ export async function getBlobContentsFromTree(
242
+ snapshot: ISnapshotTree,
243
+ storage: IDocumentStorageService,
244
+ ): Promise<ISerializableBlobContents> {
245
+ const blobs = {};
246
+ await getBlobContentsFromTreeCore(snapshot, blobs, storage);
247
+ return blobs;
248
+ }
249
+
250
+ async function getBlobContentsFromTreeCore(
251
+ tree: ISnapshotTree,
252
+ blobs: ISerializableBlobContents,
253
+ storage: IDocumentStorageService,
254
+ root = true,
255
+ ) {
256
+ const treePs: Promise<any>[] = [];
257
+ for (const [key, subTree] of Object.entries(tree.trees)) {
258
+ if (!root || key !== blobsTreeName) {
259
+ treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage, false));
260
+ }
261
+ }
262
+ for (const id of Object.values(tree.blobs)) {
263
+ const blob = await storage.readBlob(id);
264
+ // ArrayBufferLike will not survive JSON.stringify()
265
+ blobs[id] = bufferToString(blob, "utf8");
266
+ }
267
+ return Promise.all(treePs);
268
+ }
269
+
270
+ /**
271
+ * Extract blob contents from a snapshot tree with blob contents
272
+ */
273
+ export function getBlobContentsFromTreeWithBlobContents(
274
+ snapshot: ISnapshotTreeWithBlobContents,
275
+ ): ISerializableBlobContents {
276
+ const blobs = {};
277
+ getBlobContentsFromTreeWithBlobContentsCore(snapshot, blobs);
278
+ return blobs;
279
+ }
280
+
281
+ function getBlobContentsFromTreeWithBlobContentsCore(
282
+ tree: ISnapshotTreeWithBlobContents,
283
+ blobs: ISerializableBlobContents,
284
+ root = true,
285
+ ) {
286
+ for (const [key, subTree] of Object.entries(tree.trees)) {
287
+ if (!root || key !== blobsTreeName) {
288
+ getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
289
+ }
290
+ }
291
+ for (const id of Object.values(tree.blobs)) {
292
+ const blob = tree.blobsContents[id];
293
+ assert(blob !== undefined, 0x2ec /* "Blob must be present in blobsContents" */);
294
+ // ArrayBufferLike will not survive JSON.stringify()
295
+ blobs[id] = bufferToString(blob, "utf8");
296
+ }
183
297
  }