@fluidframework/runtime-utils 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419

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