@fluidframework/runtime-utils 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 (122) hide show
  1. package/.eslintrc.js +5 -7
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/dataStoreHandleContextUtils.d.ts.map +1 -1
  5. package/dist/dataStoreHandleContextUtils.js +3 -1
  6. package/dist/dataStoreHandleContextUtils.js.map +1 -1
  7. package/dist/dataStoreHelpers.d.ts +3 -1
  8. package/dist/dataStoreHelpers.d.ts.map +1 -1
  9. package/dist/dataStoreHelpers.js +27 -8
  10. package/dist/dataStoreHelpers.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +4 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/objectstoragepartition.d.ts.map +1 -1
  16. package/dist/objectstoragepartition.js.map +1 -1
  17. package/dist/objectstorageutils.d.ts.map +1 -1
  18. package/dist/objectstorageutils.js.map +1 -1
  19. package/dist/packageVersion.d.ts +1 -1
  20. package/dist/packageVersion.js +1 -1
  21. package/dist/packageVersion.js.map +1 -1
  22. package/dist/requestParser.d.ts.map +1 -1
  23. package/dist/requestParser.js.map +1 -1
  24. package/dist/runtimeFactoryHelper.d.ts +1 -1
  25. package/dist/runtimeFactoryHelper.d.ts.map +1 -1
  26. package/dist/runtimeFactoryHelper.js +7 -6
  27. package/dist/runtimeFactoryHelper.js.map +1 -1
  28. package/dist/summaryUtils.d.ts +33 -1
  29. package/dist/summaryUtils.d.ts.map +1 -1
  30. package/dist/summaryUtils.js +86 -3
  31. package/dist/summaryUtils.js.map +1 -1
  32. package/dist/unpackUsedRoutes.d.ts +11 -0
  33. package/dist/unpackUsedRoutes.d.ts.map +1 -0
  34. package/dist/unpackUsedRoutes.js +33 -0
  35. package/dist/unpackUsedRoutes.js.map +1 -0
  36. package/dist/utils.d.ts.map +1 -1
  37. package/dist/utils.js.map +1 -1
  38. package/lib/dataStoreHandleContextUtils.d.ts.map +1 -1
  39. package/lib/dataStoreHandleContextUtils.js +3 -1
  40. package/lib/dataStoreHandleContextUtils.js.map +1 -1
  41. package/lib/dataStoreHelpers.d.ts +3 -1
  42. package/lib/dataStoreHelpers.d.ts.map +1 -1
  43. package/lib/dataStoreHelpers.js +27 -8
  44. package/lib/dataStoreHelpers.js.map +1 -1
  45. package/lib/index.d.ts +2 -2
  46. package/lib/index.d.ts.map +1 -1
  47. package/lib/index.js +2 -2
  48. package/lib/index.js.map +1 -1
  49. package/lib/objectstoragepartition.d.ts.map +1 -1
  50. package/lib/objectstoragepartition.js.map +1 -1
  51. package/lib/objectstorageutils.d.ts.map +1 -1
  52. package/lib/objectstorageutils.js.map +1 -1
  53. package/lib/packageVersion.d.ts +1 -1
  54. package/lib/packageVersion.js +1 -1
  55. package/lib/packageVersion.js.map +1 -1
  56. package/lib/requestParser.d.ts.map +1 -1
  57. package/lib/requestParser.js.map +1 -1
  58. package/lib/runtimeFactoryHelper.d.ts +1 -1
  59. package/lib/runtimeFactoryHelper.d.ts.map +1 -1
  60. package/lib/runtimeFactoryHelper.js +7 -6
  61. package/lib/runtimeFactoryHelper.js.map +1 -1
  62. package/lib/summaryUtils.d.ts +33 -1
  63. package/lib/summaryUtils.d.ts.map +1 -1
  64. package/lib/summaryUtils.js +84 -2
  65. package/lib/summaryUtils.js.map +1 -1
  66. package/lib/unpackUsedRoutes.d.ts +11 -0
  67. package/lib/unpackUsedRoutes.d.ts.map +1 -0
  68. package/lib/unpackUsedRoutes.js +29 -0
  69. package/lib/unpackUsedRoutes.js.map +1 -0
  70. package/lib/utils.d.ts.map +1 -1
  71. package/lib/utils.js.map +1 -1
  72. package/package.json +54 -55
  73. package/prettier.config.cjs +1 -1
  74. package/src/dataStoreHandleContextUtils.ts +23 -16
  75. package/src/dataStoreHelpers.ts +104 -82
  76. package/src/index.ts +3 -9
  77. package/src/objectstoragepartition.ts +13 -13
  78. package/src/objectstorageutils.ts +31 -28
  79. package/src/packageVersion.ts +1 -1
  80. package/src/requestParser.ts +80 -82
  81. package/src/runtimeFactoryHelper.ts +24 -22
  82. package/src/summaryUtils.ts +393 -291
  83. package/src/unpackUsedRoutes.ts +30 -0
  84. package/src/utils.ts +6 -6
  85. package/tsconfig.esnext.json +6 -6
  86. package/tsconfig.json +8 -12
  87. package/dist/summarizerNode/index.d.ts +0 -8
  88. package/dist/summarizerNode/index.d.ts.map +0 -1
  89. package/dist/summarizerNode/index.js +0 -12
  90. package/dist/summarizerNode/index.js.map +0 -1
  91. package/dist/summarizerNode/summarizerNode.d.ts +0 -140
  92. package/dist/summarizerNode/summarizerNode.d.ts.map +0 -1
  93. package/dist/summarizerNode/summarizerNode.js +0 -439
  94. package/dist/summarizerNode/summarizerNode.js.map +0 -1
  95. package/dist/summarizerNode/summarizerNodeUtils.d.ts +0 -128
  96. package/dist/summarizerNode/summarizerNodeUtils.d.ts.map +0 -1
  97. package/dist/summarizerNode/summarizerNodeUtils.js +0 -132
  98. package/dist/summarizerNode/summarizerNodeUtils.js.map +0 -1
  99. package/dist/summarizerNode/summarizerNodeWithGc.d.ts +0 -131
  100. package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +0 -1
  101. package/dist/summarizerNode/summarizerNodeWithGc.js +0 -275
  102. package/dist/summarizerNode/summarizerNodeWithGc.js.map +0 -1
  103. package/lib/summarizerNode/index.d.ts +0 -8
  104. package/lib/summarizerNode/index.d.ts.map +0 -1
  105. package/lib/summarizerNode/index.js +0 -7
  106. package/lib/summarizerNode/index.js.map +0 -1
  107. package/lib/summarizerNode/summarizerNode.d.ts +0 -140
  108. package/lib/summarizerNode/summarizerNode.d.ts.map +0 -1
  109. package/lib/summarizerNode/summarizerNode.js +0 -434
  110. package/lib/summarizerNode/summarizerNode.js.map +0 -1
  111. package/lib/summarizerNode/summarizerNodeUtils.d.ts +0 -128
  112. package/lib/summarizerNode/summarizerNodeUtils.d.ts.map +0 -1
  113. package/lib/summarizerNode/summarizerNodeUtils.js +0 -125
  114. package/lib/summarizerNode/summarizerNodeUtils.js.map +0 -1
  115. package/lib/summarizerNode/summarizerNodeWithGc.d.ts +0 -131
  116. package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +0 -1
  117. package/lib/summarizerNode/summarizerNodeWithGc.js +0 -270
  118. package/lib/summarizerNode/summarizerNodeWithGc.js.map +0 -1
  119. package/src/summarizerNode/index.ts +0 -8
  120. package/src/summarizerNode/summarizerNode.ts +0 -593
  121. package/src/summarizerNode/summarizerNodeUtils.ts +0 -216
  122. package/src/summarizerNode/summarizerNodeWithGc.ts +0 -432
