@fluidframework/driver-utils 2.0.0-internal.5.1.1 → 2.0.0-internal.5.3.0

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 (138) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/adapters/compression/compressionTypes.d.ts +17 -0
  3. package/dist/adapters/compression/compressionTypes.d.ts.map +1 -0
  4. package/dist/adapters/compression/compressionTypes.js +17 -0
  5. package/dist/adapters/compression/compressionTypes.js.map +1 -0
  6. package/dist/adapters/compression/documentServiceCompressionAdapter.d.ts +13 -0
  7. package/dist/adapters/compression/documentServiceCompressionAdapter.d.ts.map +1 -0
  8. package/dist/adapters/compression/documentServiceCompressionAdapter.js +23 -0
  9. package/dist/adapters/compression/documentServiceCompressionAdapter.js.map +1 -0
  10. package/dist/adapters/compression/documentServiceFactoryCompressionAdapter.d.ts +16 -0
  11. package/dist/adapters/compression/documentServiceFactoryCompressionAdapter.d.ts.map +1 -0
  12. package/dist/adapters/compression/documentServiceFactoryCompressionAdapter.js +36 -0
  13. package/dist/adapters/compression/documentServiceFactoryCompressionAdapter.js.map +1 -0
  14. package/dist/adapters/compression/index.d.ts +8 -0
  15. package/dist/adapters/compression/index.d.ts.map +1 -0
  16. package/dist/adapters/compression/index.js +15 -0
  17. package/dist/adapters/compression/index.js.map +1 -0
  18. package/dist/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.d.ts +161 -0
  19. package/dist/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.d.ts.map +1 -0
  20. package/dist/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.js +365 -0
  21. package/dist/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.js.map +1 -0
  22. package/dist/adapters/compression/summaryblob/index.d.ts +6 -0
  23. package/dist/adapters/compression/summaryblob/index.d.ts.map +1 -0
  24. package/dist/adapters/compression/summaryblob/index.js +11 -0
  25. package/dist/adapters/compression/summaryblob/index.js.map +1 -0
  26. package/dist/adapters/index.d.ts +7 -0
  27. package/dist/adapters/index.d.ts.map +1 -0
  28. package/dist/adapters/index.js +14 -0
  29. package/dist/adapters/index.js.map +1 -0
  30. package/dist/adapters/predefinedAdapters.d.ts +20 -0
  31. package/dist/adapters/predefinedAdapters.d.ts.map +1 -0
  32. package/dist/adapters/predefinedAdapters.js +51 -0
  33. package/dist/adapters/predefinedAdapters.js.map +1 -0
  34. package/dist/documentServiceFactoryProxy.d.ts +19 -0
  35. package/dist/documentServiceFactoryProxy.d.ts.map +1 -0
  36. package/dist/documentServiceFactoryProxy.js +27 -0
  37. package/dist/documentServiceFactoryProxy.js.map +1 -0
  38. package/dist/documentServiceProxy.d.ts +21 -0
  39. package/dist/documentServiceProxy.d.ts.map +1 -0
  40. package/dist/documentServiceProxy.js +36 -0
  41. package/dist/documentServiceProxy.js.map +1 -0
  42. package/dist/documentStorageServiceProxy.d.ts.map +1 -1
  43. package/dist/documentStorageServiceProxy.js +1 -0
  44. package/dist/documentStorageServiceProxy.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/network.d.ts +1 -1
  50. package/dist/network.d.ts.map +1 -1
  51. package/dist/network.js.map +1 -1
  52. package/dist/networkUtils.d.ts +1 -1
  53. package/dist/networkUtils.d.ts.map +1 -1
  54. package/dist/networkUtils.js.map +1 -1
  55. package/dist/packageVersion.d.ts +1 -1
  56. package/dist/packageVersion.js +1 -1
  57. package/dist/packageVersion.js.map +1 -1
  58. package/dist/parallelRequests.d.ts +1 -1
  59. package/dist/parallelRequests.d.ts.map +1 -1
  60. package/dist/parallelRequests.js +4 -3
  61. package/dist/parallelRequests.js.map +1 -1
  62. package/lib/adapters/compression/compressionTypes.d.ts +17 -0
  63. package/lib/adapters/compression/compressionTypes.d.ts.map +1 -0
  64. package/lib/adapters/compression/compressionTypes.js +14 -0
  65. package/lib/adapters/compression/compressionTypes.js.map +1 -0
  66. package/lib/adapters/compression/documentServiceCompressionAdapter.d.ts +13 -0
  67. package/lib/adapters/compression/documentServiceCompressionAdapter.d.ts.map +1 -0
  68. package/lib/adapters/compression/documentServiceCompressionAdapter.js +19 -0
  69. package/lib/adapters/compression/documentServiceCompressionAdapter.js.map +1 -0
  70. package/lib/adapters/compression/documentServiceFactoryCompressionAdapter.d.ts +16 -0
  71. package/lib/adapters/compression/documentServiceFactoryCompressionAdapter.d.ts.map +1 -0
  72. package/lib/adapters/compression/documentServiceFactoryCompressionAdapter.js +32 -0
  73. package/lib/adapters/compression/documentServiceFactoryCompressionAdapter.js.map +1 -0
  74. package/lib/adapters/compression/index.d.ts +8 -0
  75. package/lib/adapters/compression/index.d.ts.map +1 -0
  76. package/lib/adapters/compression/index.js +8 -0
  77. package/lib/adapters/compression/index.js.map +1 -0
  78. package/lib/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.d.ts +161 -0
  79. package/lib/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.d.ts.map +1 -0
  80. package/lib/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.js +361 -0
  81. package/lib/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.js.map +1 -0
  82. package/lib/adapters/compression/summaryblob/index.d.ts +6 -0
  83. package/lib/adapters/compression/summaryblob/index.d.ts.map +1 -0
  84. package/lib/adapters/compression/summaryblob/index.js +6 -0
  85. package/lib/adapters/compression/summaryblob/index.js.map +1 -0
  86. package/lib/adapters/index.d.ts +7 -0
  87. package/lib/adapters/index.d.ts.map +1 -0
  88. package/lib/adapters/index.js +7 -0
  89. package/lib/adapters/index.js.map +1 -0
  90. package/lib/adapters/predefinedAdapters.d.ts +20 -0
  91. package/lib/adapters/predefinedAdapters.d.ts.map +1 -0
  92. package/lib/adapters/predefinedAdapters.js +46 -0
  93. package/lib/adapters/predefinedAdapters.js.map +1 -0
  94. package/lib/documentServiceFactoryProxy.d.ts +19 -0
  95. package/lib/documentServiceFactoryProxy.d.ts.map +1 -0
  96. package/lib/documentServiceFactoryProxy.js +23 -0
  97. package/lib/documentServiceFactoryProxy.js.map +1 -0
  98. package/lib/documentServiceProxy.d.ts +21 -0
  99. package/lib/documentServiceProxy.d.ts.map +1 -0
  100. package/lib/documentServiceProxy.js +32 -0
  101. package/lib/documentServiceProxy.js.map +1 -0
  102. package/lib/documentStorageServiceProxy.d.ts.map +1 -1
  103. package/lib/documentStorageServiceProxy.js +1 -0
  104. package/lib/documentStorageServiceProxy.js.map +1 -1
  105. package/lib/index.d.ts +1 -0
  106. package/lib/index.d.ts.map +1 -1
  107. package/lib/index.js +1 -0
  108. package/lib/index.js.map +1 -1
  109. package/lib/network.d.ts +1 -1
  110. package/lib/network.d.ts.map +1 -1
  111. package/lib/network.js.map +1 -1
  112. package/lib/networkUtils.d.ts +1 -1
  113. package/lib/networkUtils.d.ts.map +1 -1
  114. package/lib/networkUtils.js.map +1 -1
  115. package/lib/packageVersion.d.ts +1 -1
  116. package/lib/packageVersion.js +1 -1
  117. package/lib/packageVersion.js.map +1 -1
  118. package/lib/parallelRequests.d.ts +1 -1
  119. package/lib/parallelRequests.d.ts.map +1 -1
  120. package/lib/parallelRequests.js +4 -3
  121. package/lib/parallelRequests.js.map +1 -1
  122. package/package.json +11 -15
  123. package/src/adapters/compression/compressionTypes.ts +19 -0
  124. package/src/adapters/compression/documentServiceCompressionAdapter.ts +25 -0
  125. package/src/adapters/compression/documentServiceFactoryCompressionAdapter.ts +62 -0
  126. package/src/adapters/compression/index.ts +12 -0
  127. package/src/adapters/compression/summaryblob/documentStorageServiceSummaryBlobCompressionAdapter.ts +446 -0
  128. package/src/adapters/compression/summaryblob/index.ts +9 -0
  129. package/src/adapters/index.ts +13 -0
  130. package/src/adapters/predefinedAdapters.ts +71 -0
  131. package/src/documentServiceFactoryProxy.ts +47 -0
  132. package/src/documentServiceProxy.ts +46 -0
  133. package/src/documentStorageServiceProxy.ts +1 -0
  134. package/src/index.ts +5 -0
  135. package/src/network.ts +1 -1
  136. package/src/networkUtils.ts +1 -1
  137. package/src/packageVersion.ts +1 -1
  138. package/src/parallelRequests.ts +5 -4
@@ -0,0 +1,446 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IsoBuffer, assert } from "@fluidframework/common-utils";
7
+ import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
8
+ import {
9
+ ISnapshotTree,
10
+ ISummaryBlob,
11
+ ISummaryHandle,
12
+ ISummaryTree,
13
+ IVersion,
14
+ SummaryObject,
15
+ SummaryType,
16
+ } from "@fluidframework/protocol-definitions";
17
+ import { compress, decompress } from "lz4js";
18
+ import { DocumentStorageServiceProxy } from "../../../documentStorageServiceProxy";
19
+ import { ICompressionStorageConfig, SummaryCompressionAlgorithm } from "../";
20
+
21
+ export const blobHeadersBlobName = ".metadata.blobHeaders";
22
+ const metadataBlobName = ".metadata";
23
+
24
+ /**
25
+ * This class is a proxy for the IDocumentStorageService that compresses and decompresses blobs in the summary.
26
+ * The identification of the compressed blobs is done by adding a compression markup blob to the summary.
27
+ * Even if the markup blob is present, it does not mean that all blobs are compressed. The blob,
28
+ * which is compressed also contain the compression algorithm enumerated value from the
29
+ * SummaryCompressionAlgorithm enumeration in the first byte . If the blob is not
30
+ * commpressed, it contains the first byte equals to SummaryCompressionAlgorithm.None .
31
+ * In case, the markup blob is present, it is expected that the first byte of the markup blob
32
+ * will contain the info about the compression. If the first byte is not present, it is assumed
33
+ * that the compression is not enabled and no first prefix byte is present in the blobs.
34
+ */
35
+ export class DocumentStorageServiceCompressionAdapter extends DocumentStorageServiceProxy {
36
+ private _isCompressionEnabled: boolean = false;
37
+
38
+ constructor(
39
+ service: IDocumentStorageService,
40
+ private readonly _config: ICompressionStorageConfig,
41
+ ) {
42
+ super(service);
43
+ }
44
+
45
+ public get service(): IDocumentStorageService {
46
+ return this.internalStorageService;
47
+ }
48
+
49
+ /**
50
+ * This method returns true if there is a compression markup byte in the blob, otherwise false.
51
+ * @param blob - The blob to compress.
52
+ * @returns - True if there is a compression markup byte in the blob, otherwise false.
53
+ */
54
+ private static hasPrefix(blob: ArrayBufferLike): boolean {
55
+ const firstByte = IsoBuffer.from(blob)[0];
56
+ // eslint-disable-next-line no-bitwise
57
+ return (firstByte & 0xf0) === 0xb0;
58
+ }
59
+
60
+ /**
61
+ * This method reads the first byte from the given blob and maps that byte to the compression algorithm.
62
+ * @param blob - The maybe compressed blob.
63
+ */
64
+ private static readAlgorithmFromBlob(blob: ArrayBufferLike): number {
65
+ return !this.hasPrefix(blob)
66
+ ? SummaryCompressionAlgorithm.None
67
+ : // eslint-disable-next-line no-bitwise
68
+ IsoBuffer.from(blob)[0] & 0x0f;
69
+ }
70
+
71
+ /**
72
+ * This method writes the given algorithm to the blob as the first byte.
73
+ * @param blob - The blob to write the algorithm to.
74
+ * @param algorithm - The algorithm to write.
75
+ * @returns - The blob with the algorithm as the first byte.
76
+ */
77
+ private static writeAlgorithmToBlob(blob: ArrayBufferLike, algorithm: number): ArrayBufferLike {
78
+ if (algorithm === SummaryCompressionAlgorithm.None) {
79
+ const firstByte = IsoBuffer.from(blob)[0];
80
+ // eslint-disable-next-line no-bitwise
81
+ if ((firstByte & 0xf0) !== 0xb0) {
82
+ return blob;
83
+ }
84
+ }
85
+ assert(algorithm < 0x10, 0x6f5 /* Algorithm should be less than 0x10 */);
86
+ const blobView = new Uint8Array(blob);
87
+ const blobLength = blobView.length;
88
+ const newBlob = new Uint8Array(blobLength + 1);
89
+ // eslint-disable-next-line no-bitwise
90
+ const prefix = 0xb0 | algorithm;
91
+ newBlob[0] = prefix;
92
+ newBlob.set(blobView, 1);
93
+ return IsoBuffer.from(newBlob);
94
+ }
95
+
96
+ /**
97
+ * This method removes the algorithm markup prefix from the blob (1 byte)
98
+ * @param blob - The blob to remove the prefix from.
99
+ * @returns - The blob without the prefix.
100
+ */
101
+ private static removePrefixFromBlobIfPresent(blob: ArrayBufferLike): ArrayBufferLike {
102
+ const blobView = new Uint8Array(blob);
103
+ return this.hasPrefix(blob) ? IsoBuffer.from(blobView.subarray(1)) : blob;
104
+ }
105
+
106
+ /**
107
+ * This method converts the given argument to Uint8Array. If the parameter is already Uint8Array,
108
+ * it is just returned as is. If the parameter is string, it is converted to Uint8Array using
109
+ * TextEncoder.
110
+ * @param input - The input to convert to Uint8Array.
111
+ * @returns - The Uint8Array representation of the input.
112
+ */
113
+ private static toBinaryArray(input: string | Uint8Array): Uint8Array {
114
+ return typeof input === "string" ? new TextEncoder().encode(input) : input;
115
+ }
116
+
117
+ /**
118
+ * This method encodes the blob inside the given summary object of the SummaryType.Blob type using the given config
119
+ * containing the compression algorithm.
120
+ * @param input - The summary object to encode.
121
+ * @param config - The config containing the compression algorithm.
122
+ * @returns - The summary object with the encoded blob.
123
+ */
124
+ private static readonly blobEncoder = (
125
+ input: SummaryObject,
126
+ config: ICompressionStorageConfig,
127
+ ): SummaryObject => {
128
+ if (input.type === SummaryType.Blob) {
129
+ const summaryBlob: ISummaryBlob = input;
130
+ const original: ArrayBufferLike =
131
+ DocumentStorageServiceCompressionAdapter.toBinaryArray(summaryBlob.content);
132
+ const processed: ArrayBufferLike = DocumentStorageServiceCompressionAdapter.encodeBlob(
133
+ original,
134
+ config,
135
+ );
136
+ const newSummaryBlob = {
137
+ type: SummaryType.Blob,
138
+ content: IsoBuffer.from(processed),
139
+ };
140
+ return newSummaryBlob;
141
+ } else {
142
+ return input;
143
+ }
144
+ };
145
+
146
+ /**
147
+ * This method decodes the blob inside the given summary object of the SummaryType.Blob type.
148
+ * @param input - The summary object to decode.
149
+ * @returns - The summary object with the decoded blob.
150
+ */
151
+ private static readonly blobDecoder = (input: SummaryObject): SummaryObject => {
152
+ if (input.type === SummaryType.Blob) {
153
+ const summaryBlob: ISummaryBlob = input;
154
+ const original: Uint8Array = DocumentStorageServiceCompressionAdapter.toBinaryArray(
155
+ summaryBlob.content,
156
+ );
157
+ const processed: ArrayBufferLike =
158
+ DocumentStorageServiceCompressionAdapter.decodeBlob(original);
159
+ const newSummaryBlob = {
160
+ type: SummaryType.Blob,
161
+ content: IsoBuffer.from(processed),
162
+ };
163
+ return newSummaryBlob;
164
+ } else {
165
+ return input;
166
+ }
167
+ };
168
+
169
+ /**
170
+ * This method encodes the given blob according to the given config.
171
+ * @param file - The blob to encode.
172
+ * @param config - The config to use for encoding.
173
+ * @returns - The encoded blob.
174
+ */
175
+ private static encodeBlob(
176
+ file: ArrayBufferLike,
177
+ config: ICompressionStorageConfig,
178
+ ): ArrayBufferLike {
179
+ let maybeCompressed: ArrayBufferLike;
180
+ let algorithm = config.algorithm;
181
+ if (new Uint8Array(file).length < config.minSizeToCompress) {
182
+ maybeCompressed = file;
183
+ algorithm = SummaryCompressionAlgorithm.None;
184
+ } else if (algorithm === SummaryCompressionAlgorithm.None) {
185
+ maybeCompressed = file;
186
+ } else if (algorithm === SummaryCompressionAlgorithm.LZ4) {
187
+ const compressed = compress(file) as ArrayBufferLike;
188
+ maybeCompressed = compressed;
189
+ } else {
190
+ throw new Error(`Unknown Algorithm ${config.algorithm}`);
191
+ }
192
+ maybeCompressed = DocumentStorageServiceCompressionAdapter.writeAlgorithmToBlob(
193
+ maybeCompressed,
194
+ algorithm,
195
+ );
196
+ return maybeCompressed;
197
+ }
198
+
199
+ /**
200
+ * This method decodes the given blob.
201
+ * @param file - The blob to decode.
202
+ * @returns - The decoded blob.
203
+ */
204
+ private static decodeBlob(file: ArrayBufferLike): ArrayBufferLike {
205
+ let decompressed: ArrayBufferLike;
206
+ let originalBlob;
207
+ let algorithm;
208
+ if (this.hasPrefix(file)) {
209
+ algorithm = DocumentStorageServiceCompressionAdapter.readAlgorithmFromBlob(file);
210
+ originalBlob = this.removePrefixFromBlobIfPresent(file);
211
+ } else {
212
+ algorithm = SummaryCompressionAlgorithm.None;
213
+ originalBlob = file;
214
+ }
215
+ if (algorithm === SummaryCompressionAlgorithm.None) {
216
+ decompressed = originalBlob;
217
+ } else if (algorithm === SummaryCompressionAlgorithm.LZ4) {
218
+ decompressed = decompress(originalBlob) as ArrayBufferLike;
219
+ } else {
220
+ throw new Error(`Unknown Algorithm ${algorithm}`);
221
+ }
222
+ return decompressed;
223
+ }
224
+
225
+ /**
226
+ * This method traverses the SummaryObject recursively. If it finds the ISummaryBlob object,
227
+ * it applies encoding/decoding on it according to the given isEncode flag.
228
+ * @param isEncode - True if the encoding should be applied, false if the decoding should be applied.
229
+ * @param input - The summary object to traverse.
230
+ * @param encoder - The encoder function to use.
231
+ * @param decoder - The decoder function to use.
232
+ * @param config - The config to use for encoding.
233
+ * @param context - The summary context.
234
+ * @returns - The summary object with the encoded/decoded blob.
235
+ */
236
+ private static recursivelyReplace(
237
+ isEncode: boolean,
238
+ input: SummaryObject,
239
+ encoder: (input: SummaryObject, config: ICompressionStorageConfig) => SummaryObject,
240
+ decoder: (input: SummaryObject) => SummaryObject,
241
+ config: ICompressionStorageConfig,
242
+ context?: ISummaryContext,
243
+ ): SummaryObject {
244
+ assert(typeof input === "object", 0x6f6 /* input must be a non-null object */);
245
+ const maybeReplaced = isEncode ? encoder(input, config) : decoder(input);
246
+
247
+ if (maybeReplaced !== input) {
248
+ return maybeReplaced;
249
+ }
250
+ let clone: object | undefined;
251
+ for (const key of Object.keys(input)) {
252
+ const value = input[key];
253
+
254
+ if (Boolean(value) && typeof value === "object") {
255
+ const replaced = this.recursivelyReplace(
256
+ isEncode,
257
+ value as SummaryObject,
258
+ encoder,
259
+ decoder,
260
+ config,
261
+ context,
262
+ );
263
+ if (replaced !== value) {
264
+ clone = clone ?? (Array.isArray(input) ? [...input] : { ...input });
265
+ clone[key] = replaced;
266
+ }
267
+ }
268
+ }
269
+ return (clone ?? input) as SummaryObject;
270
+ }
271
+
272
+ /**
273
+ * This method traverses the SummaryTree recursively. If it finds the ISummaryBlob object with the key '.metadata',
274
+ * it returns the summary tree containing that blob.
275
+ *
276
+ * @param summary - The summary tree to traverse.
277
+ * @returns - The summary tree containing the metadata blob.
278
+ */
279
+ private static findMetadataHolderSummary(summary: ISummaryTree): ISummaryTree | undefined {
280
+ assert(typeof summary === "object", 0x6f7 /* summary must be a non-null object */);
281
+ for (const key of Object.keys(summary.tree)) {
282
+ const value = summary.tree[key];
283
+
284
+ if (Boolean(value) && value.type === SummaryType.Tree) {
285
+ const found = this.findMetadataHolderSummary(value);
286
+ if (found) {
287
+ return found;
288
+ }
289
+ }
290
+ if (Boolean(value) && key === metadataBlobName && value.type === SummaryType.Blob) {
291
+ return summary;
292
+ }
293
+ }
294
+ return undefined;
295
+ }
296
+
297
+ /**
298
+ * This method obtains the summary tree containing the metadata blob. It returns the content
299
+ * of the tree atribute.
300
+ * @param summary - The summary tree to traverse.
301
+ * @returns - The content of the tree attribute of the summary tree containing the metadata blob.
302
+ */
303
+ private static getMetadataHolderTree(summary: ISummaryTree) {
304
+ const metadataHolder = this.findMetadataHolderSummary(summary);
305
+ assert(metadataHolder !== undefined, 0x6f8 /* metadataHolder must be a non-null object */);
306
+ const metadataHolderTree = metadataHolder.tree;
307
+ return metadataHolderTree;
308
+ }
309
+
310
+ /**
311
+ * This method adds the compression markup blob to the nested summary tree containing the metadata blob.
312
+ * @param summary - The top summary tree to put the compression markup blob into.
313
+ */
314
+ private static putCompressionMarkup(summary: ISummaryTree): void {
315
+ const metadataHolderTree =
316
+ DocumentStorageServiceCompressionAdapter.getMetadataHolderTree(summary);
317
+ metadataHolderTree[blobHeadersBlobName] = {
318
+ type: 2,
319
+ content: "",
320
+ };
321
+ }
322
+
323
+ /**
324
+ * This method traverses the SnapshotTree recursively. If it finds the ISummaryBlob object with the key '.metadata',
325
+ * it checks, if the SummaryTree holder of that object also contains the compression markup blob. If it is found,
326
+ * it returns true, otherwise false.
327
+ * @param snapshot - The snapshot tree to traverse.
328
+ * @returns - True if the compression markup blob is found, otherwise false.
329
+ */
330
+ private static hasCompressionMarkup(snapshot: ISnapshotTree): boolean {
331
+ assert(typeof snapshot === "object", 0x6f9 /* snapshot must be a non-null object */);
332
+ for (const key of Object.keys(snapshot.blobs)) {
333
+ if (key === metadataBlobName) {
334
+ const value = snapshot.blobs[blobHeadersBlobName];
335
+ if (value !== undefined) {
336
+ return true;
337
+ }
338
+ }
339
+ }
340
+ for (const key of Object.keys(snapshot.trees)) {
341
+ const value = snapshot[key] as ISnapshotTree;
342
+ if (value !== undefined) {
343
+ const found = this.hasCompressionMarkup(value);
344
+ if (found) {
345
+ return found;
346
+ }
347
+ }
348
+ }
349
+ return false;
350
+ }
351
+
352
+ /**
353
+ * This method performs compression of the blobs in the summary tree.
354
+ * @param summary - The summary tree to compress.
355
+ * @param config - The compression config.
356
+ * @returns - The compressed summary tree.
357
+ */
358
+ public static compressSummary(
359
+ summary: ISummaryTree,
360
+ config: ICompressionStorageConfig,
361
+ ): ISummaryTree {
362
+ this.putCompressionMarkup(summary);
363
+ const prep = DocumentStorageServiceCompressionAdapter.recursivelyReplace(
364
+ true,
365
+ summary,
366
+ DocumentStorageServiceCompressionAdapter.blobEncoder,
367
+ DocumentStorageServiceCompressionAdapter.blobDecoder,
368
+ config,
369
+ ) as ISummaryTree;
370
+ // console.log(`Miso summary-blob Summary Upload: ${JSON.stringify(prep).length}`);
371
+ return prep;
372
+ }
373
+
374
+ /**
375
+ * This method read blob from the storage and decompresses it if it is compressed.
376
+ * @param id - The id of the blob to read.
377
+ * @returns - The decompressed blob.
378
+ */
379
+ public override async readBlob(id: string): Promise<ArrayBufferLike> {
380
+ const originalBlob = await super.readBlob(id);
381
+ if (!this._isCompressionEnabled) {
382
+ return originalBlob;
383
+ } else {
384
+ const decompressedBlob =
385
+ DocumentStorageServiceCompressionAdapter.decodeBlob(originalBlob);
386
+ // console.log(`Miso summary-blob Blob read END : ${id} ${decompressedBlob.byteLength}`);
387
+ return decompressedBlob;
388
+ }
389
+ }
390
+
391
+ /**
392
+ * This method loads the snapshot tree from the server. It also checks, if the compression markup blob is present
393
+ * and setups the compression flag accordingly. It also identifies the blobs that are not compressed and do not contain
394
+ * algorithm byte prefix and store them.
395
+ * @param version - The version of the snapshot tree to load.
396
+ * @param scenarioName - The scenario name of the snapshot tree to load.
397
+ * @returns - The snapshot tree.
398
+ */
399
+ public override async getSnapshotTree(
400
+ version?: IVersion | undefined,
401
+ scenarioName?: string | undefined,
402
+ // eslint-disable-next-line @rushstack/no-new-null
403
+ ): Promise<ISnapshotTree | null> {
404
+ const snapshotTree = await super.getSnapshotTree(version, scenarioName);
405
+ this._isCompressionEnabled =
406
+ snapshotTree !== undefined &&
407
+ snapshotTree !== null &&
408
+ DocumentStorageServiceCompressionAdapter.hasCompressionMarkup(snapshotTree);
409
+ return snapshotTree;
410
+ }
411
+
412
+ /**
413
+ * This method uploads the summary to the storage. It performs compression of the blobs in the summary tree.
414
+ * @param summary - The summary tree to upload.
415
+ * @param context - The summary context.
416
+ * @returns - The ID of the uploaded summary.
417
+ */
418
+ public override async uploadSummaryWithContext(
419
+ summary: ISummaryTree,
420
+ context: ISummaryContext,
421
+ ): Promise<string> {
422
+ const prep = DocumentStorageServiceCompressionAdapter.compressSummary(
423
+ summary,
424
+ this._config,
425
+ );
426
+ return super.uploadSummaryWithContext(prep, context);
427
+ }
428
+
429
+ /**
430
+ * This method downloads the summary from the storage and then applies decompression on the compressed blobs.
431
+ * @param id - The ID of the summary to be downloaded
432
+ * @returns - The summary with decompressed blobs
433
+ */
434
+ public override async downloadSummary(id: ISummaryHandle): Promise<ISummaryTree> {
435
+ const summary = await super.downloadSummary(id);
436
+ return !this._isCompressionEnabled
437
+ ? summary
438
+ : (DocumentStorageServiceCompressionAdapter.recursivelyReplace(
439
+ false,
440
+ summary,
441
+ DocumentStorageServiceCompressionAdapter.blobEncoder,
442
+ DocumentStorageServiceCompressionAdapter.blobDecoder,
443
+ this._config,
444
+ ) as ISummaryTree);
445
+ }
446
+ }
@@ -0,0 +1,9 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export {
7
+ DocumentStorageServiceCompressionAdapter,
8
+ blobHeadersBlobName,
9
+ } from "./documentStorageServiceSummaryBlobCompressionAdapter";
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export {
7
+ SummaryCompressionAlgorithm,
8
+ ICompressionStorageConfig,
9
+ DefaultCompressionStorageConfig,
10
+ blobHeadersBlobName,
11
+ } from "./compression";
12
+
13
+ export { applyStorageCompression } from "./predefinedAdapters";
@@ -0,0 +1,71 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { assert } from "@fluidframework/common-utils";
7
+ import { IDocumentServiceFactory } from "@fluidframework/driver-definitions";
8
+ import {
9
+ DocumentServiceFactoryCompressionAdapter,
10
+ ICompressionStorageConfig,
11
+ DefaultCompressionStorageConfig,
12
+ } from "./compression";
13
+
14
+ /**
15
+ * This method optionally applies compression to the given document service factory. The compression
16
+ * must be enabled by setting the config to true or by passing a compression config object.
17
+ * @param documentServiceFactory - The document service factory to apply compression to.
18
+ * @param config - The compression configuration.
19
+ * @returns - The document service factory possibly with compression applied.
20
+ */
21
+ export function applyStorageCompression(
22
+ documentServiceFactory: IDocumentServiceFactory,
23
+ config?: ICompressionStorageConfig | boolean,
24
+ ): IDocumentServiceFactory {
25
+ if (config === undefined || config === false) {
26
+ return documentServiceFactory;
27
+ } else if (config === true) {
28
+ return applyStorageCompressionInternal(
29
+ DocumentServiceFactoryCompressionAdapter,
30
+ documentServiceFactory,
31
+ );
32
+ } else {
33
+ assert(isCompressionConfig(config), 0x6f4 /* Invalid compression config */);
34
+ return applyStorageCompressionInternal(
35
+ DocumentServiceFactoryCompressionAdapter,
36
+ documentServiceFactory,
37
+ config,
38
+ );
39
+ }
40
+ }
41
+
42
+ /**
43
+ * This method applies compression to the given document service factory.
44
+ * @param documentServiceFactory - The document service factory to apply compression to.
45
+ * @param config - The compression configuration.
46
+ * @returns - The document service factory with compression applied.
47
+ */
48
+ function applyStorageCompressionInternal(
49
+ constructor: new (
50
+ documentServiceFactory: IDocumentServiceFactory,
51
+ config: ICompressionStorageConfig,
52
+ ) => IDocumentServiceFactory,
53
+ documentServiceFactory: IDocumentServiceFactory,
54
+ config: ICompressionStorageConfig = DefaultCompressionStorageConfig,
55
+ ): IDocumentServiceFactory {
56
+ if (config.algorithm === undefined) {
57
+ return documentServiceFactory;
58
+ }
59
+ return new constructor(documentServiceFactory, config);
60
+ }
61
+
62
+ /**
63
+ * This method checks whether given objects contains
64
+ * a properties expected for the interface ICompressionStorageConfig.
65
+ */
66
+ export function isCompressionConfig(config: any): config is ICompressionStorageConfig {
67
+ return (
68
+ config !== undefined &&
69
+ (config.algorithm !== undefined || config.minSizeToCompress !== undefined)
70
+ );
71
+ }
@@ -0,0 +1,47 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
7
+ import {
8
+ IDocumentService,
9
+ IDocumentServiceFactory,
10
+ IResolvedUrl,
11
+ } from "@fluidframework/driver-definitions";
12
+ import { ISummaryTree } from "@fluidframework/protocol-definitions";
13
+
14
+ /**
15
+ * This abstract class implements IDocumentServiceFactory interface. It uses delegation pattern.
16
+ * It delegates all calls to IDocumentServiceFactory implementation passed to constructor.
17
+ */
18
+
19
+ export abstract class DocumentServiceFactoryProxy implements IDocumentServiceFactory {
20
+ constructor(private readonly _serviceFactory: IDocumentServiceFactory) {}
21
+
22
+ public get serviceFactory(): IDocumentServiceFactory {
23
+ return this._serviceFactory;
24
+ }
25
+
26
+ public async createContainer(
27
+ createNewSummary: ISummaryTree | undefined,
28
+ createNewResolvedUrl: IResolvedUrl,
29
+ logger?: ITelemetryBaseLogger,
30
+ clientIsSummarizer?: boolean,
31
+ ): Promise<IDocumentService> {
32
+ return this.serviceFactory.createContainer(
33
+ createNewSummary,
34
+ createNewResolvedUrl,
35
+ logger,
36
+ clientIsSummarizer,
37
+ );
38
+ }
39
+
40
+ public async createDocumentService(
41
+ resolvedUrl: IResolvedUrl,
42
+ logger?: ITelemetryBaseLogger,
43
+ clientIsSummarizer?: boolean,
44
+ ): Promise<IDocumentService> {
45
+ return this.serviceFactory.createDocumentService(resolvedUrl, logger, clientIsSummarizer);
46
+ }
47
+ }
@@ -0,0 +1,46 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ IDocumentDeltaConnection,
8
+ IDocumentDeltaStorageService,
9
+ IDocumentService,
10
+ IDocumentStorageService,
11
+ IResolvedUrl,
12
+ } from "@fluidframework/driver-definitions";
13
+ import { IClient } from "@fluidframework/protocol-definitions";
14
+
15
+ /**
16
+ * This abstract class implements IDocumentService interface. It uses delegation pattern.
17
+ * It delegates all calls to IDocumentService implementation passed to constructor.
18
+ */
19
+
20
+ export abstract class DocumentServiceProxy implements IDocumentService {
21
+ constructor(private readonly _service: IDocumentService) {}
22
+
23
+ public get service(): IDocumentService {
24
+ return this._service;
25
+ }
26
+
27
+ public async connectToStorage(): Promise<IDocumentStorageService> {
28
+ return this._service.connectToStorage();
29
+ }
30
+
31
+ public async connectToDeltaStorage(): Promise<IDocumentDeltaStorageService> {
32
+ return this._service.connectToDeltaStorage();
33
+ }
34
+
35
+ public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {
36
+ return this._service.connectToDeltaStream(client);
37
+ }
38
+
39
+ public dispose(error?: any): void {
40
+ this._service.dispose(error);
41
+ }
42
+
43
+ public get resolvedUrl(): IResolvedUrl {
44
+ return this._service.resolvedUrl;
45
+ }
46
+ }
@@ -54,6 +54,7 @@ export class DocumentStorageServiceProxy implements IDocumentStorageService {
54
54
  summary: ISummaryTree,
55
55
  context: ISummaryContext,
56
56
  ): Promise<string> {
57
+ console.log(`Summary uploaded: ${JSON.stringify(summary).length} bytes`);
57
58
  return this.internalStorageService.uploadSummaryWithContext(summary, context);
58
59
  }