@@ -5,28 +5,29 @@
5
5
 
6
6
  import { TelemetryEventPropertyType } from "@fluidframework/common-definitions";
7
7
  import {
8
- bufferToString,
9
- fromBase64ToUtf8,
10
- IsoBuffer,
11
- Uint8ArrayToString,
12
- unreachableCase,
8
+ bufferToString,
9
+ fromBase64ToUtf8,
10
+ IsoBuffer,
11
+ Uint8ArrayToString,
12
+ unreachableCase,
13
13
  } from "@fluidframework/common-utils";
14
14
  import { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from "@fluidframework/protocol-base";
15
15
  import {
16
- ITree,
17
- SummaryType,
18
- ISummaryTree,
19
- SummaryObject,
20
- ISummaryBlob,
21
- TreeEntry,
22
- ITreeEntry,
23
- ISnapshotTree,
16
+ ITree,
17
+ SummaryType,
18
+ ISummaryTree,
19
+ SummaryObject,
20
+ ISummaryBlob,
21
+ TreeEntry,
22
+ ITreeEntry,
23
+ ISnapshotTree,
24
24
  } from "@fluidframework/protocol-definitions";
25
25
  import {
26
- ISummaryStats,
27
- ISummarizeResult,
28
- ISummaryTreeWithStats,
29
- ITelemetryContext,
26
+ ISummaryStats,
27
+ ISummarizeResult,
28
+ ISummaryTreeWithStats,
29
+ ITelemetryContext,
30
+ IGarbageCollectionData,
30
31
  } from "@fluidframework/runtime-definitions";
31
32
 
32
33
  /**
@@ -35,153 +36,167 @@ import {
35
36
  * @param stats - stats to merge
36
37
  */
37
38
  export function mergeStats(...stats: ISummaryStats[]): ISummaryStats {
38
- const results = {
39
- treeNodeCount: 0,
40
- blobNodeCount: 0,
41
- handleNodeCount: 0,
42
- totalBlobSize: 0,
43
- unreferencedBlobSize: 0,
44
- };
45
- for (const stat of stats) {
46
- results.treeNodeCount += stat.treeNodeCount;
47
- results.blobNodeCount += stat.blobNodeCount;
48
- results.handleNodeCount += stat.handleNodeCount;
49
- results.totalBlobSize += stat.totalBlobSize;
50
- results.unreferencedBlobSize += stat.unreferencedBlobSize;
51
- }
52
- return results;
39
+ const results = {
40
+ treeNodeCount: 0,
41
+ blobNodeCount: 0,
42
+ handleNodeCount: 0,
43
+ totalBlobSize: 0,
44
+ unreferencedBlobSize: 0,
45
+ };
46
+ for (const stat of stats) {
47
+ results.treeNodeCount += stat.treeNodeCount;
48
+ results.blobNodeCount += stat.blobNodeCount;
49
+ results.handleNodeCount += stat.handleNodeCount;
50
+ results.totalBlobSize += stat.totalBlobSize;
51
+ results.unreferencedBlobSize += stat.unreferencedBlobSize;
52
+ }
53
+ return results;
53
54
  }
54
55
 
55
56
  export function utf8ByteLength(str: string): number {
56
- // returns the byte length of an utf8 string
57
- let s = str.length;
58
- for (let i = str.length - 1; i >= 0; i--) {
59
- const code = str.charCodeAt(i);
60
- if (code > 0x7f && code <= 0x7ff) {
61
- s++;
62
- } else if (code > 0x7ff && code <= 0xffff) {
63
- s += 2;
64
- }
65
- if (code >= 0xDC00 && code <= 0xDFFF) {
66
- i--; // trail surrogate
67
- }
68
- }
69
- return s;
57
+ // returns the byte length of an utf8 string
58
+ let s = str.length;
59
+ for (let i = str.length - 1; i >= 0; i--) {
60
+ const code = str.charCodeAt(i);
61
+ if (code > 0x7f && code <= 0x7ff) {
62
+ s++;
63
+ } else if (code > 0x7ff && code <= 0xffff) {
64
+ s += 2;
65
+ }
66
+ if (code >= 0xdc00 && code <= 0xdfff) {
67
+ i--; // trail surrogate
68
+ }
69
+ }
70
+ return s;
70
71
  }
71
72
 
72
73
  export function getBlobSize(content: ISummaryBlob["content"]): number {
73
- return typeof content === "string" ? utf8ByteLength(content) : content.byteLength;
74
+ return typeof content === "string" ? utf8ByteLength(content) : content.byteLength;
74
75
  }
75
76
 
76
77
  function calculateStatsCore(summaryObject: SummaryObject, stats: ISummaryStats): void {
77
- switch (summaryObject.type) {
78
- case SummaryType.Tree: {
79
- stats.treeNodeCount++;
80
- for (const value of Object.values(summaryObject.tree)) {
81
- calculateStatsCore(value, stats);
82
- }
83
- return;
84
- }
85
- case SummaryType.Handle: {
86
- stats.handleNodeCount++;
87
- return;
88
- }
89
- case SummaryType.Blob: {
90
- stats.blobNodeCount++;
91
- stats.totalBlobSize += getBlobSize(summaryObject.content);
92
- return;
93
- }
94
- default: return;
95
- }
78
+ switch (summaryObject.type) {
79
+ case SummaryType.Tree: {
80
+ stats.treeNodeCount++;
81
+ for (const value of Object.values(summaryObject.tree)) {
82
+ calculateStatsCore(value, stats);
83
+ }
84
+ return;
85
+ }
86
+ case SummaryType.Handle: {
87
+ stats.handleNodeCount++;
88
+ return;
89
+ }
90
+ case SummaryType.Blob: {
91
+ stats.blobNodeCount++;
92
+ stats.totalBlobSize += getBlobSize(summaryObject.content);
93
+ return;
94
+ }
95
+ default:
96
+ return;
97
+ }
96
98
  }
97
99
 
98
100
  export function calculateStats(summary: SummaryObject): ISummaryStats {
99
- const stats = mergeStats();
100
- calculateStatsCore(summary, stats);
101
- return stats;
101
+ const stats = mergeStats();
102
+ calculateStatsCore(summary, stats);
103
+ return stats;
102
104
  }
103
105
 
104
- export function addBlobToSummary(summary: ISummaryTreeWithStats, key: string, content: string | Uint8Array): void {
105
- const blob: ISummaryBlob = {
106
- type: SummaryType.Blob,
107
- content,
108
- };
109
- summary.summary.tree[key] = blob;
110
- summary.stats.blobNodeCount++;
111
- summary.stats.totalBlobSize += getBlobSize(content);
106
+ export function addBlobToSummary(
107
+ summary: ISummaryTreeWithStats,
108
+ key: string,
109
+ content: string | Uint8Array,
110
+ ): void {
111
+ const blob: ISummaryBlob = {
112
+ type: SummaryType.Blob,
113
+ content,
114
+ };
115
+ summary.summary.tree[key] = blob;
116
+ summary.stats.blobNodeCount++;
117
+ summary.stats.totalBlobSize += getBlobSize(content);
112
118
  }
113
119
 
114
- export function addTreeToSummary(summary: ISummaryTreeWithStats, key: string, summarizeResult: ISummarizeResult): void {
115
- summary.summary.tree[key] = summarizeResult.summary;
116
- summary.stats = mergeStats(summary.stats, summarizeResult.stats);
120
+ export function addTreeToSummary(
121
+ summary: ISummaryTreeWithStats,
122
+ key: string,
123
+ summarizeResult: ISummarizeResult,
124
+ ): void {
125
+ summary.summary.tree[key] = summarizeResult.summary;
126
+ summary.stats = mergeStats(summary.stats, summarizeResult.stats);
117
127
  }
118
128
 
119
129
  export function addSummarizeResultToSummary(
120
- summary: ISummaryTreeWithStats,
121
- key: string,
122
- summarizeResult: ISummarizeResult,
130
+ summary: ISummaryTreeWithStats,
131
+ key: string,
132
+ summarizeResult: ISummarizeResult,
123
133
  ): void {
124
- summary.summary.tree[key] = summarizeResult.summary;
125
- summary.stats = mergeStats(summary.stats, summarizeResult.stats);
134
+ summary.summary.tree[key] = summarizeResult.summary;
135
+ summary.stats = mergeStats(summary.stats, summarizeResult.stats);
126
136
  }
127
137
 
128
138
  export class SummaryTreeBuilder implements ISummaryTreeWithStats {
129
- private attachmentCounter: number = 0;
130
-
131
- public get summary(): ISummaryTree {
132
- return {
133
- type: SummaryType.Tree,
134
- tree: { ...this.summaryTree },
135
- };
136
- }
137
-
138
- public get stats(): Readonly<ISummaryStats> {
139
- return { ...this.summaryStats };
140
- }
141
-
142
- constructor() {
143
- this.summaryStats = mergeStats();
144
- this.summaryStats.treeNodeCount++;
145
- }
146
-
147
- private readonly summaryTree: { [path: string]: SummaryObject; } = {};
148
- private summaryStats: ISummaryStats;
149
-
150
- public addBlob(key: string, content: string | Uint8Array): void {
151
- // Prevent cloning by directly referencing underlying private properties
152
- addBlobToSummary({
153
- summary: {
154
- type: SummaryType.Tree,
155
- tree: this.summaryTree,
156
- },
157
- stats: this.summaryStats,
158
- }, key, content);
159
- }
160
-
161
- public addHandle(
162
- key: string,
163
- handleType: SummaryType.Tree | SummaryType.Blob | SummaryType.Attachment,
164
- handle: string): void {
165
- this.summaryTree[key] = {
166
- type: SummaryType.Handle,
167
- handleType,
168
- handle,
169
- };
170
- this.summaryStats.handleNodeCount++;
171
- }
172
-
173
- public addWithStats(key: string, summarizeResult: ISummarizeResult): void {
174
- this.summaryTree[key] = summarizeResult.summary;
175
- this.summaryStats = mergeStats(this.summaryStats, summarizeResult.stats);
176
- }
177
-
178
- public addAttachment(id: string) {
179
- this.summaryTree[this.attachmentCounter++] = { id, type: SummaryType.Attachment };
180
- }
181
-
182
- public getSummaryTree(): ISummaryTreeWithStats {
183
- return { summary: this.summary, stats: this.stats };
184
- }
139
+ private attachmentCounter: number = 0;
140
+
141
+ public get summary(): ISummaryTree {
142
+ return {
143
+ type: SummaryType.Tree,
144
+ tree: { ...this.summaryTree },
145
+ };
146
+ }
147
+
148
+ public get stats(): Readonly<ISummaryStats> {
149
+ return { ...this.summaryStats };
150
+ }
151
+
152
+ constructor() {
153
+ this.summaryStats = mergeStats();
154
+ this.summaryStats.treeNodeCount++;
155
+ }
156
+
157
+ private readonly summaryTree: { [path: string]: SummaryObject } = {};
158
+ private summaryStats: ISummaryStats;
159
+
160
+ public addBlob(key: string, content: string | Uint8Array): void {
161
+ // Prevent cloning by directly referencing underlying private properties
162
+ addBlobToSummary(
163
+ {
164
+ summary: {
165
+ type: SummaryType.Tree,
166
+ tree: this.summaryTree,
167
+ },
168
+ stats: this.summaryStats,
169
+ },
170
+ key,
171
+ content,
172
+ );
173
+ }
174
+
175
+ public addHandle(
176
+ key: string,
177
+ handleType: SummaryType.Tree | SummaryType.Blob | SummaryType.Attachment,
178
+ handle: string,
179
+ ): void {
180
+ this.summaryTree[key] = {
181
+ type: SummaryType.Handle,
182
+ handleType,
183
+ handle,
184
+ };
185
+ this.summaryStats.handleNodeCount++;
186
+ }
187
+
188
+ public addWithStats(key: string, summarizeResult: ISummarizeResult): void {
189
+ this.summaryTree[key] = summarizeResult.summary;
190
+ this.summaryStats = mergeStats(this.summaryStats, summarizeResult.stats);
191
+ }
192
+
193
+ public addAttachment(id: string) {
194
+ this.summaryTree[this.attachmentCounter++] = { id, type: SummaryType.Attachment };
195
+ }
196
+
197
+ public getSummaryTree(): ISummaryTreeWithStats {
198
+ return { summary: this.summary, stats: this.stats };
199
+ }
185
200
  }
186
201
 
187
202
  /**
@@ -190,45 +205,44 @@ export class SummaryTreeBuilder implements ISummaryTreeWithStats {
190
205
  * @param fullTree - true to never use handles, even if id is specified
191
206
  */
192
207
  export function convertToSummaryTreeWithStats(
193
- snapshot: ITree,
194
- fullTree: boolean = false,
208
+ snapshot: ITree,
209
+ fullTree: boolean = false,
195
210
  ): ISummaryTreeWithStats {
196
- const builder = new SummaryTreeBuilder();
197
- for (const entry of snapshot.entries) {
198
- switch (entry.type) {
199
- case TreeEntry.Blob: {
200
- const blob = entry.value;
201
- const content = blob.encoding === "base64"
202
- ? IsoBuffer.from(blob.contents, "base64")
203
- : blob.contents;
204
- builder.addBlob(entry.path, content);
205
- break;
206
- }
207
-
208
- case TreeEntry.Tree: {
209
- const subtree = convertToSummaryTree(
210
- entry.value,
211
- fullTree);
212
- builder.addWithStats(entry.path, subtree);
213
-
214
- break;
215
- }
216
-
217
- case TreeEntry.Attachment: {
218
- const id = entry.value.id;
219
- builder.addAttachment(id);
220
-
221
- break;
222
- }
223
-
224
- default:
225
- throw new Error("Unexpected TreeEntry type");
226
- }
227
- }
228
-
229
- const summaryTree = builder.getSummaryTree();
230
- summaryTree.summary.unreferenced = snapshot.unreferenced;
231
- return summaryTree;
211
+ const builder = new SummaryTreeBuilder();
212
+ for (const entry of snapshot.entries) {
213
+ switch (entry.type) {
214
+ case TreeEntry.Blob: {
215
+ const blob = entry.value;
216
+ const content =
217
+ blob.encoding === "base64"
218
+ ? IsoBuffer.from(blob.contents, "base64")
219
+ : blob.contents;
220
+ builder.addBlob(entry.path, content);
221
+ break;
222
+ }
223
+
224
+ case TreeEntry.Tree: {
225
+ const subtree = convertToSummaryTree(entry.value, fullTree);
226
+ builder.addWithStats(entry.path, subtree);
227
+
228
+ break;
229
+ }
230
+
231
+ case TreeEntry.Attachment: {
232
+ const id = entry.value.id;
233
+ builder.addAttachment(id);
234
+
235
+ break;
236
+ }
237
+
238
+ default:
239
+ throw new Error("Unexpected TreeEntry type");
240
+ }
241
+ }
242
+
243
+ const summaryTree = builder.getSummaryTree();
244
+ summaryTree.summary.unreferenced = snapshot.unreferenced;
245
+ return summaryTree;
232
246
  }
233
247
 
234
248
  /**
@@ -236,25 +250,22 @@ export function convertToSummaryTreeWithStats(
236
250
  * @param snapshot - snapshot in ITree format
237
251
  * @param fullTree - true to never use handles, even if id is specified
238
252
  */
239
- export function convertToSummaryTree(
240
- snapshot: ITree,
241
- fullTree: boolean = false,
242
- ): ISummarizeResult {
243
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
244
- if (snapshot.id && !fullTree) {
245
- const stats = mergeStats();
246
- stats.handleNodeCount++;
247
- return {
248
- summary: {
249
- handle: snapshot.id,
250
- handleType: SummaryType.Tree,
251
- type: SummaryType.Handle,
252
- },
253
- stats,
254
- };
255
- } else {
256
- return convertToSummaryTreeWithStats(snapshot, fullTree);
257
- }
253
+ export function convertToSummaryTree(snapshot: ITree, fullTree: boolean = false): ISummarizeResult {
254
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
255
+ if (snapshot.id && !fullTree) {
256
+ const stats = mergeStats();
257
+ stats.handleNodeCount++;
258
+ return {
259
+ summary: {
260
+ handle: snapshot.id,
261
+ handleType: SummaryType.Tree,
262
+ type: SummaryType.Handle,
263
+ },
264
+ stats,
265
+ };
266
+ } else {
267
+ return convertToSummaryTreeWithStats(snapshot, fullTree);
268
+ }
258
269
  }
259
270
 
260
271
  /**
@@ -262,35 +273,33 @@ export function convertToSummaryTree(
262
273
  * was taken by serialize api in detached container.
263
274
  * @param snapshot - snapshot in ISnapshotTree format
264
275
  */
265
- export function convertSnapshotTreeToSummaryTree(
266
- snapshot: ISnapshotTree,
267
- ): ISummaryTreeWithStats {
268
- const builder = new SummaryTreeBuilder();
269
- for (const [path, id] of Object.entries(snapshot.blobs)) {
270
- let decoded: string | undefined;
271
- if ((snapshot as any).blobsContents !== undefined) {
272
- const content: ArrayBufferLike = (snapshot as any).blobsContents[id];
273
- if (content !== undefined) {
274
- decoded = bufferToString(content, "utf-8");
275
- }
276
- // 0.44 back-compat We still put contents in same blob for back-compat so need to add blob
277
- // only for blobPath -> blobId mapping and not for blobId -> blob value contents.
278
- } else if (snapshot.blobs[id] !== undefined) {
279
- decoded = fromBase64ToUtf8(snapshot.blobs[id]);
280
- }
281
- if (decoded !== undefined) {
282
- builder.addBlob(path, decoded);
283
- }
284
- }
285
-
286
- for (const [key, tree] of Object.entries(snapshot.trees)) {
287
- const subtree = convertSnapshotTreeToSummaryTree(tree);
288
- builder.addWithStats(key, subtree);
289
- }
290
-
291
- const summaryTree = builder.getSummaryTree();
292
- summaryTree.summary.unreferenced = snapshot.unreferenced;
293
- return summaryTree;
276
+ export function convertSnapshotTreeToSummaryTree(snapshot: ISnapshotTree): ISummaryTreeWithStats {
277
+ const builder = new SummaryTreeBuilder();
278
+ for (const [path, id] of Object.entries(snapshot.blobs)) {
279
+ let decoded: string | undefined;
280
+ if ((snapshot as any).blobsContents !== undefined) {
281
+ const content: ArrayBufferLike = (snapshot as any).blobsContents[id];
282
+ if (content !== undefined) {
283
+ decoded = bufferToString(content, "utf-8");
284
+ }
285
+ // 0.44 back-compat We still put contents in same blob for back-compat so need to add blob
286
+ // only for blobPath -> blobId mapping and not for blobId -> blob value contents.
287
+ } else if (snapshot.blobs[id] !== undefined) {
288
+ decoded = fromBase64ToUtf8(snapshot.blobs[id]);
289
+ }
290
+ if (decoded !== undefined) {
291
+ builder.addBlob(path, decoded);
292
+ }
293
+ }
294
+
295
+ for (const [key, tree] of Object.entries(snapshot.trees)) {
296
+ const subtree = convertSnapshotTreeToSummaryTree(tree);
297
+ builder.addWithStats(key, subtree);
298
+ }
299
+
300
+ const summaryTree = builder.getSummaryTree();
301
+ summaryTree.summary.unreferenced = snapshot.unreferenced;
302
+ return summaryTree;
294
303
  }
295
304
 
296
305
  /**
@@ -298,71 +307,164 @@ export function convertSnapshotTreeToSummaryTree(
298
307
  * @param summaryTree - summary tree in ISummaryTree format
299
308
  */
300
309
  export function convertSummaryTreeToITree(summaryTree: ISummaryTree): ITree {
301
- const entries: ITreeEntry[] = [];
302
- for (const [key, value] of Object.entries(summaryTree.tree)) {
303
- switch (value.type) {
304
- case SummaryType.Blob: {
305
- let parsedContent: string;
306
- let encoding: "utf-8" | "base64" = "utf-8";
307
- if (typeof value.content === "string") {
308
- parsedContent = value.content;
309
- } else {
310
- parsedContent = Uint8ArrayToString(value.content, "base64");
311
- encoding = "base64";
312
- }
313
- entries.push(new BlobTreeEntry(key, parsedContent, encoding));
314
- break;
315
- }
316
-
317
- case SummaryType.Tree: {
318
- entries.push(new TreeTreeEntry(key, convertSummaryTreeToITree(value)));
319
- break;
320
- }
321
-
322
- case SummaryType.Attachment: {
323
- entries.push(new AttachmentTreeEntry(key, value.id));
324
- break;
325
- }
326
-
327
- case SummaryType.Handle: {
328
- throw new Error("Should not have Handle type in summary tree");
329
- }
330
-
331
- default:
332
- unreachableCase(value, "Unexpected summary tree type");
333
- }
334
- }
335
- return {
336
- entries,
337
- unreferenced: summaryTree.unreferenced,
338
- };
310
+ const entries: ITreeEntry[] = [];
311
+ for (const [key, value] of Object.entries(summaryTree.tree)) {
312
+ switch (value.type) {
313
+ case SummaryType.Blob: {
314
+ let parsedContent: string;
315
+ let encoding: "utf-8" | "base64" = "utf-8";
316
+ if (typeof value.content === "string") {
317
+ parsedContent = value.content;
318
+ } else {
319
+ parsedContent = Uint8ArrayToString(value.content, "base64");
320
+ encoding = "base64";
321
+ }
322
+ entries.push(new BlobTreeEntry(key, parsedContent, encoding));
323
+ break;
324
+ }
325
+
326
+ case SummaryType.Tree: {
327
+ entries.push(new TreeTreeEntry(key, convertSummaryTreeToITree(value)));
328
+ break;
329
+ }
330
+
331
+ case SummaryType.Attachment: {
332
+ entries.push(new AttachmentTreeEntry(key, value.id));
333
+ break;
334
+ }
335
+
336
+ case SummaryType.Handle: {
337
+ throw new Error("Should not have Handle type in summary tree");
338
+ }
339
+
340
+ default:
341
+ unreachableCase(value, "Unexpected summary tree type");
342
+ }
343
+ }
344
+ return {
345
+ entries,
346
+ unreferenced: summaryTree.unreferenced,
347
+ };
339
348
  }
340
349
 
341
350
  export class TelemetryContext implements ITelemetryContext {
342
- private readonly telemetry = new Map<string, TelemetryEventPropertyType>();
343
-
344
- /**
345
- * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.set}
346
- */
347
- set(prefix: string, property: string, value: TelemetryEventPropertyType): void {
348
- this.telemetry.set(`${prefix}${property}`, value);
349
- }
350
-
351
- /**
352
- * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.get}
353
- */
354
- get(prefix: string, property: string): TelemetryEventPropertyType {
355
- return this.telemetry.get(`${prefix}${property}`);
356
- }
357
-
358
- /**
359
- * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.serialize}
360
- */
361
- serialize(): string {
362
- const jsonObject = {};
363
- this.telemetry.forEach((value, key) => {
364
- jsonObject[key] = value;
365
- });
366
- return JSON.stringify(jsonObject);
367
- }
351
+ private readonly telemetry = new Map<string, TelemetryEventPropertyType>();
352
+
353
+ /**
354
+ * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.set}
355
+ */
356
+ set(prefix: string, property: string, value: TelemetryEventPropertyType): void {
357
+ this.telemetry.set(`${prefix}${property}`, value);
358
+ }
359
+
360
+ /**
361
+ * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.setMultiple}
362
+ */
363
+ setMultiple(
364
+ prefix: string,
365
+ property: string,
366
+ values: Record<string, TelemetryEventPropertyType>,
367
+ ): void {
368
+ // Set the values individually so that they are logged as a flat list along with other properties.
369
+ for (const key of Object.keys(values)) {
370
+ this.set(prefix, `${property}_${key}`, values[key]);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.get}
376
+ */
377
+ get(prefix: string, property: string): TelemetryEventPropertyType {
378
+ return this.telemetry.get(`${prefix}${property}`);
379
+ }
380
+
381
+ /**
382
+ * {@inheritDoc @fluidframework/runtime-definitions#ITelemetryContext.serialize}
383
+ */
384
+ serialize(): string {
385
+ const jsonObject = {};
386
+ this.telemetry.forEach((value, key) => {
387
+ jsonObject[key] = value;
388
+ });
389
+ return JSON.stringify(jsonObject);
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Trims the leading slashes from the given string.
395
+ * @param str - A string that may contain leading slashes.
396
+ * @returns A new string without leading slashes.
397
+ */
398
+ function trimLeadingSlashes(str: string) {
399
+ return str.replace(/^\/+/g, "");
400
+ }
401
+
402
+ /**
403
+ * Trims the trailing slashes from the given string.
404
+ * @param str - A string that may contain trailing slashes.
405
+ * @returns A new string without trailing slashes.
406
+ */
407
+ function trimTrailingSlashes(str: string) {
408
+ return str.replace(/\/+$/g, "");
409
+ }
410
+
411
+ /**
412
+ * Helper class to build the garbage collection data of a node by combining the data from multiple nodes.
413
+ * @internal
414
+ */
415
+ export class GCDataBuilder implements IGarbageCollectionData {
416
+ private readonly gcNodesSet: { [id: string]: Set<string> } = {};
417
+ public get gcNodes(): { [id: string]: string[] } {
418
+ const gcNodes = {};
419
+ for (const [nodeId, outboundRoutes] of Object.entries(this.gcNodesSet)) {
420
+ gcNodes[nodeId] = [...outboundRoutes];
421
+ }
422
+ return gcNodes;
423
+ }
424
+
425
+ public addNode(id: string, outboundRoutes: string[]) {
426
+ this.gcNodesSet[id] = new Set(outboundRoutes);
427
+ }
428
+
429
+ /**
430
+ * Adds the given GC nodes. It does the following:
431
+ * - Normalizes the ids of the given nodes.
432
+ * - Prefixes the given `prefixId` to the given nodes' ids.
433
+ * - Adds the outbound routes of the nodes against the normalized and prefixed id.
434
+ */
435
+ public prefixAndAddNodes(prefixId: string, gcNodes: { [id: string]: string[] }) {
436
+ for (const [id, outboundRoutes] of Object.entries(gcNodes)) {
437
+ // Remove any leading slashes from the id.
438
+ let normalizedId = trimLeadingSlashes(id);
439
+ // Prefix the given id to the normalized id.
440
+ normalizedId = `/${prefixId}/${normalizedId}`;
441
+ // Remove any trailing slashes from the normalized id. Note that the trailing slashes are removed after
442
+ // adding the prefix for handling the special case where id is "/".
443
+ normalizedId = trimTrailingSlashes(normalizedId);
444
+
445
+ // Add the outbound routes against the normalized and prefixed id without duplicates.
446
+ this.gcNodesSet[normalizedId] = new Set(outboundRoutes);
447
+ }
448
+ }
449
+
450
+ public addNodes(gcNodes: { [id: string]: string[] }) {
451
+ for (const [id, outboundRoutes] of Object.entries(gcNodes)) {
452
+ this.gcNodesSet[id] = new Set(outboundRoutes);
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Adds the given outbound route to the outbound routes of all GC nodes.
458
+ */
459
+ public addRouteToAllNodes(outboundRoute: string) {
460
+ for (const outboundRoutes of Object.values(this.gcNodesSet)) {
461
+ outboundRoutes.add(outboundRoute);
462
+ }
463
+ }
464
+
465
+ public getGCData(): IGarbageCollectionData {
466
+ return {
467
+ gcNodes: this.gcNodes,
468
+ };
469
+ }
368
470
  }