59
60
 
package/src/index.ts CHANGED
@@ -49,3 +49,8 @@ export {
49
49
  isCombinedAppAndProtocolSummary,
50
50
  } from "./summaryForCreateNew";
51
51
  export { convertSummaryTreeToSnapshotITree } from "./treeConversions";
52
+ export {
53
+ applyStorageCompression,
54
+ ICompressionStorageConfig,
55
+ blobHeadersBlobName,
56
+ } from "./adapters";
package/src/network.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  ILocationRedirectionError,
12
12
  IResolvedUrl,
13
13
  } from "@fluidframework/driver-definitions";
14
- import { ITelemetryProperties } from "@fluidframework/common-definitions";
14
+ import { ITelemetryProperties } from "@fluidframework/core-interfaces";
15
15
  import { IFluidErrorBase, LoggingError } from "@fluidframework/telemetry-utils";
16
16
 
17
17
  export enum OnlineStatus {
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryErrorEvent } from "@fluidframework/common-definitions";
6
+ import { ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
7
7
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
8
8
  import { isOnline, OnlineStatus, canRetryOnError } from "./network";
9
9
 
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/driver-utils";
9
- export const pkgVersion = "2.0.0-internal.5.1.1";
9
+ export const pkgVersion = "2.0.0-internal.5.3.0";