@fluidframework/odsp-driver 2.117.0 → 3.0.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 (258) hide show
  1. package/CHANGELOG.md +92 -2
  2. package/README.md +53 -50
  3. package/api-extractor/api-extractor-lint-pointInTime.cjs.json +5 -0
  4. package/api-extractor/api-extractor-lint-pointInTime.esm.json +5 -0
  5. package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
  6. package/api-extractor/api-extractor-report.pointInTime.json +9 -0
  7. package/api-report/odsp-driver.legacy.alpha.api.md +28 -3
  8. package/api-report/odsp-driver.legacy.beta.api.md +28 -3
  9. package/api-report/odsp-driver.point-in-time.legacy.beta.api.md +117 -0
  10. package/dist/ReadBufferUtils.js +2 -1
  11. package/dist/ReadBufferUtils.js.map +1 -1
  12. package/dist/WriteBufferUtils.js +4 -6
  13. package/dist/WriteBufferUtils.js.map +1 -1
  14. package/dist/checkUrl.js +1 -2
  15. package/dist/checkUrl.js.map +1 -1
  16. package/dist/compactSnapshotParser.d.ts.map +1 -1
  17. package/dist/compactSnapshotParser.js +5 -4
  18. package/dist/compactSnapshotParser.js.map +1 -1
  19. package/dist/compactSnapshotWriter.js +1 -2
  20. package/dist/compactSnapshotWriter.js.map +1 -1
  21. package/dist/createFile/createFile.js +4 -5
  22. package/dist/createFile/createFile.js.map +1 -1
  23. package/dist/createFile/createNewContainerOnExistingFile.js +1 -2
  24. package/dist/createFile/createNewContainerOnExistingFile.js.map +1 -1
  25. package/dist/createFile/createNewUtils.d.ts.map +1 -1
  26. package/dist/createFile/createNewUtils.js +4 -5
  27. package/dist/createFile/createNewUtils.js.map +1 -1
  28. package/dist/createFile/index.js +1 -2
  29. package/dist/createFile/index.js.map +1 -1
  30. package/dist/createOdspCreateContainerRequest.js +1 -2
  31. package/dist/createOdspCreateContainerRequest.js.map +1 -1
  32. package/dist/createOdspUrl.js +1 -2
  33. package/dist/createOdspUrl.js.map +1 -1
  34. package/dist/epochTracker.d.ts.map +1 -1
  35. package/dist/epochTracker.js +17 -6
  36. package/dist/epochTracker.js.map +1 -1
  37. package/dist/fetchSnapshot.d.ts.map +1 -1
  38. package/dist/fetchSnapshot.js +5 -5
  39. package/dist/fetchSnapshot.js.map +1 -1
  40. package/dist/getFileLink.d.ts.map +1 -1
  41. package/dist/getQueryString.js +1 -2
  42. package/dist/getQueryString.js.map +1 -1
  43. package/dist/getUrlAndHeadersWithAuth.js +1 -2
  44. package/dist/getUrlAndHeadersWithAuth.js.map +1 -1
  45. package/dist/index.d.ts +3 -4
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +3 -5
  48. package/dist/index.js.map +1 -1
  49. package/dist/legacy.d.ts +4 -1
  50. package/dist/legacyAlpha.d.ts +4 -1
  51. package/dist/localOdspDriver/localOdspDeltaStorageService.js +2 -0
  52. package/dist/localOdspDriver/localOdspDeltaStorageService.js.map +1 -1
  53. package/dist/localOdspDriver/localOdspDocumentService.js +5 -1
  54. package/dist/localOdspDriver/localOdspDocumentService.js.map +1 -1
  55. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +2 -1
  56. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  57. package/dist/localOdspDriver/localOdspDocumentStorageManager.js +4 -1
  58. package/dist/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  59. package/dist/mockify.js +2 -2
  60. package/dist/mockify.js.map +1 -1
  61. package/dist/odspCache.js +7 -8
  62. package/dist/odspCache.js.map +1 -1
  63. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  64. package/dist/odspDelayLoadedDeltaStream.js +42 -26
  65. package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
  66. package/dist/odspDeltaStorageService.js +14 -1
  67. package/dist/odspDeltaStorageService.js.map +1 -1
  68. package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
  69. package/dist/odspDocumentDeltaConnection.js +58 -41
  70. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  71. package/dist/odspDocumentService.js +16 -1
  72. package/dist/odspDocumentService.js.map +1 -1
  73. package/dist/odspDocumentServiceFactory.d.ts +30 -1
  74. package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
  75. package/dist/odspDocumentServiceFactory.js +30 -2
  76. package/dist/odspDocumentServiceFactory.js.map +1 -1
  77. package/dist/odspDocumentServiceFactoryCore.d.ts +83 -3
  78. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  79. package/dist/odspDocumentServiceFactoryCore.js +38 -12
  80. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  81. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  82. package/dist/odspDocumentStorageManager.js +31 -13
  83. package/dist/odspDocumentStorageManager.js.map +1 -1
  84. package/dist/odspDocumentStorageServiceBase.js +23 -20
  85. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  86. package/dist/odspDriverUrlResolver.js +2 -2
  87. package/dist/odspDriverUrlResolver.js.map +1 -1
  88. package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  89. package/dist/odspDriverUrlResolverForShareLink.js +6 -1
  90. package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
  91. package/dist/odspError.js +1 -2
  92. package/dist/odspError.js.map +1 -1
  93. package/dist/odspFluidFileLink.js +4 -4
  94. package/dist/odspFluidFileLink.js.map +1 -1
  95. package/dist/odspLayerCompatState.d.ts +8 -1
  96. package/dist/odspLayerCompatState.d.ts.map +1 -1
  97. package/dist/odspLayerCompatState.js +20 -1
  98. package/dist/odspLayerCompatState.js.map +1 -1
  99. package/dist/odspLocationRedirection.js +1 -2
  100. package/dist/odspLocationRedirection.js.map +1 -1
  101. package/dist/odspPublicUtils.js +1 -2
  102. package/dist/odspPublicUtils.js.map +1 -1
  103. package/dist/odspSnapshotParser.js +1 -2
  104. package/dist/odspSnapshotParser.js.map +1 -1
  105. package/dist/odspSummaryUploadManager.js +7 -0
  106. package/dist/odspSummaryUploadManager.js.map +1 -1
  107. package/dist/odspUrlHelper.js +6 -7
  108. package/dist/odspUrlHelper.js.map +1 -1
  109. package/dist/odspUtils.d.ts.map +1 -1
  110. package/dist/odspUtils.js +21 -21
  111. package/dist/odspUtils.js.map +1 -1
  112. package/dist/odspVersionManager/odspFileVersionFetcher.js +1 -2
  113. package/dist/odspVersionManager/odspFileVersionFetcher.js.map +1 -1
  114. package/dist/odspVersionManager/odspVersionManager.js +9 -8
  115. package/dist/odspVersionManager/odspVersionManager.js.map +1 -1
  116. package/dist/opsCaching.js +7 -1
  117. package/dist/opsCaching.js.map +1 -1
  118. package/dist/packageVersion.d.ts +1 -1
  119. package/dist/packageVersion.d.ts.map +1 -1
  120. package/dist/packageVersion.js +1 -1
  121. package/dist/packageVersion.js.map +1 -1
  122. package/dist/pointInTime.d.ts +15 -0
  123. package/dist/pointInTime.d.ts.map +1 -0
  124. package/dist/pointInTime.js +16 -0
  125. package/dist/pointInTime.js.map +1 -0
  126. package/dist/pointInTimeDriver/createPointInTimeDocumentService.d.ts +34 -0
  127. package/dist/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -0
  128. package/dist/pointInTimeDriver/createPointInTimeDocumentService.js +101 -0
  129. package/dist/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -0
  130. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js +10 -6
  131. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
  132. package/dist/prefetchLatestSnapshot.js +1 -2
  133. package/dist/prefetchLatestSnapshot.js.map +1 -1
  134. package/dist/retryErrorsStorageAdapter.js +3 -1
  135. package/dist/retryErrorsStorageAdapter.js.map +1 -1
  136. package/dist/retryUtils.js +1 -2
  137. package/dist/retryUtils.js.map +1 -1
  138. package/dist/vroom.d.ts.map +1 -1
  139. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  140. package/dist/zipItDataRepresentationUtils.js +20 -15
  141. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  142. package/lib/ReadBufferUtils.js +2 -1
  143. package/lib/ReadBufferUtils.js.map +1 -1
  144. package/lib/WriteBufferUtils.js +2 -4
  145. package/lib/WriteBufferUtils.js.map +1 -1
  146. package/lib/compactSnapshotParser.d.ts.map +1 -1
  147. package/lib/compactSnapshotParser.js +3 -2
  148. package/lib/compactSnapshotParser.js.map +1 -1
  149. package/lib/createFile/createNewUtils.d.ts.map +1 -1
  150. package/lib/createFile/createNewUtils.js +2 -2
  151. package/lib/createFile/createNewUtils.js.map +1 -1
  152. package/lib/epochTracker.d.ts.map +1 -1
  153. package/lib/epochTracker.js +15 -4
  154. package/lib/epochTracker.js.map +1 -1
  155. package/lib/fetchSnapshot.d.ts.map +1 -1
  156. package/lib/getFileLink.d.ts.map +1 -1
  157. package/lib/index.d.ts +3 -4
  158. package/lib/index.d.ts.map +1 -1
  159. package/lib/index.js +3 -6
  160. package/lib/index.js.map +1 -1
  161. package/lib/legacy.d.ts +5 -2
  162. package/lib/legacyAlpha.d.ts +5 -2
  163. package/lib/localOdspDriver/localOdspDeltaStorageService.js +2 -0
  164. package/lib/localOdspDriver/localOdspDeltaStorageService.js.map +1 -1
  165. package/lib/localOdspDriver/localOdspDocumentService.js +5 -1
  166. package/lib/localOdspDriver/localOdspDocumentService.js.map +1 -1
  167. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +2 -1
  168. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  169. package/lib/localOdspDriver/localOdspDocumentStorageManager.js +4 -1
  170. package/lib/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  171. package/lib/odspCache.js +7 -8
  172. package/lib/odspCache.js.map +1 -1
  173. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  174. package/lib/odspDelayLoadedDeltaStream.js +42 -26
  175. package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
  176. package/lib/odspDeltaStorageService.js +14 -1
  177. package/lib/odspDeltaStorageService.js.map +1 -1
  178. package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
  179. package/lib/odspDocumentDeltaConnection.js +58 -41
  180. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  181. package/lib/odspDocumentService.js +16 -1
  182. package/lib/odspDocumentService.js.map +1 -1
  183. package/lib/odspDocumentServiceFactory.d.ts +30 -1
  184. package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
  185. package/lib/odspDocumentServiceFactory.js +28 -1
  186. package/lib/odspDocumentServiceFactory.js.map +1 -1
  187. package/lib/odspDocumentServiceFactoryCore.d.ts +83 -3
  188. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  189. package/lib/odspDocumentServiceFactoryCore.js +39 -13
  190. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  191. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  192. package/lib/odspDocumentStorageManager.js +32 -14
  193. package/lib/odspDocumentStorageManager.js.map +1 -1
  194. package/lib/odspDocumentStorageServiceBase.js +23 -20
  195. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  196. package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  197. package/lib/odspDriverUrlResolverForShareLink.js +6 -1
  198. package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
  199. package/lib/odspLayerCompatState.d.ts +8 -1
  200. package/lib/odspLayerCompatState.d.ts.map +1 -1
  201. package/lib/odspLayerCompatState.js +20 -1
  202. package/lib/odspLayerCompatState.js.map +1 -1
  203. package/lib/odspSummaryUploadManager.js +7 -0
  204. package/lib/odspSummaryUploadManager.js.map +1 -1
  205. package/lib/odspUtils.d.ts.map +1 -1
  206. package/lib/odspVersionManager/odspVersionManager.js +7 -6
  207. package/lib/odspVersionManager/odspVersionManager.js.map +1 -1
  208. package/lib/opsCaching.js +7 -1
  209. package/lib/opsCaching.js.map +1 -1
  210. package/lib/packageVersion.d.ts +1 -1
  211. package/lib/packageVersion.d.ts.map +1 -1
  212. package/lib/packageVersion.js +1 -1
  213. package/lib/packageVersion.js.map +1 -1
  214. package/lib/pointInTime.d.ts +15 -0
  215. package/lib/pointInTime.d.ts.map +1 -0
  216. package/lib/pointInTime.js +12 -0
  217. package/lib/pointInTime.js.map +1 -0
  218. package/lib/pointInTimeDriver/createPointInTimeDocumentService.d.ts +34 -0
  219. package/lib/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -0
  220. package/lib/pointInTimeDriver/createPointInTimeDocumentService.js +99 -0
  221. package/lib/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -0
  222. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js +10 -6
  223. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
  224. package/lib/public.d.ts +1 -1
  225. package/lib/retryErrorsStorageAdapter.js +3 -1
  226. package/lib/retryErrorsStorageAdapter.js.map +1 -1
  227. package/lib/vroom.d.ts.map +1 -1
  228. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  229. package/lib/zipItDataRepresentationUtils.js +10 -5
  230. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  231. package/package.json +49 -54
  232. package/src/compactSnapshotParser.ts +6 -2
  233. package/src/createFile/createNewUtils.ts +6 -2
  234. package/src/index.ts +10 -6
  235. package/src/odspDocumentServiceFactory.ts +69 -1
  236. package/src/odspDocumentServiceFactoryCore.ts +105 -4
  237. package/src/odspDocumentStorageManager.ts +2 -2
  238. package/src/odspLayerCompatState.ts +29 -1
  239. package/src/odspVersionManager/DEV.md +9 -8
  240. package/src/packageVersion.ts +1 -1
  241. package/src/pointInTime.ts +30 -0
  242. package/src/pointInTimeDriver/createPointInTimeDocumentService.ts +193 -0
  243. package/src/zipItDataRepresentationUtils.ts +3 -3
  244. package/tsconfig.json +1 -1
  245. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts +0 -41
  246. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts.map +0 -1
  247. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js +0 -132
  248. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js.map +0 -1
  249. package/internal.d.ts +0 -11
  250. package/legacy/alpha.d.ts +0 -11
  251. package/legacy.d.ts +0 -11
  252. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts +0 -41
  253. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts.map +0 -1
  254. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js +0 -128
  255. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js.map +0 -1
  256. package/src/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.ts +0 -260
  257. /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
  258. /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
package/dist/odspUtils.js CHANGED
@@ -4,7 +4,27 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.getContainerPackageName = exports.appendNavParam = exports.useLegacyFlowWithoutGroupsForSnapshotFetch = exports.isSnapshotFetchForLoadingGroup = exports.isInstanceOfISnapshot = exports.getJoinSessionCacheKey = exports.measureP = exports.measure = exports.buildOdspShareLinkReqParams = exports.maxUmpPostBodySize = exports.snapshotWithLoadingGroupIdSupported = exports.createCacheSnapshotKey = exports.toInstrumentedOdspTokenFetcher = exports.toInstrumentedOdspStorageTokenFetcher = exports.createOdspLogger = exports.isOdspResolvedUrl = exports.getOdspResolvedUrl = exports.isNewFileInfo = exports.fetchAndParseAsJSONHelper = exports.fetchArray = exports.fetchHelper = exports.getWithRetryForTokenRefresh = exports.getWithRetryForTokenRefreshRepeat = void 0;
7
+ exports.maxUmpPostBodySize = exports.createOdspLogger = exports.getWithRetryForTokenRefreshRepeat = void 0;
8
+ exports.getWithRetryForTokenRefresh = getWithRetryForTokenRefresh;
9
+ exports.fetchHelper = fetchHelper;
10
+ exports.fetchArray = fetchArray;
11
+ exports.fetchAndParseAsJSONHelper = fetchAndParseAsJSONHelper;
12
+ exports.isNewFileInfo = isNewFileInfo;
13
+ exports.getOdspResolvedUrl = getOdspResolvedUrl;
14
+ exports.isOdspResolvedUrl = isOdspResolvedUrl;
15
+ exports.toInstrumentedOdspStorageTokenFetcher = toInstrumentedOdspStorageTokenFetcher;
16
+ exports.toInstrumentedOdspTokenFetcher = toInstrumentedOdspTokenFetcher;
17
+ exports.createCacheSnapshotKey = createCacheSnapshotKey;
18
+ exports.snapshotWithLoadingGroupIdSupported = snapshotWithLoadingGroupIdSupported;
19
+ exports.buildOdspShareLinkReqParams = buildOdspShareLinkReqParams;
20
+ exports.measure = measure;
21
+ exports.measureP = measureP;
22
+ exports.getJoinSessionCacheKey = getJoinSessionCacheKey;
23
+ exports.isInstanceOfISnapshot = isInstanceOfISnapshot;
24
+ exports.isSnapshotFetchForLoadingGroup = isSnapshotFetchForLoadingGroup;
25
+ exports.useLegacyFlowWithoutGroupsForSnapshotFetch = useLegacyFlowWithoutGroupsForSnapshotFetch;
26
+ exports.appendNavParam = appendNavParam;
27
+ exports.getContainerPackageName = getContainerPackageName;
8
28
  const client_utils_1 = require("@fluid-internal/client-utils");
9
29
  const internal_1 = require("@fluidframework/core-utils/internal");
10
30
  const internal_2 = require("@fluidframework/driver-utils/internal");
@@ -52,7 +72,6 @@ async function getWithRetryForTokenRefresh(get) {
52
72
  }
53
73
  });
54
74
  }
55
- exports.getWithRetryForTokenRefresh = getWithRetryForTokenRefresh;
56
75
  async function fetchHelper(requestInfo, requestInit) {
57
76
  const start = (0, client_utils_1.performanceNow)();
58
77
  return fetch(requestInfo, requestInit).then(async (fetchResponse) => {
@@ -119,7 +138,6 @@ async function fetchHelper(requestInfo, requestInit) {
119
138
  }
120
139
  });
121
140
  }
122
- exports.fetchHelper = fetchHelper;
123
141
  // This allows `fetch` to be mocked (e.g. with sinon `stub()`)
124
142
  fetchHelper.fetch = fetch;
125
143
  /**
@@ -148,7 +166,6 @@ async function fetchArray(requestInfo, requestInit) {
148
166
  duration,
149
167
  };
150
168
  }
151
- exports.fetchArray = fetchArray;
152
169
  /**
153
170
  * A utility function to fetch and parse as JSON with support for retries
154
171
  * @param requestInfo - fetch requestInfo, can be a string
@@ -180,16 +197,13 @@ async function fetchAndParseAsJSONHelper(requestInfo, requestInit) {
180
197
  };
181
198
  return res;
182
199
  }
183
- exports.fetchAndParseAsJSONHelper = fetchAndParseAsJSONHelper;
184
200
  function isNewFileInfo(fileInfo) {
185
201
  return fileInfo.type === undefined || fileInfo.type === "New";
186
202
  }
187
- exports.isNewFileInfo = isNewFileInfo;
188
203
  function getOdspResolvedUrl(resolvedUrl) {
189
204
  (0, internal_1.assert)(resolvedUrl.odspResolvedUrl === true, 0x1de /* "Not an ODSP resolved url" */);
190
205
  return resolvedUrl;
191
206
  }
192
- exports.getOdspResolvedUrl = getOdspResolvedUrl;
193
207
  /**
194
208
  * Type narrowing utility to determine if the provided {@link @fluidframework/driver-definitions#IResolvedUrl}
195
209
  * is an {@link @fluidframework/odsp-driver-definitions#IOdspResolvedUrl}.
@@ -199,7 +213,6 @@ exports.getOdspResolvedUrl = getOdspResolvedUrl;
199
213
  function isOdspResolvedUrl(resolvedUrl) {
200
214
  return "odspResolvedUrl" in resolvedUrl && resolvedUrl.odspResolvedUrl === true;
201
215
  }
202
- exports.isOdspResolvedUrl = isOdspResolvedUrl;
203
216
  const createOdspLogger = (logger) => (0, internal_5.createChildLogger)({
204
217
  logger,
205
218
  namespace: "OdspDriver",
@@ -220,7 +233,6 @@ function toInstrumentedOdspStorageTokenFetcher(logger, resolvedUrlParts, tokenFe
220
233
  // Drop undefined from signature - we can do it safely due to throwOnNullToken == true above
221
234
  return res;
222
235
  }
223
- exports.toInstrumentedOdspStorageTokenFetcher = toInstrumentedOdspStorageTokenFetcher;
224
236
  /**
225
237
  * Returns a function that can be used to fetch storage or websocket token.
226
238
  * There are scenarios where websocket token is not required / present (consumer stack and ordering service token),
@@ -272,7 +284,6 @@ function toInstrumentedOdspTokenFetcher(logger, resolvedUrlParts, tokenFetcher,
272
284
  }), { cancel: "generic" });
273
285
  };
274
286
  }
275
- exports.toInstrumentedOdspTokenFetcher = toInstrumentedOdspTokenFetcher;
276
287
  function createCacheSnapshotKey(odspResolvedUrl, snapshotWithLoadingGroupId) {
277
288
  const cacheEntry = {
278
289
  type: snapshotWithLoadingGroupId ? internal_4.snapshotWithLoadingGroupIdKey : internal_4.snapshotKey,
@@ -285,11 +296,9 @@ function createCacheSnapshotKey(odspResolvedUrl, snapshotWithLoadingGroupId) {
285
296
  };
286
297
  return cacheEntry;
287
298
  }
288
- exports.createCacheSnapshotKey = createCacheSnapshotKey;
289
299
  function snapshotWithLoadingGroupIdSupported(config) {
290
300
  return config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch2");
291
301
  }
292
- exports.snapshotWithLoadingGroupIdSupported = snapshotWithLoadingGroupIdSupported;
293
302
  // 80KB is the max body size that we can put in ump post body for server to be able to accept it.
294
303
  // Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.
295
304
  exports.maxUmpPostBodySize = 79872;
@@ -311,25 +320,21 @@ function buildOdspShareLinkReqParams(shareLinkType) {
311
320
  : shareLinkRequestParams;
312
321
  return shareLinkRequestParams;
313
322
  }
314
- exports.buildOdspShareLinkReqParams = buildOdspShareLinkReqParams;
315
323
  function measure(callback) {
316
324
  const start = (0, client_utils_1.performanceNow)();
317
325
  const result = callback();
318
326
  const time = (0, client_utils_1.performanceNow)() - start;
319
327
  return [result, time];
320
328
  }
321
- exports.measure = measure;
322
329
  async function measureP(callback) {
323
330
  const start = (0, client_utils_1.performanceNow)();
324
331
  const result = await callback();
325
332
  const time = (0, client_utils_1.performanceNow)() - start;
326
333
  return [result, time];
327
334
  }
328
- exports.measureP = measureP;
329
335
  function getJoinSessionCacheKey(odspResolvedUrl) {
330
336
  return `${odspResolvedUrl.hashedDocumentId}/joinsession`;
331
337
  }
332
- exports.getJoinSessionCacheKey = getJoinSessionCacheKey;
333
338
  /**
334
339
  * Utility API to check if the type of snapshot contents is `ISnapshot`.
335
340
  * @internal
@@ -340,7 +345,6 @@ function isInstanceOfISnapshot(
340
345
  obj) {
341
346
  return obj !== undefined && "snapshotFormatV" in obj && obj.snapshotFormatV === 1;
342
347
  }
343
- exports.isInstanceOfISnapshot = isInstanceOfISnapshot;
344
348
  /**
345
349
  * This tells whether request if for a specific loading group or not. The snapshot which
346
350
  * we fetch on initial load, fetches all ungrouped content.
@@ -348,7 +352,6 @@ exports.isInstanceOfISnapshot = isInstanceOfISnapshot;
348
352
  function isSnapshotFetchForLoadingGroup(loadingGroupIds) {
349
353
  return loadingGroupIds !== undefined && loadingGroupIds.length > 0;
350
354
  }
351
- exports.isSnapshotFetchForLoadingGroup = isSnapshotFetchForLoadingGroup;
352
355
  /*
353
356
  * This tells whether we are using legacy flow for fetching snapshot where we don't use
354
357
  * groupId query param in the trees latest network call.
@@ -356,7 +359,6 @@ exports.isSnapshotFetchForLoadingGroup = isSnapshotFetchForLoadingGroup;
356
359
  function useLegacyFlowWithoutGroupsForSnapshotFetch(loadingGroupIds) {
357
360
  return loadingGroupIds === undefined;
358
361
  }
359
- exports.useLegacyFlowWithoutGroupsForSnapshotFetch = useLegacyFlowWithoutGroupsForSnapshotFetch;
360
362
  // back-compat: GitHub #9653
361
363
  const isFluidPackage = (pkg) => typeof pkg === "object" && typeof pkg?.name === "string" && typeof pkg?.fluid === "object";
362
364
  /**
@@ -387,7 +389,6 @@ function appendNavParam(baseUrl, odspResolvedUrl, dataStorePath, containerPackag
387
389
  });
388
390
  return url.href;
389
391
  }
390
- exports.appendNavParam = appendNavParam;
391
392
  /**
392
393
  * Returns the package name of the container package information.
393
394
  * @param packageInfoSource - Information of the package connected to the URL
@@ -413,5 +414,4 @@ function getContainerPackageName(packageInfoSource) {
413
414
  }
414
415
  return containerPackageName;
415
416
  }
416
- exports.getContainerPackageName = getContainerPackageName;
417
417
  //# sourceMappingURL=odspUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type {\n\tITelemetryBaseLogger,\n\tITelemetryBaseProperties,\n} from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIResolvedUrl,\n\tISnapshot,\n\tIContainerPackageInfo,\n\tICacheEntry,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\ttype AuthorizationError,\n\tNetworkErrorBasic,\n\tNonRetryableError,\n\tOnlineStatus,\n\tRetryableError,\n\tisOnline,\n} from \"@fluidframework/driver-utils/internal\";\nimport {\n\tfetchIncorrectResponse,\n\tgetSPOAndGraphRequestIdsFromResponse,\n\tthrowOdspNetworkError,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\nimport {\n\ttype IOdspResolvedUrl,\n\ttype IOdspUrlParts,\n\ttype ISharingLinkKind,\n\ttype InstrumentedStorageTokenFetcher,\n\ttype InstrumentedTokenFetcher,\n\tOdspErrorTypes,\n\tauthHeaderFromTokenResponse,\n\ttype OdspResourceTokenFetchOptions,\n\ttype TokenFetchOptions,\n\ttype TokenFetcher,\n\tisTokenFromCache,\n\tsnapshotKey,\n\ttokenFromResponse,\n\tsnapshotWithLoadingGroupIdKey,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype IConfigProvider,\n\ttype IFluidErrorBase,\n\tPerformanceEvent,\n\tTelemetryDataTag,\n\ttype TelemetryLoggerExt,\n\twrapError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { storeLocatorInOdspUrl } from \"./odspFluidFileLink.js\";\n// eslint-disable-next-line import-x/no-deprecated\nimport type { ISnapshotContents } from \"./odspPublicUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/**\n * @legacy\n * @beta\n */\nexport interface IOdspResponse<T> {\n\tcontent: T;\n\theaders: Map<string, string>;\n\tpropsToLog: ITelemetryBaseProperties;\n\tduration: number;\n}\n\n/**\n * This interface captures the portion of TokenFetchOptions required for refreshing tokens\n * It is controlled by logic in getWithRetryForTokenRefresh to specify what is the required refresh behavior\n */\nexport interface TokenFetchOptionsEx {\n\trefresh: boolean;\n\tclaims?: string;\n\ttenantId?: string;\n\t/**\n\t * The previous error we hit in {@link getWithRetryForTokenRefresh}.\n\t */\n\tpreviousError?: unknown;\n}\n\nfunction headersToMap(headers: Headers): Map<string, string> {\n\tconst newHeaders = new Map<string, string>();\n\tfor (const [key, value] of headers.entries()) {\n\t\tnewHeaders.set(key, value);\n\t}\n\treturn newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(\n\tget: (options: TokenFetchOptionsEx) => Promise<T>,\n): Promise<T> {\n\treturn get({ refresh: false }).catch(async (error) => {\n\t\tconst options: TokenFetchOptionsEx = { refresh: true, previousError: error };\n\t\tswitch ((error as Partial<IFluidErrorBase>).errorType) {\n\t\t\t// If the error is 401 or 403 refresh the token and try once more.\n\t\t\tcase OdspErrorTypes.authorizationError: {\n\t\t\t\tconst authError = error as AuthorizationError;\n\t\t\t\treturn get({ ...options, claims: authError.claims, tenantId: authError.tenantId });\n\t\t\t}\n\n\t\t\tcase OdspErrorTypes.incorrectServerResponse: // some error on the wire, retry once\n\t\t\tcase OdspErrorTypes.fetchTokenError: {\n\t\t\t\t// If the token was null, then retry once.\n\t\t\t\treturn get(options);\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\t// Caller may determine that it wants one retry\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-explicit-any\n\t\t\t\tif ((error as any)[getWithRetryForTokenRefreshRepeat] === true) {\n\t\t\t\t\treturn get(options);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport async function fetchHelper(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n\tconst start = performanceNow();\n\n\treturn fetch(requestInfo, requestInit).then(\n\t\tasync (fetchResponse) => {\n\t\t\tconst response = fetchResponse;\n\t\t\t// Let's assume we can retry.\n\t\t\tif (!response) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t// pre-0.58 error message: No response from fetch call\n\t\t\t\t\t\"No response from ODSP fetch call\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!response.ok || response.status < 200 || response.status >= 300) {\n\t\t\t\tthrowOdspNetworkError(\n\t\t\t\t\t// pre-0.58 error message prefix: odspFetchError\n\t\t\t\t\t`ODSP fetch error [${response.status}]`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\tresponse,\n\t\t\t\t\tawait response.text(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst headers = headersToMap(response.headers);\n\t\t\treturn {\n\t\t\t\tcontent: response,\n\t\t\t\theaders,\n\t\t\t\tpropsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n\t\t\t\tduration: performanceNow() - start,\n\t\t\t};\n\t\t},\n\t\t(error) => {\n\t\t\tconst online = isOnline();\n\n\t\t\t// The error message may not be suitable to log for privacy reasons, so tag it as such\n\t\t\tconst taggedErrorMessage = {\n\t\t\t\tvalue: `${error}`, // This uses toString for objects, which often results in `${error.name}: ${error.message}`\n\t\t\t\ttag: TelemetryDataTag.UserData,\n\t\t\t};\n\t\t\t// After redacting URLs we believe the error message is safe to log\n\t\t\tconst urlRegex = /((http|https):\\/\\/(\\S*))/i;\n\t\t\tconst redactedErrorText = taggedErrorMessage.value.replace(urlRegex, \"REDACTED_URL\");\n\n\t\t\t// This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tif (error.name === \"AbortError\") {\n\t\t\t\tthrow new RetryableError(\"Fetch Timeout (AbortError)\", OdspErrorTypes.fetchTimeout, {\n\t\t\t\t\tdriverVersion,\n\t\t\t\t});\n\t\t\t}\n\t\t\t// TCP/IP timeout\n\t\t\tif (redactedErrorText.includes(\"ETIMEDOUT\")) {\n\t\t\t\tthrow new RetryableError(\"Fetch Timeout (ETIMEDOUT)\", OdspErrorTypes.fetchTimeout, {\n\t\t\t\t\tdriverVersion,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// eslint-disable-next-line unicorn/prefer-ternary\n\t\t\tif (online === OnlineStatus.Offline) {\n\t\t\t\tthrow new RetryableError(\n\t\t\t\t\t// pre-0.58 error message prefix: Offline\n\t\t\t\t\t`ODSP fetch failure (Offline): ${redactedErrorText}`,\n\t\t\t\t\tOdspErrorTypes.offlineError,\n\t\t\t\t\t{\n\t\t\t\t\t\tdriverVersion,\n\t\t\t\t\t\trawErrorMessage: taggedErrorMessage,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// It is perhaps still possible that this is due to being offline, the error does not reveal enough\n\t\t\t\t// information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n\t\t\t\tthrow new RetryableError(\n\t\t\t\t\t// pre-0.58 error message prefix: Fetch error\n\t\t\t\t\t`ODSP fetch failure: ${redactedErrorText}`,\n\t\t\t\t\tOdspErrorTypes.fetchFailure,\n\t\t\t\t\t{\n\t\t\t\t\t\tdriverVersion,\n\t\t\t\t\t\trawErrorMessage: taggedErrorMessage,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n}\n// This allows `fetch` to be mocked (e.g. with sinon `stub()`)\nfetchHelper.fetch = fetch;\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n\tconst { content, headers, propsToLog, duration } = await fetchHelper(\n\t\trequestInfo,\n\t\trequestInit,\n\t);\n\tlet arrayBuffer: ArrayBuffer;\n\ttry {\n\t\tarrayBuffer = await content.arrayBuffer();\n\t} catch {\n\t\t// Parsing can fail and message could contain full request URI, including\n\t\t// tokens, etc. So do not log error object itself.\n\t\tthrowOdspNetworkError(\n\t\t\t\"Error while parsing fetch response\",\n\t\t\tfetchIncorrectResponse,\n\t\t\tcontent, // response\n\t\t\tundefined, // response text\n\t\t\tpropsToLog,\n\t\t);\n\t}\n\n\tpropsToLog.bodySize = arrayBuffer.byteLength;\n\treturn {\n\t\theaders,\n\t\tcontent: arrayBuffer,\n\t\tpropsToLog,\n\t\tduration,\n\t};\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n\tconst { content, headers, propsToLog, duration } = await fetchHelper(\n\t\trequestInfo,\n\t\trequestInit,\n\t);\n\tlet text: string | undefined;\n\ttry {\n\t\ttext = await content.text();\n\t} catch {\n\t\t// JSON.parse() can fail and message would container full request URI, including\n\t\t// tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n\t\t// (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n\t\t// succeeds on retry.\n\t\t// So do not log error object itself.\n\t\tthrowOdspNetworkError(\n\t\t\t// pre-0.58 error message: errorWhileParsingFetchResponse\n\t\t\t\"Error while parsing fetch response\",\n\t\t\tfetchIncorrectResponse,\n\t\t\tcontent, // response\n\t\t\ttext,\n\t\t\tpropsToLog,\n\t\t);\n\t}\n\n\tpropsToLog.bodySize = text.length;\n\tconst res = {\n\t\theaders,\n\t\tcontent: JSON.parse(text) as T,\n\t\tpropsToLog,\n\t\tduration,\n\t};\n\treturn res;\n}\n\nexport interface IFileInfoBase {\n\ttype: \"New\" | \"Existing\";\n\tsiteUrl: string;\n\tdriveId: string;\n}\n\nexport interface INewFileInfo extends IFileInfoBase {\n\ttype: \"New\";\n\tfilename: string;\n\tfilePath: string;\n\t/**\n\t * application can request creation of a share link along with the creation of a new file\n\t * by passing in an optional param to specify the kind of sharing link\n\t */\n\tcreateLinkType?: ISharingLinkKind;\n}\n\nexport interface IExistingFileInfo extends IFileInfoBase {\n\ttype: \"Existing\";\n\titemId: string;\n}\n\nexport function isNewFileInfo(\n\tfileInfo: INewFileInfo | IExistingFileInfo,\n): fileInfo is INewFileInfo {\n\treturn fileInfo.type === undefined || fileInfo.type === \"New\";\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n\tassert(\n\t\t(resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true,\n\t\t0x1de /* \"Not an ODSP resolved url\" */,\n\t);\n\treturn resolvedUrl as IOdspResolvedUrl;\n}\n\n/**\n * Type narrowing utility to determine if the provided {@link @fluidframework/driver-definitions#IResolvedUrl}\n * is an {@link @fluidframework/odsp-driver-definitions#IOdspResolvedUrl}.\n * @legacy\n * @beta\n */\nexport function isOdspResolvedUrl(resolvedUrl: IResolvedUrl): resolvedUrl is IOdspResolvedUrl {\n\treturn \"odspResolvedUrl\" in resolvedUrl && resolvedUrl.odspResolvedUrl === true;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger): TelemetryLoggerExt =>\n\tcreateChildLogger({\n\t\tlogger,\n\t\tnamespace: \"OdspDriver\",\n\t\tproperties: {\n\t\t\tall: {\n\t\t\t\tdriverVersion,\n\t\t\t},\n\t\t},\n\t});\n\n/**\n * Returns a function that can be used to fetch storage token.\n * Storage token can not be empty - if original delegate (tokenFetcher argument) returns null result, exception will be thrown\n */\nexport function toInstrumentedOdspStorageTokenFetcher(\n\tlogger: TelemetryLoggerExt,\n\tresolvedUrlParts: IOdspUrlParts,\n\ttokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n): InstrumentedStorageTokenFetcher {\n\tconst res = toInstrumentedOdspTokenFetcher(\n\t\tlogger,\n\t\tresolvedUrlParts,\n\t\ttokenFetcher,\n\t\ttrue, // throwOnNullToken\n\t\tfalse, // returnPlainToken\n\t);\n\t// Drop undefined from signature - we can do it safely due to throwOnNullToken == true above\n\treturn res as InstrumentedStorageTokenFetcher;\n}\n\n/**\n * Returns a function that can be used to fetch storage or websocket token.\n * There are scenarios where websocket token is not required / present (consumer stack and ordering service token),\n * thus it could return null. Use toInstrumentedOdspStorageTokenFetcher if you deal with storage token.\n * @param returnPlainToken - When true, tokenResponse.token is returned. When false, tokenResponse.authorizationHeader is returned or an authorization header value is created based on tokenResponse.token\n */\nexport function toInstrumentedOdspTokenFetcher(\n\tlogger: TelemetryLoggerExt,\n\tresolvedUrlParts: IOdspUrlParts,\n\ttokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tthrowOnNullToken: boolean,\n\treturnPlainToken: boolean,\n): InstrumentedTokenFetcher {\n\treturn async (\n\t\toptions: TokenFetchOptions,\n\t\tname: string,\n\t\talwaysRecordTokenFetchTelemetry: boolean = false,\n\t) => {\n\t\t// Telemetry note: if options.refresh is true, there is a potential perf issue:\n\t\t// Host should optimize and provide non-expired tokens on all critical paths.\n\t\t// Exceptions: race conditions around expiration, revoked tokens, host that does not care\n\t\t// (fluid-fetcher)\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{\n\t\t\t\teventName: `${name}_GetToken`,\n\t\t\t\tattempts: options.refresh ? 2 : 1,\n\t\t\t\thasClaims: !!options.claims,\n\t\t\t\thasTenantId: !!options.tenantId,\n\t\t\t},\n\t\t\tasync (event) =>\n\t\t\t\ttokenFetcher({\n\t\t\t\t\t...options,\n\t\t\t\t\t...resolvedUrlParts,\n\t\t\t\t}).then(\n\t\t\t\t\t(tokenResponse) => {\n\t\t\t\t\t\tconst returnVal = returnPlainToken\n\t\t\t\t\t\t\t? tokenFromResponse(tokenResponse)\n\t\t\t\t\t\t\t: authHeaderFromTokenResponse(tokenResponse);\n\t\t\t\t\t\t// This event alone generates so many events that is materially impacts cost of telemetry\n\t\t\t\t\t\t// Thus do not report end event when it comes back quickly.\n\t\t\t\t\t\t// Note that most of the hosts do not report if result is comming from cache or not,\n\t\t\t\t\t\t// so we can't rely on that here. But always record if specified explicitly for cases such as\n\t\t\t\t\t\t// calling trees/latest during load.\n\t\t\t\t\t\tif (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n\t\t\t\t\t\t\tevent.end({\n\t\t\t\t\t\t\t\tfromCache: isTokenFromCache(tokenResponse),\n\t\t\t\t\t\t\t\tisNull: returnVal === null,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (returnVal === null && throwOnNullToken) {\n\t\t\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\t\t// pre-0.58 error message: Token is null for ${name} call\n\t\t\t\t\t\t\t\t`The Host-provided token fetcher returned null`,\n\t\t\t\t\t\t\t\tOdspErrorTypes.fetchTokenError,\n\t\t\t\t\t\t\t\t{ method: name, driverVersion },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn returnVal;\n\t\t\t\t\t},\n\t\t\t\t\t(error) => {\n\t\t\t\t\t\t// There is an important but unofficial contract here where token providers can set canRetry: true\n\t\t\t\t\t\t// to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\tconst rawCanRetry = error?.canRetry;\n\t\t\t\t\t\tconst tokenError = wrapError(\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t(errorMessage) =>\n\t\t\t\t\t\t\t\tnew NetworkErrorBasic(\n\t\t\t\t\t\t\t\t\t`The Host-provided token fetcher threw an error`,\n\t\t\t\t\t\t\t\t\tOdspErrorTypes.fetchTokenError,\n\t\t\t\t\t\t\t\t\ttypeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n\t\t\t\t\t\t\t\t\t{ method: name, errorMessage, driverVersion },\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow tokenError;\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t{ cancel: \"generic\" },\n\t\t);\n\t};\n}\n\nexport function createCacheSnapshotKey(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tsnapshotWithLoadingGroupId: boolean | undefined,\n): ICacheEntry {\n\tconst cacheEntry: ICacheEntry = {\n\t\ttype: snapshotWithLoadingGroupId ? snapshotWithLoadingGroupIdKey : snapshotKey,\n\t\tkey: \"\",\n\t\tfile: {\n\t\t\tresolvedUrl: odspResolvedUrl,\n\t\t\tdocId: odspResolvedUrl.hashedDocumentId,\n\t\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\t},\n\t};\n\treturn cacheEntry;\n}\n\nexport function snapshotWithLoadingGroupIdSupported(\n\tconfig: IConfigProvider,\n): boolean | undefined {\n\treturn config.getBoolean(\"Fluid.Container.UseLoadingGroupIdForSnapshotFetch2\");\n}\n\n// 80KB is the max body size that we can put in ump post body for server to be able to accept it.\n// Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.\nexport const maxUmpPostBodySize = 79872;\n\n/**\n * Build request parameters to request for the creation of a sharing link along with the creation of the file\n * through the /snapshot api call.\n * @param shareLinkType - Kind of sharing link requested\n * @returns A string of request parameters that can be concatenated with the base URI\n */\nexport function buildOdspShareLinkReqParams(\n\tshareLinkType: ISharingLinkKind | undefined,\n): string | undefined {\n\tif (!shareLinkType) {\n\t\treturn;\n\t}\n\tconst scope = shareLinkType.scope;\n\tlet shareLinkRequestParams = `createLinkScope=${scope}`;\n\tconst role = shareLinkType.role;\n\tshareLinkRequestParams = role\n\t\t? `${shareLinkRequestParams}&createLinkRole=${role}`\n\t\t: shareLinkRequestParams;\n\treturn shareLinkRequestParams;\n}\n\nexport function measure<T>(callback: () => T): [T, number] {\n\tconst start = performanceNow();\n\tconst result = callback();\n\tconst time = performanceNow() - start;\n\treturn [result, time];\n}\n\nexport async function measureP<T>(callback: () => Promise<T>): Promise<[T, number]> {\n\tconst start = performanceNow();\n\tconst result = await callback();\n\tconst time = performanceNow() - start;\n\treturn [result, time];\n}\n\nexport function getJoinSessionCacheKey(odspResolvedUrl: IOdspResolvedUrl): string {\n\treturn `${odspResolvedUrl.hashedDocumentId}/joinsession`;\n}\n\n/**\n * Utility API to check if the type of snapshot contents is `ISnapshot`.\n * @internal\n * @param obj - obj whose type needs to be identified.\n */\nexport function isInstanceOfISnapshot(\n\t// eslint-disable-next-line import-x/no-deprecated\n\tobj: ISnapshotContents | ISnapshot | undefined,\n): obj is ISnapshot {\n\treturn obj !== undefined && \"snapshotFormatV\" in obj && obj.snapshotFormatV === 1;\n}\n\n/**\n * This tells whether request if for a specific loading group or not. The snapshot which\n * we fetch on initial load, fetches all ungrouped content.\n */\nexport function isSnapshotFetchForLoadingGroup(\n\tloadingGroupIds: string[] | undefined,\n): boolean {\n\treturn loadingGroupIds !== undefined && loadingGroupIds.length > 0;\n}\n\n/*\n * This tells whether we are using legacy flow for fetching snapshot where we don't use\n * groupId query param in the trees latest network call.\n */\nexport function useLegacyFlowWithoutGroupsForSnapshotFetch(\n\tloadingGroupIds: string[] | undefined,\n): boolean {\n\treturn loadingGroupIds === undefined;\n}\n\n// back-compat: GitHub #9653\nconst isFluidPackage = (pkg: Record<string, unknown>): boolean =>\n\ttypeof pkg === \"object\" && typeof pkg?.name === \"string\" && typeof pkg?.fluid === \"object\";\n\n/**\n * Appends the store locator properties to the provided base URL. This function is useful for scenarios where an application\n * has a base URL (for example a sharing link) of the Fluid file, but does not have the locator information that would be used by Fluid\n * to load the file later.\n * @param baseUrl - The input URL on which the locator params will be appended.\n * @param resolvedUrl - odsp-driver's resolvedURL object.\n * @param dataStorePath - The relative data store path URL.\n * For requesting a driver URL, this value should always be '/'. If an empty string is passed, then dataStorePath\n * will be extracted from the resolved url if present.\n * @param containerPackageName - Name of the package to be included in the URL.\n * @returns The provided base URL appended with odsp-specific locator information\n */\nexport function appendNavParam(\n\tbaseUrl: string,\n\todspResolvedUrl: IOdspResolvedUrl,\n\tdataStorePath: string,\n\tcontainerPackageName?: string,\n): string {\n\tconst url = new URL(baseUrl);\n\n\t// If the user has passed an empty dataStorePath, then extract it from the resolved url.\n\tconst actualDataStorePath = dataStorePath || (odspResolvedUrl.dataStorePath ?? \"\");\n\n\tstoreLocatorInOdspUrl(url, {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t\tdataStorePath: actualDataStorePath,\n\t\tappName: odspResolvedUrl.appName,\n\t\tcontainerPackageName,\n\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\tcontext: odspResolvedUrl.context,\n\t});\n\n\treturn url.href;\n}\n\n/**\n * Returns the package name of the container package information.\n * @param packageInfoSource - Information of the package connected to the URL\n * @returns The package name of the container package\n */\nexport function getContainerPackageName(\n\tpackageInfoSource: IContainerPackageInfo | undefined,\n): string | undefined {\n\tlet containerPackageName: string | undefined;\n\tif (packageInfoSource && \"name\" in packageInfoSource) {\n\t\tcontainerPackageName = packageInfoSource.name;\n\t\t// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t} else if (isFluidPackage((packageInfoSource as any)?.package)) {\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\tcontainerPackageName = (packageInfoSource as any)?.package.name;\n\t} else {\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\tcontainerPackageName = (packageInfoSource as any)?.package;\n\t}\n\treturn containerPackageName;\n}\n"]}
1
+ {"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type {\n\tITelemetryBaseLogger,\n\tITelemetryBaseProperties,\n} from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIResolvedUrl,\n\tISnapshot,\n\tIContainerPackageInfo,\n\tICacheEntry,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\ttype AuthorizationError,\n\tNetworkErrorBasic,\n\tNonRetryableError,\n\tOnlineStatus,\n\tRetryableError,\n\tisOnline,\n} from \"@fluidframework/driver-utils/internal\";\nimport {\n\tfetchIncorrectResponse,\n\tgetSPOAndGraphRequestIdsFromResponse,\n\tthrowOdspNetworkError,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\nimport {\n\ttype IOdspResolvedUrl,\n\ttype IOdspUrlParts,\n\ttype ISharingLinkKind,\n\ttype InstrumentedStorageTokenFetcher,\n\ttype InstrumentedTokenFetcher,\n\tOdspErrorTypes,\n\tauthHeaderFromTokenResponse,\n\ttype OdspResourceTokenFetchOptions,\n\ttype TokenFetchOptions,\n\ttype TokenFetcher,\n\tisTokenFromCache,\n\tsnapshotKey,\n\ttokenFromResponse,\n\tsnapshotWithLoadingGroupIdKey,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype IConfigProvider,\n\ttype IFluidErrorBase,\n\tPerformanceEvent,\n\tTelemetryDataTag,\n\ttype TelemetryLoggerExt,\n\twrapError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { storeLocatorInOdspUrl } from \"./odspFluidFileLink.js\";\n// eslint-disable-next-line import-x/no-deprecated\nimport type { ISnapshotContents } from \"./odspPublicUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/**\n * @legacy\n * @beta\n */\nexport interface IOdspResponse<T> {\n\tcontent: T;\n\theaders: Map<string, string>;\n\tpropsToLog: ITelemetryBaseProperties;\n\tduration: number;\n}\n\n/**\n * This interface captures the portion of TokenFetchOptions required for refreshing tokens\n * It is controlled by logic in getWithRetryForTokenRefresh to specify what is the required refresh behavior\n */\nexport interface TokenFetchOptionsEx {\n\trefresh: boolean;\n\tclaims?: string;\n\ttenantId?: string;\n\t/**\n\t * The previous error we hit in {@link getWithRetryForTokenRefresh}.\n\t */\n\tpreviousError?: unknown;\n}\n\nfunction headersToMap(headers: Headers): Map<string, string> {\n\tconst newHeaders = new Map<string, string>();\n\tfor (const [key, value] of headers.entries()) {\n\t\tnewHeaders.set(key, value);\n\t}\n\treturn newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(\n\tget: (options: TokenFetchOptionsEx) => Promise<T>,\n): Promise<T> {\n\treturn get({ refresh: false }).catch(async (error) => {\n\t\tconst options: TokenFetchOptionsEx = { refresh: true, previousError: error };\n\t\tswitch ((error as Partial<IFluidErrorBase>).errorType) {\n\t\t\t// If the error is 401 or 403 refresh the token and try once more.\n\t\t\tcase OdspErrorTypes.authorizationError: {\n\t\t\t\tconst authError = error as AuthorizationError;\n\t\t\t\treturn get({ ...options, claims: authError.claims, tenantId: authError.tenantId });\n\t\t\t}\n\n\t\t\tcase OdspErrorTypes.incorrectServerResponse: // some error on the wire, retry once\n\t\t\tcase OdspErrorTypes.fetchTokenError: {\n\t\t\t\t// If the token was null, then retry once.\n\t\t\t\treturn get(options);\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\t// Caller may determine that it wants one retry\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-explicit-any\n\t\t\t\tif ((error as any)[getWithRetryForTokenRefreshRepeat] === true) {\n\t\t\t\t\treturn get(options);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t});\n}\n\nexport async function fetchHelper(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n\tconst start = performanceNow();\n\n\treturn fetch(requestInfo, requestInit).then(\n\t\tasync (fetchResponse) => {\n\t\t\tconst response = fetchResponse;\n\t\t\t// Let's assume we can retry.\n\t\t\tif (!response) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t// pre-0.58 error message: No response from fetch call\n\t\t\t\t\t\"No response from ODSP fetch call\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!response.ok || response.status < 200 || response.status >= 300) {\n\t\t\t\tthrowOdspNetworkError(\n\t\t\t\t\t// pre-0.58 error message prefix: odspFetchError\n\t\t\t\t\t`ODSP fetch error [${response.status}]`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\tresponse,\n\t\t\t\t\tawait response.text(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst headers = headersToMap(response.headers);\n\t\t\treturn {\n\t\t\t\tcontent: response,\n\t\t\t\theaders,\n\t\t\t\tpropsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n\t\t\t\tduration: performanceNow() - start,\n\t\t\t};\n\t\t},\n\t\t(error) => {\n\t\t\tconst online = isOnline();\n\n\t\t\t// The error message may not be suitable to log for privacy reasons, so tag it as such\n\t\t\tconst taggedErrorMessage = {\n\t\t\t\tvalue: `${error}`, // This uses toString for objects, which often results in `${error.name}: ${error.message}`\n\t\t\t\ttag: TelemetryDataTag.UserData,\n\t\t\t};\n\t\t\t// After redacting URLs we believe the error message is safe to log\n\t\t\tconst urlRegex = /((http|https):\\/\\/(\\S*))/i;\n\t\t\tconst redactedErrorText = taggedErrorMessage.value.replace(urlRegex, \"REDACTED_URL\");\n\n\t\t\t// This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tif (error.name === \"AbortError\") {\n\t\t\t\tthrow new RetryableError(\"Fetch Timeout (AbortError)\", OdspErrorTypes.fetchTimeout, {\n\t\t\t\t\tdriverVersion,\n\t\t\t\t});\n\t\t\t}\n\t\t\t// TCP/IP timeout\n\t\t\tif (redactedErrorText.includes(\"ETIMEDOUT\")) {\n\t\t\t\tthrow new RetryableError(\"Fetch Timeout (ETIMEDOUT)\", OdspErrorTypes.fetchTimeout, {\n\t\t\t\t\tdriverVersion,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// eslint-disable-next-line unicorn/prefer-ternary\n\t\t\tif (online === OnlineStatus.Offline) {\n\t\t\t\tthrow new RetryableError(\n\t\t\t\t\t// pre-0.58 error message prefix: Offline\n\t\t\t\t\t`ODSP fetch failure (Offline): ${redactedErrorText}`,\n\t\t\t\t\tOdspErrorTypes.offlineError,\n\t\t\t\t\t{\n\t\t\t\t\t\tdriverVersion,\n\t\t\t\t\t\trawErrorMessage: taggedErrorMessage,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// It is perhaps still possible that this is due to being offline, the error does not reveal enough\n\t\t\t\t// information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n\t\t\t\tthrow new RetryableError(\n\t\t\t\t\t// pre-0.58 error message prefix: Fetch error\n\t\t\t\t\t`ODSP fetch failure: ${redactedErrorText}`,\n\t\t\t\t\tOdspErrorTypes.fetchFailure,\n\t\t\t\t\t{\n\t\t\t\t\t\tdriverVersion,\n\t\t\t\t\t\trawErrorMessage: taggedErrorMessage,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n}\n// This allows `fetch` to be mocked (e.g. with sinon `stub()`)\nfetchHelper.fetch = fetch;\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n\tconst { content, headers, propsToLog, duration } = await fetchHelper(\n\t\trequestInfo,\n\t\trequestInit,\n\t);\n\tlet arrayBuffer: ArrayBuffer;\n\ttry {\n\t\tarrayBuffer = await content.arrayBuffer();\n\t} catch {\n\t\t// Parsing can fail and message could contain full request URI, including\n\t\t// tokens, etc. So do not log error object itself.\n\t\tthrowOdspNetworkError(\n\t\t\t\"Error while parsing fetch response\",\n\t\t\tfetchIncorrectResponse,\n\t\t\tcontent, // response\n\t\t\tundefined, // response text\n\t\t\tpropsToLog,\n\t\t);\n\t}\n\n\tpropsToLog.bodySize = arrayBuffer.byteLength;\n\treturn {\n\t\theaders,\n\t\tcontent: arrayBuffer,\n\t\tpropsToLog,\n\t\tduration,\n\t};\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n\trequestInfo: RequestInfo,\n\trequestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n\tconst { content, headers, propsToLog, duration } = await fetchHelper(\n\t\trequestInfo,\n\t\trequestInit,\n\t);\n\tlet text: string | undefined;\n\ttry {\n\t\ttext = await content.text();\n\t} catch {\n\t\t// JSON.parse() can fail and message would container full request URI, including\n\t\t// tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n\t\t// (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n\t\t// succeeds on retry.\n\t\t// So do not log error object itself.\n\t\tthrowOdspNetworkError(\n\t\t\t// pre-0.58 error message: errorWhileParsingFetchResponse\n\t\t\t\"Error while parsing fetch response\",\n\t\t\tfetchIncorrectResponse,\n\t\t\tcontent, // response\n\t\t\ttext,\n\t\t\tpropsToLog,\n\t\t);\n\t}\n\n\tpropsToLog.bodySize = text.length;\n\tconst res = {\n\t\theaders,\n\t\tcontent: JSON.parse(text) as T,\n\t\tpropsToLog,\n\t\tduration,\n\t};\n\treturn res;\n}\n\nexport interface IFileInfoBase {\n\ttype: \"New\" | \"Existing\";\n\tsiteUrl: string;\n\tdriveId: string;\n}\n\nexport interface INewFileInfo extends IFileInfoBase {\n\ttype: \"New\";\n\tfilename: string;\n\tfilePath: string;\n\t/**\n\t * application can request creation of a share link along with the creation of a new file\n\t * by passing in an optional param to specify the kind of sharing link\n\t */\n\tcreateLinkType?: ISharingLinkKind;\n}\n\nexport interface IExistingFileInfo extends IFileInfoBase {\n\ttype: \"Existing\";\n\titemId: string;\n}\n\nexport function isNewFileInfo(\n\tfileInfo: INewFileInfo | IExistingFileInfo,\n): fileInfo is INewFileInfo {\n\treturn fileInfo.type === undefined || fileInfo.type === \"New\";\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n\tassert(\n\t\t(resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true,\n\t\t0x1de /* \"Not an ODSP resolved url\" */,\n\t);\n\treturn resolvedUrl as IOdspResolvedUrl;\n}\n\n/**\n * Type narrowing utility to determine if the provided {@link @fluidframework/driver-definitions#IResolvedUrl}\n * is an {@link @fluidframework/odsp-driver-definitions#IOdspResolvedUrl}.\n * @legacy\n * @beta\n */\nexport function isOdspResolvedUrl(resolvedUrl: IResolvedUrl): resolvedUrl is IOdspResolvedUrl {\n\treturn \"odspResolvedUrl\" in resolvedUrl && resolvedUrl.odspResolvedUrl === true;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger): TelemetryLoggerExt =>\n\tcreateChildLogger({\n\t\tlogger,\n\t\tnamespace: \"OdspDriver\",\n\t\tproperties: {\n\t\t\tall: {\n\t\t\t\tdriverVersion,\n\t\t\t},\n\t\t},\n\t});\n\n/**\n * Returns a function that can be used to fetch storage token.\n * Storage token can not be empty - if original delegate (tokenFetcher argument) returns null result, exception will be thrown\n */\nexport function toInstrumentedOdspStorageTokenFetcher(\n\tlogger: TelemetryLoggerExt,\n\tresolvedUrlParts: IOdspUrlParts,\n\ttokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n): InstrumentedStorageTokenFetcher {\n\tconst res = toInstrumentedOdspTokenFetcher(\n\t\tlogger,\n\t\tresolvedUrlParts,\n\t\ttokenFetcher,\n\t\ttrue, // throwOnNullToken\n\t\tfalse, // returnPlainToken\n\t);\n\t// Drop undefined from signature - we can do it safely due to throwOnNullToken == true above\n\treturn res as InstrumentedStorageTokenFetcher;\n}\n\n/**\n * Returns a function that can be used to fetch storage or websocket token.\n * There are scenarios where websocket token is not required / present (consumer stack and ordering service token),\n * thus it could return null. Use toInstrumentedOdspStorageTokenFetcher if you deal with storage token.\n * @param returnPlainToken - When true, tokenResponse.token is returned. When false, tokenResponse.authorizationHeader is returned or an authorization header value is created based on tokenResponse.token\n */\nexport function toInstrumentedOdspTokenFetcher(\n\tlogger: TelemetryLoggerExt,\n\tresolvedUrlParts: IOdspUrlParts,\n\ttokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tthrowOnNullToken: boolean,\n\treturnPlainToken: boolean,\n): InstrumentedTokenFetcher {\n\treturn async (\n\t\toptions: TokenFetchOptions,\n\t\tname: string,\n\t\talwaysRecordTokenFetchTelemetry: boolean = false,\n\t) => {\n\t\t// Telemetry note: if options.refresh is true, there is a potential perf issue:\n\t\t// Host should optimize and provide non-expired tokens on all critical paths.\n\t\t// Exceptions: race conditions around expiration, revoked tokens, host that does not care\n\t\t// (fluid-fetcher)\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{\n\t\t\t\teventName: `${name}_GetToken`,\n\t\t\t\tattempts: options.refresh ? 2 : 1,\n\t\t\t\thasClaims: !!options.claims,\n\t\t\t\thasTenantId: !!options.tenantId,\n\t\t\t},\n\t\t\tasync (event) =>\n\t\t\t\ttokenFetcher({\n\t\t\t\t\t...options,\n\t\t\t\t\t...resolvedUrlParts,\n\t\t\t\t}).then(\n\t\t\t\t\t(tokenResponse) => {\n\t\t\t\t\t\tconst returnVal = returnPlainToken\n\t\t\t\t\t\t\t? tokenFromResponse(tokenResponse)\n\t\t\t\t\t\t\t: authHeaderFromTokenResponse(tokenResponse);\n\t\t\t\t\t\t// This event alone generates so many events that is materially impacts cost of telemetry\n\t\t\t\t\t\t// Thus do not report end event when it comes back quickly.\n\t\t\t\t\t\t// Note that most of the hosts do not report if result is comming from cache or not,\n\t\t\t\t\t\t// so we can't rely on that here. But always record if specified explicitly for cases such as\n\t\t\t\t\t\t// calling trees/latest during load.\n\t\t\t\t\t\tif (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n\t\t\t\t\t\t\tevent.end({\n\t\t\t\t\t\t\t\tfromCache: isTokenFromCache(tokenResponse),\n\t\t\t\t\t\t\t\tisNull: returnVal === null,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (returnVal === null && throwOnNullToken) {\n\t\t\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\t\t// pre-0.58 error message: Token is null for ${name} call\n\t\t\t\t\t\t\t\t`The Host-provided token fetcher returned null`,\n\t\t\t\t\t\t\t\tOdspErrorTypes.fetchTokenError,\n\t\t\t\t\t\t\t\t{ method: name, driverVersion },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn returnVal;\n\t\t\t\t\t},\n\t\t\t\t\t(error) => {\n\t\t\t\t\t\t// There is an important but unofficial contract here where token providers can set canRetry: true\n\t\t\t\t\t\t// to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\tconst rawCanRetry = error?.canRetry;\n\t\t\t\t\t\tconst tokenError = wrapError(\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t(errorMessage) =>\n\t\t\t\t\t\t\t\tnew NetworkErrorBasic(\n\t\t\t\t\t\t\t\t\t`The Host-provided token fetcher threw an error`,\n\t\t\t\t\t\t\t\t\tOdspErrorTypes.fetchTokenError,\n\t\t\t\t\t\t\t\t\ttypeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n\t\t\t\t\t\t\t\t\t{ method: name, errorMessage, driverVersion },\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow tokenError;\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t{ cancel: \"generic\" },\n\t\t);\n\t};\n}\n\nexport function createCacheSnapshotKey(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tsnapshotWithLoadingGroupId: boolean | undefined,\n): ICacheEntry {\n\tconst cacheEntry: ICacheEntry = {\n\t\ttype: snapshotWithLoadingGroupId ? snapshotWithLoadingGroupIdKey : snapshotKey,\n\t\tkey: \"\",\n\t\tfile: {\n\t\t\tresolvedUrl: odspResolvedUrl,\n\t\t\tdocId: odspResolvedUrl.hashedDocumentId,\n\t\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\t},\n\t};\n\treturn cacheEntry;\n}\n\nexport function snapshotWithLoadingGroupIdSupported(\n\tconfig: IConfigProvider,\n): boolean | undefined {\n\treturn config.getBoolean(\"Fluid.Container.UseLoadingGroupIdForSnapshotFetch2\");\n}\n\n// 80KB is the max body size that we can put in ump post body for server to be able to accept it.\n// Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.\nexport const maxUmpPostBodySize = 79872;\n\n/**\n * Build request parameters to request for the creation of a sharing link along with the creation of the file\n * through the /snapshot api call.\n * @param shareLinkType - Kind of sharing link requested\n * @returns A string of request parameters that can be concatenated with the base URI\n */\nexport function buildOdspShareLinkReqParams(\n\tshareLinkType: ISharingLinkKind | undefined,\n): string | undefined {\n\tif (!shareLinkType) {\n\t\treturn;\n\t}\n\tconst scope = shareLinkType.scope;\n\tlet shareLinkRequestParams = `createLinkScope=${scope}`;\n\tconst role = shareLinkType.role;\n\tshareLinkRequestParams = role\n\t\t? `${shareLinkRequestParams}&createLinkRole=${role}`\n\t\t: shareLinkRequestParams;\n\treturn shareLinkRequestParams;\n}\n\nexport function measure<T>(callback: () => T): [T, number] {\n\tconst start = performanceNow();\n\tconst result = callback();\n\tconst time = performanceNow() - start;\n\treturn [result, time];\n}\n\nexport async function measureP<T>(callback: () => Promise<T>): Promise<[T, number]> {\n\tconst start = performanceNow();\n\tconst result = await callback();\n\tconst time = performanceNow() - start;\n\treturn [result, time];\n}\n\nexport function getJoinSessionCacheKey(odspResolvedUrl: IOdspResolvedUrl): string {\n\treturn `${odspResolvedUrl.hashedDocumentId}/joinsession`;\n}\n\n/**\n * Utility API to check if the type of snapshot contents is `ISnapshot`.\n * @internal\n * @param obj - obj whose type needs to be identified.\n */\nexport function isInstanceOfISnapshot(\n\t// eslint-disable-next-line import-x/no-deprecated\n\tobj: ISnapshotContents | ISnapshot | undefined,\n): obj is ISnapshot {\n\treturn obj !== undefined && \"snapshotFormatV\" in obj && obj.snapshotFormatV === 1;\n}\n\n/**\n * This tells whether request if for a specific loading group or not. The snapshot which\n * we fetch on initial load, fetches all ungrouped content.\n */\nexport function isSnapshotFetchForLoadingGroup(\n\tloadingGroupIds: string[] | undefined,\n): boolean {\n\treturn loadingGroupIds !== undefined && loadingGroupIds.length > 0;\n}\n\n/*\n * This tells whether we are using legacy flow for fetching snapshot where we don't use\n * groupId query param in the trees latest network call.\n */\nexport function useLegacyFlowWithoutGroupsForSnapshotFetch(\n\tloadingGroupIds: string[] | undefined,\n): boolean {\n\treturn loadingGroupIds === undefined;\n}\n\n// back-compat: GitHub #9653\nconst isFluidPackage = (pkg: Record<string, unknown>): boolean =>\n\ttypeof pkg === \"object\" && typeof pkg?.name === \"string\" && typeof pkg?.fluid === \"object\";\n\n/**\n * Appends the store locator properties to the provided base URL. This function is useful for scenarios where an application\n * has a base URL (for example a sharing link) of the Fluid file, but does not have the locator information that would be used by Fluid\n * to load the file later.\n * @param baseUrl - The input URL on which the locator params will be appended.\n * @param resolvedUrl - odsp-driver's resolvedURL object.\n * @param dataStorePath - The relative data store path URL.\n * For requesting a driver URL, this value should always be '/'. If an empty string is passed, then dataStorePath\n * will be extracted from the resolved url if present.\n * @param containerPackageName - Name of the package to be included in the URL.\n * @returns The provided base URL appended with odsp-specific locator information\n */\nexport function appendNavParam(\n\tbaseUrl: string,\n\todspResolvedUrl: IOdspResolvedUrl,\n\tdataStorePath: string,\n\tcontainerPackageName?: string,\n): string {\n\tconst url = new URL(baseUrl);\n\n\t// If the user has passed an empty dataStorePath, then extract it from the resolved url.\n\tconst actualDataStorePath = dataStorePath || (odspResolvedUrl.dataStorePath ?? \"\");\n\n\tstoreLocatorInOdspUrl(url, {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t\tdataStorePath: actualDataStorePath,\n\t\tappName: odspResolvedUrl.appName,\n\t\tcontainerPackageName,\n\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\tcontext: odspResolvedUrl.context,\n\t});\n\n\treturn url.href;\n}\n\n/**\n * Returns the package name of the container package information.\n * @param packageInfoSource - Information of the package connected to the URL\n * @returns The package name of the container package\n */\nexport function getContainerPackageName(\n\tpackageInfoSource: IContainerPackageInfo | undefined,\n): string | undefined {\n\tlet containerPackageName: string | undefined;\n\tif (packageInfoSource && \"name\" in packageInfoSource) {\n\t\tcontainerPackageName = packageInfoSource.name;\n\t\t// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t} else if (isFluidPackage((packageInfoSource as any)?.package)) {\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\tcontainerPackageName = (packageInfoSource as any)?.package.name;\n\t} else {\n\t\t// TODO: use a stronger type\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\n\t\tcontainerPackageName = (packageInfoSource as any)?.package;\n\t}\n\treturn containerPackageName;\n}\n"]}
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.createOdspFileVersionFetcher = void 0;
7
+ exports.createOdspFileVersionFetcher = createOdspFileVersionFetcher;
8
8
  /*
9
9
  * An {@link IOdspFileVersionFetcher} backed by the ODSP REST APIs:
10
10
  * - GET /_api/v2.1/.../versions -- enumerate the file's versions.
@@ -115,5 +115,4 @@ function createOdspFileVersionFetcher(props) {
115
115
  getRecoverableVersionEpoch,
116
116
  };
117
117
  }
118
- exports.createOdspFileVersionFetcher = createOdspFileVersionFetcher;
119
118
  //# sourceMappingURL=odspFileVersionFetcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"odspFileVersionFetcher.js","sourceRoot":"","sources":["../../src/odspVersionManager/odspFileVersionFetcher.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/*\n * An {@link IOdspFileVersionFetcher} backed by the ODSP REST APIs:\n * - GET /_api/v2.1/.../versions -- enumerate the file's versions.\n * - GET /_api/v2.1/.../versions/{label}/opStream/snapshots/trees/latest?blobs=2 -- fetch a version's\n * snapshot and read its sequence number, parsed with the driver's snapshot parser.\n * - GET /_api/v2.1/.../[versions/{label}/]opStream/snapshots/trees/latest?blobs=0 -- read a version's\n * or the live document's ODSP epoch (`x-fluid-epoch`) to compare their lineage.\n */\n\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport { OdspErrorTypes } from \"@fluidframework/odsp-driver-definitions/internal\";\nimport type {\n\tIOdspUrlParts,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport { currentReadVersion, parseCompactSnapshotResponse } from \"../compactSnapshotParser.js\";\nimport type { IOdspSnapshot } from \"../contracts.js\";\nimport type { EpochTracker } from \"../epochTracker.js\";\nimport { getHeadersWithAuth } from \"../getUrlAndHeadersWithAuth.js\";\nimport { convertOdspSnapshotToSnapshotTreeAndBlobs } from \"../odspSnapshotParser.js\";\nimport { getApiRoot } from \"../odspUrlHelper.js\";\nimport { fetchArray, getWithRetryForTokenRefresh } from \"../odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"../packageVersion.js\";\n\n/**\n * A single ODSP file version, as listed by the file's version history.\n */\nexport interface OdspFileVersionRef {\n\t/**\n\t * The version's label (e.g. `\"42.0\"`), used to address the version when fetching it.\n\t */\n\treadonly versionId: string;\n\t/**\n\t * Last-modified timestamp of this version, ISO-8601.\n\t */\n\treadonly lastModifiedDateTime: string;\n}\n\n/**\n * Provides a file's versions and resolves each version's Fluid sequence number. Injected into\n * the version manager so the selection logic does not depend on how versions are fetched.\n */\nexport interface IOdspFileVersionFetcher {\n\t/**\n\t * Enumerate the file's versions, newest-first.\n\t */\n\tlistFileVersions(): Promise<OdspFileVersionRef[]>;\n\t/**\n\t * Resolve a single version's Fluid sequence number. Throws on failure rather than returning a\n\t * wrong value.\n\t */\n\tresolveSequenceNumber(versionId: string): Promise<number>;\n\t/**\n\t * Read the live document's current ODSP epoch (`x-fluid-epoch`), or `undefined`. Epoch identifies\n\t * the file's binary lineage and changes on a version restore or download-then-reupload; compared\n\t * with {@link IOdspFileVersionFetcher.getRecoverableVersionEpoch} to confirm a base is on the live\n\t * document's lineage.\n\t */\n\tgetLiveDocumentEpoch(): Promise<string | undefined>;\n\t/**\n\t * Read the ODSP epoch of a specific file version, or `undefined`. See\n\t * {@link IOdspFileVersionFetcher.getLiveDocumentEpoch}.\n\t */\n\tgetRecoverableVersionEpoch(versionId: string): Promise<string | undefined>;\n}\n\n/**\n * Raw shape of a OneDrive/SharePoint driveItem version (an entry in the `/versions` response).\n * @see https://learn.microsoft.com/en-us/onedrive/developer/rest-api/resources/driveitemversion\n */\ninterface IDriveItemVersion {\n\t/** The version's label, e.g. \"42.0\". */\n\treadonly id: string;\n\treadonly lastModifiedDateTime: string;\n}\n\n/** A single page of the driveItem `/versions` response. */\ninterface IDriveItemVersionsPage {\n\treadonly value?: IDriveItemVersion[];\n\t/** Absolute URL of the next page, present only while more versions remain. */\n\treadonly \"@odata.nextLink\"?: string;\n}\n\n/**\n * Inputs needed to make authenticated requests against a specific ODSP file.\n */\nexport interface OdspFileVersionFetcherProps {\n\treadonly urlParts: IOdspUrlParts;\n\treadonly getAuthHeader: InstrumentedStorageTokenFetcher;\n\treadonly epochTracker: EpochTracker;\n\treadonly logger: TelemetryLoggerExt;\n}\n\n/**\n * Create an {@link IOdspFileVersionFetcher} that talks to a specific ODSP file.\n */\nexport function createOdspFileVersionFetcher(\n\tprops: OdspFileVersionFetcherProps,\n): IOdspFileVersionFetcher {\n\tconst { urlParts, getAuthHeader, epochTracker, logger } = props;\n\tconst { siteUrl, driveId, itemId } = urlParts;\n\n\tconst listFileVersions = async (): Promise<OdspFileVersionRef[]> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\tconst method = \"GET\";\n\t\t\tconst versions: OdspFileVersionRef[] = [];\n\t\t\t// The file's version history (distinct from the driver's snapshot list), from the same API\n\t\t\t// root as the snapshot call so consumer (ODC) and enterprise (SPO) hosts are handled alike.\n\t\t\t// A long history is paged, so follow `@odata.nextLink` until it is absent; otherwise a base\n\t\t\t// version beyond the first page would be missed and wrongly reported as \"no base version\".\n\t\t\tlet url = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}/versions`;\n\t\t\tdo {\n\t\t\t\tconst token = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"FileVersions\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\t\tconst response = await epochTracker.fetchAndParseAsJSON<{\n\t\t\t\t\tvalue?: IDriveItemVersion[];\n\t\t\t\t}>(url, { method, headers }, \"versions\");\n\t\t\t\tconst page = response.content as IDriveItemVersionsPage;\n\t\t\t\t// The API returns versions newest-first.\n\t\t\t\tfor (const version of page.value ?? []) {\n\t\t\t\t\tversions.push({\n\t\t\t\t\t\tversionId: version.id,\n\t\t\t\t\t\tlastModifiedDateTime: version.lastModifiedDateTime,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\turl = page[\"@odata.nextLink\"] ?? \"\";\n\t\t\t} while (url);\n\t\t\treturn versions;\n\t\t});\n\n\tconst resolveSequenceNumber = async (versionId: string): Promise<number> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\t// The sequence number lives in the version snapshot's `.protocol/attributes` blob, so fetch the\n\t\t\t// version-scoped snapshot with `blobs=2` to inline it. No op stream needed.\n\t\t\tconst url = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}/versions/${encodeURIComponent(\n\t\t\t\tversionId,\n\t\t\t)}/opStream/snapshots/trees/latest?blobs=2`;\n\t\t\tconst method = \"GET\";\n\t\t\tconst token = await getAuthHeader(\n\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\"FileVersionSnapshot\",\n\t\t\t);\n\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\t// The snapshot comes back as JSON or \"ms-fluid\" (ODSP's compact binary form). Accept both and\n\t\t\t// pin the binary version (as the driver's snapshot fetch does) so the parser can read it.\n\t\t\theaders.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;\n\t\t\tconst response = await epochTracker.fetch(url, { method, headers }, \"treesLatest\");\n\t\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\t\tlet sequenceNumber: number | undefined;\n\t\t\tif (contentType.includes(\"application/json\")) {\n\t\t\t\t// JSON framing: read it with the driver's JSON snapshot parser.\n\t\t\t\tconst snapshotJson = (await response.content.json()) as IOdspSnapshot;\n\t\t\t\tsequenceNumber =\n\t\t\t\t\tconvertOdspSnapshotToSnapshotTreeAndBlobs(snapshotJson).sequenceNumber;\n\t\t\t} else if (contentType.includes(\"application/ms-fluid\")) {\n\t\t\t\t// ms-fluid framing: the compact binary form; read it with the driver's compact-snapshot parser.\n\t\t\t\tconst bytes = new Uint8Array(await response.content.arrayBuffer());\n\t\t\t\tsequenceNumber = parseCompactSnapshotResponse(bytes, logger).sequenceNumber;\n\t\t\t} else {\n\t\t\t\t// Neither framing (e.g. an HTML error page). Throw the driver's typed bad-response error\n\t\t\t\t// (like fetchSnapshot.ts): canRetry=false stops the loader re-driving, while the\n\t\t\t\t// incorrectServerResponse errorType still earns one wire-retry from getWithRetryForTokenRefresh.\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t`ODSP file version ${versionId} snapshot returned an unexpected content-type`,\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion, contentType, accept: headers.accept },\n\t\t\t\t);\n\t\t\t}\n\t\t\t// The sequence number must be a non-negative integer; a missing or malformed one throws the same\n\t\t\t// typed error as above rather than feeding a wrong value into base selection.\n\t\t\tif (\n\t\t\t\t!(\n\t\t\t\t\ttypeof sequenceNumber === \"number\" &&\n\t\t\t\t\tNumber.isInteger(sequenceNumber) &&\n\t\t\t\t\tsequenceNumber >= 0\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t`ODSP file version ${versionId} snapshot has a missing or invalid sequenceNumber (${String(sequenceNumber)})`,\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion, contentType, accept: headers.accept },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn sequenceNumber;\n\t\t});\n\n\tconst itemRoot = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}`;\n\n\t// Reads the `x-fluid-epoch` header from `url`. Deliberately uses the raw fetch helper instead of\n\t// `epochTracker.fetch`: the whole point is to COMPARE the base version's epoch against the live\n\t// document's epoch, but the shared EpochTracker pins to the first epoch it sees and throws on the\n\t// second (divergent) read - so it could never yield two epochs to compare. `fetchArray` also lets\n\t// the body (JSON or ms-fluid binary) be consumed and discarded; only the header is needed.\n\tconst readEpoch = async (url: string, scenarioName: string): Promise<string | undefined> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\tconst method = \"GET\";\n\t\t\tconst token = await getAuthHeader(\n\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\tscenarioName,\n\t\t\t);\n\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\tconst response = await fetchArray(url, { method, headers });\n\t\t\treturn response.headers.get(\"x-fluid-epoch\") ?? undefined;\n\t\t});\n\n\tconst getLiveDocumentEpoch = async (): Promise<string | undefined> =>\n\t\t// The (unversioned) live snapshot endpoint is a current-file read, so its epoch is the live\n\t\t// document's epoch. `blobs=0` keeps the response to the tree metadata.\n\t\treadEpoch(`${itemRoot}/opStream/snapshots/trees/latest?blobs=0`, \"LiveEpoch\");\n\n\tconst getRecoverableVersionEpoch = async (versionId: string): Promise<string | undefined> =>\n\t\treadEpoch(\n\t\t\t`${itemRoot}/versions/${encodeURIComponent(versionId)}/opStream/snapshots/trees/latest?blobs=0`,\n\t\t\t\"FileVersionEpoch\",\n\t\t);\n\n\treturn {\n\t\tlistFileVersions,\n\t\tresolveSequenceNumber,\n\t\tgetLiveDocumentEpoch,\n\t\tgetRecoverableVersionEpoch,\n\t};\n}\n"]}
1
+ {"version":3,"file":"odspFileVersionFetcher.js","sourceRoot":"","sources":["../../src/odspVersionManager/odspFileVersionFetcher.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/*\n * An {@link IOdspFileVersionFetcher} backed by the ODSP REST APIs:\n * - GET /_api/v2.1/.../versions -- enumerate the file's versions.\n * - GET /_api/v2.1/.../versions/{label}/opStream/snapshots/trees/latest?blobs=2 -- fetch a version's\n * snapshot and read its sequence number, parsed with the driver's snapshot parser.\n * - GET /_api/v2.1/.../[versions/{label}/]opStream/snapshots/trees/latest?blobs=0 -- read a version's\n * or the live document's ODSP epoch (`x-fluid-epoch`) to compare their lineage.\n */\n\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport { OdspErrorTypes } from \"@fluidframework/odsp-driver-definitions/internal\";\nimport type {\n\tIOdspUrlParts,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport { currentReadVersion, parseCompactSnapshotResponse } from \"../compactSnapshotParser.js\";\nimport type { IOdspSnapshot } from \"../contracts.js\";\nimport type { EpochTracker } from \"../epochTracker.js\";\nimport { getHeadersWithAuth } from \"../getUrlAndHeadersWithAuth.js\";\nimport { convertOdspSnapshotToSnapshotTreeAndBlobs } from \"../odspSnapshotParser.js\";\nimport { getApiRoot } from \"../odspUrlHelper.js\";\nimport { fetchArray, getWithRetryForTokenRefresh } from \"../odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"../packageVersion.js\";\n\n/**\n * A single ODSP file version, as listed by the file's version history.\n */\nexport interface OdspFileVersionRef {\n\t/**\n\t * The version's label (e.g. `\"42.0\"`), used to address the version when fetching it.\n\t */\n\treadonly versionId: string;\n\t/**\n\t * Last-modified timestamp of this version, ISO-8601.\n\t */\n\treadonly lastModifiedDateTime: string;\n}\n\n/**\n * Provides a file's versions and resolves each version's Fluid sequence number. Injected into\n * the version manager so the selection logic does not depend on how versions are fetched.\n */\nexport interface IOdspFileVersionFetcher {\n\t/**\n\t * Enumerate the file's versions, newest-first.\n\t */\n\tlistFileVersions(): Promise<OdspFileVersionRef[]>;\n\t/**\n\t * Resolve a single version's Fluid sequence number. Throws on failure rather than returning a\n\t * wrong value.\n\t */\n\tresolveSequenceNumber(versionId: string): Promise<number>;\n\t/**\n\t * Read the live document's current ODSP epoch (`x-fluid-epoch`), or `undefined`. Epoch identifies\n\t * the file's binary lineage and changes on a version restore or download-then-reupload; compared\n\t * with {@link IOdspFileVersionFetcher.getRecoverableVersionEpoch} to confirm a base is on the live\n\t * document's lineage.\n\t */\n\tgetLiveDocumentEpoch(): Promise<string | undefined>;\n\t/**\n\t * Read the ODSP epoch of a specific file version, or `undefined`. See\n\t * {@link IOdspFileVersionFetcher.getLiveDocumentEpoch}.\n\t */\n\tgetRecoverableVersionEpoch(versionId: string): Promise<string | undefined>;\n}\n\n/**\n * Raw shape of a OneDrive/SharePoint driveItem version (an entry in the `/versions` response).\n * @see https://learn.microsoft.com/en-us/onedrive/developer/rest-api/resources/driveitemversion\n */\ninterface IDriveItemVersion {\n\t/** The version's label, e.g. \"42.0\". */\n\treadonly id: string;\n\treadonly lastModifiedDateTime: string;\n}\n\n/** A single page of the driveItem `/versions` response. */\ninterface IDriveItemVersionsPage {\n\treadonly value?: IDriveItemVersion[];\n\t/** Absolute URL of the next page, present only while more versions remain. */\n\treadonly \"@odata.nextLink\"?: string;\n}\n\n/**\n * Inputs needed to make authenticated requests against a specific ODSP file.\n */\nexport interface OdspFileVersionFetcherProps {\n\treadonly urlParts: IOdspUrlParts;\n\treadonly getAuthHeader: InstrumentedStorageTokenFetcher;\n\treadonly epochTracker: EpochTracker;\n\treadonly logger: TelemetryLoggerExt;\n}\n\n/**\n * Create an {@link IOdspFileVersionFetcher} that talks to a specific ODSP file.\n */\nexport function createOdspFileVersionFetcher(\n\tprops: OdspFileVersionFetcherProps,\n): IOdspFileVersionFetcher {\n\tconst { urlParts, getAuthHeader, epochTracker, logger } = props;\n\tconst { siteUrl, driveId, itemId } = urlParts;\n\n\tconst listFileVersions = async (): Promise<OdspFileVersionRef[]> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\tconst method = \"GET\";\n\t\t\tconst versions: OdspFileVersionRef[] = [];\n\t\t\t// The file's version history (distinct from the driver's snapshot list), from the same API\n\t\t\t// root as the snapshot call so consumer (ODC) and enterprise (SPO) hosts are handled alike.\n\t\t\t// A long history is paged, so follow `@odata.nextLink` until it is absent; otherwise a base\n\t\t\t// version beyond the first page would be missed and wrongly reported as \"no base version\".\n\t\t\tlet url = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}/versions`;\n\t\t\tdo {\n\t\t\t\tconst token = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"FileVersions\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\t\tconst response = await epochTracker.fetchAndParseAsJSON<{\n\t\t\t\t\tvalue?: IDriveItemVersion[];\n\t\t\t\t}>(url, { method, headers }, \"versions\");\n\t\t\t\tconst page = response.content as IDriveItemVersionsPage;\n\t\t\t\t// The API returns versions newest-first.\n\t\t\t\tfor (const version of page.value ?? []) {\n\t\t\t\t\tversions.push({\n\t\t\t\t\t\tversionId: version.id,\n\t\t\t\t\t\tlastModifiedDateTime: version.lastModifiedDateTime,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\turl = page[\"@odata.nextLink\"] ?? \"\";\n\t\t\t} while (url);\n\t\t\treturn versions;\n\t\t});\n\n\tconst resolveSequenceNumber = async (versionId: string): Promise<number> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\t// The sequence number lives in the version snapshot's `.protocol/attributes` blob, so fetch the\n\t\t\t// version-scoped snapshot with `blobs=2` to inline it. No op stream needed.\n\t\t\tconst url = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}/versions/${encodeURIComponent(\n\t\t\t\tversionId,\n\t\t\t)}/opStream/snapshots/trees/latest?blobs=2`;\n\t\t\tconst method = \"GET\";\n\t\t\tconst token = await getAuthHeader(\n\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\"FileVersionSnapshot\",\n\t\t\t);\n\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\t// The snapshot comes back as JSON or \"ms-fluid\" (ODSP's compact binary form). Accept both and\n\t\t\t// pin the binary version (as the driver's snapshot fetch does) so the parser can read it.\n\t\t\theaders.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;\n\t\t\tconst response = await epochTracker.fetch(url, { method, headers }, \"treesLatest\");\n\t\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\t\tlet sequenceNumber: number | undefined;\n\t\t\tif (contentType.includes(\"application/json\")) {\n\t\t\t\t// JSON framing: read it with the driver's JSON snapshot parser.\n\t\t\t\tconst snapshotJson = (await response.content.json()) as IOdspSnapshot;\n\t\t\t\tsequenceNumber =\n\t\t\t\t\tconvertOdspSnapshotToSnapshotTreeAndBlobs(snapshotJson).sequenceNumber;\n\t\t\t} else if (contentType.includes(\"application/ms-fluid\")) {\n\t\t\t\t// ms-fluid framing: the compact binary form; read it with the driver's compact-snapshot parser.\n\t\t\t\tconst bytes = new Uint8Array(await response.content.arrayBuffer());\n\t\t\t\tsequenceNumber = parseCompactSnapshotResponse(bytes, logger).sequenceNumber;\n\t\t\t} else {\n\t\t\t\t// Neither framing (e.g. an HTML error page). Throw the driver's typed bad-response error\n\t\t\t\t// (like fetchSnapshot.ts): canRetry=false stops the loader re-driving, while the\n\t\t\t\t// incorrectServerResponse errorType still earns one wire-retry from getWithRetryForTokenRefresh.\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t`ODSP file version ${versionId} snapshot returned an unexpected content-type`,\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion, contentType, accept: headers.accept },\n\t\t\t\t);\n\t\t\t}\n\t\t\t// The sequence number must be a non-negative integer; a missing or malformed one throws the same\n\t\t\t// typed error as above rather than feeding a wrong value into base selection.\n\t\t\tif (\n\t\t\t\t!(\n\t\t\t\t\ttypeof sequenceNumber === \"number\" &&\n\t\t\t\t\tNumber.isInteger(sequenceNumber) &&\n\t\t\t\t\tsequenceNumber >= 0\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t`ODSP file version ${versionId} snapshot has a missing or invalid sequenceNumber (${String(sequenceNumber)})`,\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion, contentType, accept: headers.accept },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn sequenceNumber;\n\t\t});\n\n\tconst itemRoot = `${getApiRoot(new URL(siteUrl))}/drives/${driveId}/items/${itemId}`;\n\n\t// Reads the `x-fluid-epoch` header from `url`. Deliberately uses the raw fetch helper instead of\n\t// `epochTracker.fetch`: the whole point is to COMPARE the base version's epoch against the live\n\t// document's epoch, but the shared EpochTracker pins to the first epoch it sees and throws on the\n\t// second (divergent) read - so it could never yield two epochs to compare. `fetchArray` also lets\n\t// the body (JSON or ms-fluid binary) be consumed and discarded; only the header is needed.\n\tconst readEpoch = async (url: string, scenarioName: string): Promise<string | undefined> =>\n\t\tgetWithRetryForTokenRefresh(async (options) => {\n\t\t\tconst method = \"GET\";\n\t\t\tconst token = await getAuthHeader(\n\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\tscenarioName,\n\t\t\t);\n\t\t\tconst headers = getHeadersWithAuth(token);\n\t\t\tconst response = await fetchArray(url, { method, headers });\n\t\t\treturn response.headers.get(\"x-fluid-epoch\") ?? undefined;\n\t\t});\n\n\tconst getLiveDocumentEpoch = async (): Promise<string | undefined> =>\n\t\t// The (unversioned) live snapshot endpoint is a current-file read, so its epoch is the live\n\t\t// document's epoch. `blobs=0` keeps the response to the tree metadata.\n\t\treadEpoch(`${itemRoot}/opStream/snapshots/trees/latest?blobs=0`, \"LiveEpoch\");\n\n\tconst getRecoverableVersionEpoch = async (versionId: string): Promise<string | undefined> =>\n\t\treadEpoch(\n\t\t\t`${itemRoot}/versions/${encodeURIComponent(versionId)}/opStream/snapshots/trees/latest?blobs=0`,\n\t\t\t\"FileVersionEpoch\",\n\t\t);\n\n\treturn {\n\t\tlistFileVersions,\n\t\tresolveSequenceNumber,\n\t\tgetLiveDocumentEpoch,\n\t\tgetRecoverableVersionEpoch,\n\t};\n}\n"]}
@@ -4,7 +4,8 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.createOdspVersionManager = exports.OdspVersionManager = void 0;
7
+ exports.OdspVersionManager = void 0;
8
+ exports.createOdspVersionManager = createOdspVersionManager;
8
9
  /**
9
10
  * Selects the ODSP file version whose snapshot sits at or before a target Fluid sequence number —
10
11
  * the base to load or replay from when materializing a document at a point in time.
@@ -26,14 +27,15 @@ const odspFileVersionFetcher_js_1 = require("./odspFileVersionFetcher.js");
26
27
  // Exported only so the same-package tests can construct it with a fake IOdspFileVersionFetcher.
27
28
  // Deliberately kept out of the folder barrel and the package public index, so it is not public API.
28
29
  class OdspVersionManager {
30
+ fetcher;
31
+ // Sealed versions' sequence numbers, memoized so each is resolved at most once per manager instance
32
+ // (a sealed version's number is fixed once the version exists).
33
+ seqCache = new internal_1.PromiseCache();
34
+ // Sealed versions' ODSP epochs, memoized like their sequence numbers. The live document's epoch is NOT
35
+ // cached — it can change (restore/reupload), so validateLineageEpoch always reads it fresh.
36
+ epochCache = new internal_1.PromiseCache();
29
37
  constructor(fetcher) {
30
38
  this.fetcher = fetcher;
31
- // Sealed versions' sequence numbers, memoized so each is resolved at most once per manager instance
32
- // (a sealed version's number is fixed once the version exists).
33
- this.seqCache = new internal_1.PromiseCache();
34
- // Sealed versions' ODSP epochs, memoized like their sequence numbers. The live document's epoch is NOT
35
- // cached — it can change (restore/reupload), so validateLineageEpoch always reads it fresh.
36
- this.epochCache = new internal_1.PromiseCache();
37
39
  }
38
40
  async findBaseForSeq(target) {
39
41
  // Re-enumerate the list each call (it changes as new versions are cut).
@@ -118,5 +120,4 @@ exports.OdspVersionManager = OdspVersionManager;
118
120
  function createOdspVersionManager(props) {
119
121
  return new OdspVersionManager((0, odspFileVersionFetcher_js_1.createOdspFileVersionFetcher)(props));
120
122
  }
121
- exports.createOdspVersionManager = createOdspVersionManager;
122
123
  //# sourceMappingURL=odspVersionManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"odspVersionManager.js","sourceRoot":"","sources":["../../src/odspVersionManager/odspVersionManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Selects the ODSP file version whose snapshot sits at or before a target Fluid sequence number —\n * the base to load or replay from when materializing a document at a point in time.\n *\n * The selection logic depends on an injected {@link IOdspFileVersionFetcher}, so it is independent of\n * how versions are enumerated and resolved (real ODSP, a test double, or an alternative backend).\n */\n\nimport { PromiseCache } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport { OdspErrorTypes } from \"@fluidframework/odsp-driver-definitions/internal\";\n\nimport { pkgVersion as driverVersion } from \"../packageVersion.js\";\n\nimport {\n\tcreateOdspFileVersionFetcher,\n\ttype OdspFileVersionFetcherProps,\n\ttype OdspFileVersionRef,\n\ttype IOdspFileVersionFetcher,\n} from \"./odspFileVersionFetcher.js\";\n\n// Re-exported so consumers (and this module's own index) can keep importing these fetcher-owned\n// types from the version manager. The definitions live in odspFileVersionFetcher.ts so that file\n// does not depend on this one, avoiding a circular dependency between the two modules.\nexport type { OdspFileVersionRef, IOdspFileVersionFetcher } from \"./odspFileVersionFetcher.js\";\n\n/**\n * An ODSP file version together with its resolved Fluid sequence number.\n */\nexport interface ResolvedVersion extends OdspFileVersionRef {\n\t/**\n\t * The Fluid sequence number the version's snapshot represents.\n\t */\n\treadonly sequenceNumber: number;\n}\n\n/**\n * Result of resolving the base version for a target sequence number.\n *\n * @remarks\n * The tip (newest) version is excluded from base selection, so when the target is at or after the head\n * the base is the newest *sealed* version with `seq <= target` (a normal `found`); if the file's only\n * version is the tip, the result is `noBaseVersion`. The wired consumer surfaces `noBaseVersion` as a\n * `UsageError`; loading the live file for a near-head target is a possible future consumer choice, not\n * current behavior.\n */\nexport type BaseForSeq =\n\t| {\n\t\t\t/** A recoverable version with `sequenceNumber <= target` was found. */\n\t\t\treadonly kind: \"found\";\n\t\t\treadonly base: ResolvedVersion;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * No sealed version has `sequenceNumber <= target` — the target predates retained history, or\n\t\t\t * the only version is the excluded tip.\n\t\t\t */\n\t\t\treadonly kind: \"noBaseVersion\";\n\t\t\t/** The oldest sequence number that was resolved while searching, if any. */\n\t\t\treadonly oldestResolvedSeq?: number;\n\t };\n\n/**\n * Selects the file version to use as the base for loading or replaying to a target sequence number.\n */\nexport interface IOdspVersionManager {\n\t/**\n\t * Given a target sequence number, return the closest version at or before it (`found`), or\n\t * `noBaseVersion` if the target predates the oldest retained version.\n\t *\n\t * @remarks\n\t * A `found` base is guaranteed to share the live document's ODSP epoch (lineage): before returning\n\t * it, the chosen base's epoch is compared with the live document's, and a mismatch throws a non-retryable error\n\t * rather than returning a base that cannot be replayed. Op availability is enforced separately and\n\t * lazily as the loader reads the bridging ops.\n\t */\n\tfindBaseForSeq(target: number): Promise<BaseForSeq>;\n}\n\n/**\n * Default {@link IOdspVersionManager}. Caches resolved sequence numbers (which never change); the version\n * list is re-enumerated on each query rather than cached, since new versions are cut over time. The\n * resolution strategy (eager, newest-to-oldest, stopping at the first usable base) is hidden behind\n * {@link findBaseForSeq} and can change without affecting callers.\n */\n// Exported only so the same-package tests can construct it with a fake IOdspFileVersionFetcher.\n// Deliberately kept out of the folder barrel and the package public index, so it is not public API.\nexport class OdspVersionManager implements IOdspVersionManager {\n\t// Sealed versions' sequence numbers, memoized so each is resolved at most once per manager instance\n\t// (a sealed version's number is fixed once the version exists).\n\tprivate readonly seqCache = new PromiseCache<string, number>();\n\t// Sealed versions' ODSP epochs, memoized like their sequence numbers. The live document's epoch is NOT\n\t// cached — it can change (restore/reupload), so validateLineageEpoch always reads it fresh.\n\tprivate readonly epochCache = new PromiseCache<string, string | undefined>();\n\n\tpublic constructor(private readonly fetcher: IOdspFileVersionFetcher) {}\n\n\tpublic async findBaseForSeq(target: number): Promise<BaseForSeq> {\n\t\t// Re-enumerate the list each call (it changes as new versions are cut).\n\t\tconst versions = await this.fetcher.listFileVersions();\n\n\t\t// Start past the tip (index 0): the newest version's sequence number can still advance until a newer\n\t\t// version is cut, so it is treated as the live head rather than a stable base. Scan the remaining\n\t\t// (sealed) versions newest-first and return the first with sequence number <= target — the closest\n\t\t// base — or noBaseVersion, reporting the oldest sequence number seen.\n\t\tconst candidates = versions.slice(1);\n\n\t\tlet oldestResolvedSeq: number | undefined;\n\t\tfor (const version of candidates) {\n\t\t\tconst sequenceNumber = await this.resolveSeq(version.versionId);\n\t\t\toldestResolvedSeq =\n\t\t\t\toldestResolvedSeq === undefined\n\t\t\t\t\t? sequenceNumber\n\t\t\t\t\t: Math.min(oldestResolvedSeq, sequenceNumber);\n\t\t\tif (sequenceNumber <= target) {\n\t\t\t\tconst base = { ...version, sequenceNumber };\n\t\t\t\t// Confirm the chosen base shares the live document's lineage before handing it back\n\t\t\t\tawait this.validateLineageEpoch(base);\n\t\t\t\treturn { kind: \"found\", base };\n\t\t\t}\n\t\t}\n\t\treturn { kind: \"noBaseVersion\", oldestResolvedSeq };\n\t}\n\n\tprivate async validateLineageEpoch(base: ResolvedVersion): Promise<void> {\n\t\t// The live document's epoch can change (a restore or download-and-reupload bumps it), so it is\n\t\t// always read fresh. A numbered version's snapshot is immutable, so its epoch never changes and\n\t\t// is cached per versionId (see resolveVersionEpoch).\n\t\tconst [liveEpoch, baseEpoch] = await Promise.all([\n\t\t\tthis.fetcher.getLiveDocumentEpoch(),\n\t\t\tthis.resolveVersionEpoch(base.versionId),\n\t\t]);\n\t\tif (liveEpoch === undefined || baseEpoch === undefined) {\n\t\t\tthrow new NonRetryableError(\n\t\t\t\t`Cannot verify that ODSP file version ${base.versionId} shares the live document's ` +\n\t\t\t\t\t`lineage: the storage response is missing an epoch (base epoch: ${baseEpoch ?? \"unknown\"}, ` +\n\t\t\t\t\t`live epoch: ${liveEpoch ?? \"unknown\"}).`,\n\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t{\n\t\t\t\t\tdriverVersion,\n\t\t\t\t\tserverEpoch: liveEpoch,\n\t\t\t\t\tclientEpoch: baseEpoch,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tif (liveEpoch !== baseEpoch) {\n\t\t\tthrow new NonRetryableError(\n\t\t\t\t`ODSP file version ${base.versionId} is on epoch \"${baseEpoch}\" but the live document is ` +\n\t\t\t\t\t`on epoch \"${liveEpoch}\". A binary file change (e.g. a version restore or ` +\n\t\t\t\t\t`download-and-reupload) renumbered the op stream, so ops cannot be replayed from this ` +\n\t\t\t\t\t`base onto the live document.`,\n\t\t\t\tOdspErrorTypes.fileOverwrittenInStorage,\n\t\t\t\t{\n\t\t\t\t\tdriverVersion,\n\t\t\t\t\tserverEpoch: liveEpoch,\n\t\t\t\t\tclientEpoch: baseEpoch,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic async listVersions(): Promise<ResolvedVersion[]> {\n\t\tconst versions = await this.fetcher.listFileVersions();\n\t\t// Resolution order does not matter, so resolve concurrently; the newest-first array order is\n\t\t// preserved by Promise.all regardless of completion order.\n\t\treturn Promise.all(\n\t\t\tversions.map(async (version, index) => ({\n\t\t\t\t...version,\n\t\t\t\t// Resolve the tip (index 0) fresh each call, since its sequence number can still change;\n\t\t\t\t// sealed versions come from the cache.\n\t\t\t\tsequenceNumber:\n\t\t\t\t\tindex === 0\n\t\t\t\t\t\t? await this.fetcher.resolveSequenceNumber(version.versionId)\n\t\t\t\t\t\t: await this.resolveSeq(version.versionId),\n\t\t\t})),\n\t\t);\n\t}\n\n\tprivate async resolveSeq(versionId: string): Promise<number> {\n\t\t// Cached indefinitely (a sealed version's number is fixed); concurrent calls coalesce and a failed\n\t\t// resolution is evicted so a later call retries.\n\t\treturn this.seqCache.addOrGet(versionId, async () =>\n\t\t\tthis.fetcher.resolveSequenceNumber(versionId),\n\t\t);\n\t}\n\n\tprivate async resolveVersionEpoch(versionId: string): Promise<string | undefined> {\n\t\t// Cached like resolveSeq (a sealed version's epoch is fixed). The live document's epoch is read\n\t\t// fresh instead (see validateLineageEpoch).\n\t\treturn this.epochCache.addOrGet(versionId, async () =>\n\t\t\tthis.fetcher.getRecoverableVersionEpoch(versionId),\n\t\t);\n\t}\n}\n\n/**\n * Create an {@link IOdspVersionManager} for a specific ODSP file, wired to the real ODSP REST APIs.\n */\nexport function createOdspVersionManager(\n\tprops: OdspFileVersionFetcherProps,\n): IOdspVersionManager {\n\treturn new OdspVersionManager(createOdspFileVersionFetcher(props));\n}\n"]}
1
+ {"version":3,"file":"odspVersionManager.js","sourceRoot":"","sources":["../../src/odspVersionManager/odspVersionManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Selects the ODSP file version whose snapshot sits at or before a target Fluid sequence number —\n * the base to load or replay from when materializing a document at a point in time.\n *\n * The selection logic depends on an injected {@link IOdspFileVersionFetcher}, so it is independent of\n * how versions are enumerated and resolved (real ODSP, a test double, or an alternative backend).\n */\n\nimport { PromiseCache } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport { OdspErrorTypes } from \"@fluidframework/odsp-driver-definitions/internal\";\n\nimport { pkgVersion as driverVersion } from \"../packageVersion.js\";\n\nimport {\n\tcreateOdspFileVersionFetcher,\n\ttype OdspFileVersionFetcherProps,\n\ttype OdspFileVersionRef,\n\ttype IOdspFileVersionFetcher,\n} from \"./odspFileVersionFetcher.js\";\n\n// Re-exported so consumers (and this module's own index) can keep importing these fetcher-owned\n// types from the version manager. The definitions live in odspFileVersionFetcher.ts so that file\n// does not depend on this one, avoiding a circular dependency between the two modules.\nexport type { OdspFileVersionRef, IOdspFileVersionFetcher } from \"./odspFileVersionFetcher.js\";\n\n/**\n * An ODSP file version together with its resolved Fluid sequence number.\n */\nexport interface ResolvedVersion extends OdspFileVersionRef {\n\t/**\n\t * The Fluid sequence number the version's snapshot represents.\n\t */\n\treadonly sequenceNumber: number;\n}\n\n/**\n * Result of resolving the base version for a target sequence number.\n *\n * @remarks\n * The tip (newest) version is excluded from base selection, so when the target is at or after the head\n * the base is the newest *sealed* version with `seq <= target` (a normal `found`); if the file's only\n * version is the tip, the result is `noBaseVersion`. The wired consumer surfaces `noBaseVersion` as a\n * `UsageError`; loading the live file for a near-head target is a possible future consumer choice, not\n * current behavior.\n */\nexport type BaseForSeq =\n\t| {\n\t\t\t/** A recoverable version with `sequenceNumber <= target` was found. */\n\t\t\treadonly kind: \"found\";\n\t\t\treadonly base: ResolvedVersion;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * No sealed version has `sequenceNumber <= target` — the target predates retained history, or\n\t\t\t * the only version is the excluded tip.\n\t\t\t */\n\t\t\treadonly kind: \"noBaseVersion\";\n\t\t\t/** The oldest sequence number that was resolved while searching, if any. */\n\t\t\treadonly oldestResolvedSeq?: number;\n\t };\n\n/**\n * Selects the file version to use as the base for loading or replaying to a target sequence number.\n */\nexport interface IOdspVersionManager {\n\t/**\n\t * Given a target sequence number, return the closest version at or before it (`found`), or\n\t * `noBaseVersion` if the target predates the oldest retained version.\n\t *\n\t * @remarks\n\t * A `found` base is guaranteed to share the live document's ODSP epoch (lineage): before returning\n\t * it, the chosen base's epoch is compared with the live document's, and a mismatch throws a non-retryable error\n\t * rather than returning a base that cannot be replayed. Op availability is enforced separately and\n\t * lazily as the loader reads the bridging ops.\n\t */\n\tfindBaseForSeq(target: number): Promise<BaseForSeq>;\n}\n\n/**\n * Default {@link IOdspVersionManager}. Caches resolved sequence numbers (which never change); the version\n * list is re-enumerated on each query rather than cached, since new versions are cut over time. The\n * resolution strategy (eager, newest-to-oldest, stopping at the first usable base) is hidden behind\n * {@link findBaseForSeq} and can change without affecting callers.\n */\n// Exported only so the same-package tests can construct it with a fake IOdspFileVersionFetcher.\n// Deliberately kept out of the folder barrel and the package public index, so it is not public API.\nexport class OdspVersionManager implements IOdspVersionManager {\n\t// Sealed versions' sequence numbers, memoized so each is resolved at most once per manager instance\n\t// (a sealed version's number is fixed once the version exists).\n\tprivate readonly seqCache = new PromiseCache<string, number>();\n\t// Sealed versions' ODSP epochs, memoized like their sequence numbers. The live document's epoch is NOT\n\t// cached — it can change (restore/reupload), so validateLineageEpoch always reads it fresh.\n\tprivate readonly epochCache = new PromiseCache<string, string | undefined>();\n\n\tpublic constructor(private readonly fetcher: IOdspFileVersionFetcher) {}\n\n\tpublic async findBaseForSeq(target: number): Promise<BaseForSeq> {\n\t\t// Re-enumerate the list each call (it changes as new versions are cut).\n\t\tconst versions = await this.fetcher.listFileVersions();\n\n\t\t// Start past the tip (index 0): the newest version's sequence number can still advance until a newer\n\t\t// version is cut, so it is treated as the live head rather than a stable base. Scan the remaining\n\t\t// (sealed) versions newest-first and return the first with sequence number <= target — the closest\n\t\t// base — or noBaseVersion, reporting the oldest sequence number seen.\n\t\tconst candidates = versions.slice(1);\n\n\t\tlet oldestResolvedSeq: number | undefined;\n\t\tfor (const version of candidates) {\n\t\t\tconst sequenceNumber = await this.resolveSeq(version.versionId);\n\t\t\toldestResolvedSeq =\n\t\t\t\toldestResolvedSeq === undefined\n\t\t\t\t\t? sequenceNumber\n\t\t\t\t\t: Math.min(oldestResolvedSeq, sequenceNumber);\n\t\t\tif (sequenceNumber <= target) {\n\t\t\t\tconst base = { ...version, sequenceNumber };\n\t\t\t\t// Confirm the chosen base shares the live document's lineage before handing it back\n\t\t\t\tawait this.validateLineageEpoch(base);\n\t\t\t\treturn { kind: \"found\", base };\n\t\t\t}\n\t\t}\n\t\treturn { kind: \"noBaseVersion\", oldestResolvedSeq };\n\t}\n\n\tprivate async validateLineageEpoch(base: ResolvedVersion): Promise<void> {\n\t\t// The live document's epoch can change (a restore or download-and-reupload bumps it), so it is\n\t\t// always read fresh. A numbered version's snapshot is immutable, so its epoch never changes and\n\t\t// is cached per versionId (see resolveVersionEpoch).\n\t\tconst [liveEpoch, baseEpoch] = await Promise.all([\n\t\t\tthis.fetcher.getLiveDocumentEpoch(),\n\t\t\tthis.resolveVersionEpoch(base.versionId),\n\t\t]);\n\t\tif (liveEpoch === undefined || baseEpoch === undefined) {\n\t\t\tthrow new NonRetryableError(\n\t\t\t\t`Cannot verify that ODSP file version ${base.versionId} shares the live document's ` +\n\t\t\t\t\t`lineage: the storage response is missing an epoch (base epoch: ${baseEpoch ?? \"unknown\"}, ` +\n\t\t\t\t\t`live epoch: ${liveEpoch ?? \"unknown\"}).`,\n\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t{\n\t\t\t\t\tdriverVersion,\n\t\t\t\t\tserverEpoch: liveEpoch,\n\t\t\t\t\tclientEpoch: baseEpoch,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tif (liveEpoch !== baseEpoch) {\n\t\t\tthrow new NonRetryableError(\n\t\t\t\t`ODSP file version ${base.versionId} is on epoch \"${baseEpoch}\" but the live document is ` +\n\t\t\t\t\t`on epoch \"${liveEpoch}\". A binary file change (e.g. a version restore or ` +\n\t\t\t\t\t`download-and-reupload) renumbered the op stream, so ops cannot be replayed from this ` +\n\t\t\t\t\t`base onto the live document.`,\n\t\t\t\tOdspErrorTypes.fileOverwrittenInStorage,\n\t\t\t\t{\n\t\t\t\t\tdriverVersion,\n\t\t\t\t\tserverEpoch: liveEpoch,\n\t\t\t\t\tclientEpoch: baseEpoch,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic async listVersions(): Promise<ResolvedVersion[]> {\n\t\tconst versions = await this.fetcher.listFileVersions();\n\t\t// Resolution order does not matter, so resolve concurrently; the newest-first array order is\n\t\t// preserved by Promise.all regardless of completion order.\n\t\treturn Promise.all(\n\t\t\tversions.map(async (version, index) => ({\n\t\t\t\t...version,\n\t\t\t\t// Resolve the tip (index 0) fresh each call, since its sequence number can still change;\n\t\t\t\t// sealed versions come from the cache.\n\t\t\t\tsequenceNumber:\n\t\t\t\t\tindex === 0\n\t\t\t\t\t\t? await this.fetcher.resolveSequenceNumber(version.versionId)\n\t\t\t\t\t\t: await this.resolveSeq(version.versionId),\n\t\t\t})),\n\t\t);\n\t}\n\n\tprivate async resolveSeq(versionId: string): Promise<number> {\n\t\t// Cached indefinitely (a sealed version's number is fixed); concurrent calls coalesce and a failed\n\t\t// resolution is evicted so a later call retries.\n\t\treturn this.seqCache.addOrGet(versionId, async () =>\n\t\t\tthis.fetcher.resolveSequenceNumber(versionId),\n\t\t);\n\t}\n\n\tprivate async resolveVersionEpoch(versionId: string): Promise<string | undefined> {\n\t\t// Cached like resolveSeq (a sealed version's epoch is fixed). The live document's epoch is read\n\t\t// fresh instead (see validateLineageEpoch).\n\t\treturn this.epochCache.addOrGet(versionId, async () =>\n\t\t\tthis.fetcher.getRecoverableVersionEpoch(versionId),\n\t\t);\n\t}\n}\n\n/**\n * Create an {@link IOdspVersionManager} for a specific ODSP file, wired to the real ODSP REST APIs.\n */\nexport function createOdspVersionManager(\n\tprops: OdspFileVersionFetcherProps,\n): IOdspVersionManager {\n\treturn new OdspVersionManager(createOdspFileVersionFetcher(props));\n}\n"]}
@@ -7,13 +7,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.OpsCache = void 0;
8
8
  const client_utils_1 = require("@fluid-internal/client-utils");
9
9
  class OpsCache {
10
+ logger;
11
+ cache;
12
+ batchSize;
13
+ timerGranularity;
14
+ totalOpsToCache;
15
+ batches = new Map();
16
+ timer;
10
17
  constructor(startingSequenceNumber, logger, cache, batchSize, timerGranularity, totalOpsToCache) {
11
18
  this.logger = logger;
12
19
  this.cache = cache;
13
20
  this.batchSize = batchSize;
14
21
  this.timerGranularity = timerGranularity;
15
22
  this.totalOpsToCache = totalOpsToCache;
16
- this.batches = new Map();
17
23
  /**
18
24
  * Initial batch is a special case because it will never be full - all ops prior (inclusive) to
19
25
  * `startingSequenceNumber` are never going to show up (undefined)
@@ -1 +1 @@
1
- {"version":3,"file":"opsCaching.js","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAA8D;AAyB9D,MAAa,QAAQ;IAIpB,YACC,sBAA8B,EACb,MAA0B,EAC1B,KAAa,EACb,SAAiB,EACjB,gBAAwB,EACjC,eAAuB;QAJd,WAAM,GAAN,MAAM,CAAoB;QAC1B,UAAK,GAAL,KAAK,CAAQ;QACb,cAAS,GAAT,SAAS,CAAQ;QACjB,qBAAgB,GAAhB,gBAAgB,CAAQ;QACjC,oBAAe,GAAf,eAAe,CAAQ;QATf,YAAO,GAA+B,IAAI,GAAG,EAAE,CAAC;QAWhE;;;WAGG;QACH,MAAM,cAAc,GACnB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,uBAAuB,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC3E,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,EAAE;gBAC7D,cAAc;gBACd,SAAS,EAAE,IAAI,CAAC,2BAA2B,EAAE;gBAC7C,KAAK,EAAE,KAAK;aACZ,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAEM,OAAO;QACb,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACxB,CAAC;IACF,CAAC;IAEM,QAAQ;QACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzC,qGAAqG;YACrG,IACC,KAAK,KAAK,IAAI;gBACd,CAAC,KAAK,CAAC,KAAK;gBACZ,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBAC5B,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS;oBAChC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,EAC1D,CAAC;gBACF,SAAS;YACV,CAAC;YACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAEM,MAAM,CAAC,GAAe;QAC5B,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,EAAE,CAAC;YAC/B,OAAO;QACR,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACtB,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;YAExE,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAEjD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAChC,YAAY,GAAG;oBACd,cAAc,EAAE,IAAI,CAAC,SAAS,GAAG,CAAC;oBAClC,SAAS,EAAE,IAAI,CAAC,2BAA2B,EAAE;oBAC7C,KAAK,EAAE,IAAI;iBACX,CAAC;gBACF,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;gBAC7C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC7C,CAAC;iBAAM,IACN,YAAY,KAAK,IAAI;gBACrB,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,SAAS,EACpD,CAAC;gBACF,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;gBAC7C,YAAY,CAAC,cAAc,EAAE,CAAC;gBAC9B,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACP,yEAAyE;gBACzE,OAAO;YACR,CAAC;YAED,IAAI,YAAY,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC;gBACvC,gCAAgC;gBAChC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;gBACtC,2CAA2C;gBAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,aAAa,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBACpE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACpB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,MAAM;YACP,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,EAAW;QAC9C,MAAM,QAAQ,GAAe,EAAE,CAAC;QAChC,IAAI,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,WAAW,EAAE,CAAC,CAAC;YACtE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,MAAM,MAAM,GAAe,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;YACzD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC3C,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;gBACzB,mFAAmF;gBACnF,IAAI,EAAE,EAAE,CAAC;oBACR,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;wBACjD,OAAO,QAAQ,CAAC;oBACjB,CAAC;oBACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAC3B,IAAI,EAAE,CAAC,cAAc,GAAG,IAAI,EAAE,CAAC;4BAC9B,OAAO,QAAQ,CAAC;wBACjB,CAAC;6BAAM,IAAI,EAAE,CAAC,cAAc,GAAG,IAAI,EAAE,CAAC;4BACrC,SAAS;wBACV,CAAC;oBACF,CAAC;oBACD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACnB,CAAC;qBAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,qDAAqD;oBACrD,OAAO,QAAQ,CAAC;gBACjB,CAAC;YACF,CAAC;YAED,+FAA+F;YAC/F,oGAAoG;YACpG,IAAI,kBAAkB,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC5C,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,WAAW,EAAE,CAAC;QACf,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,EAAW;QACzC,MAAM,KAAK,GAAG,IAAA,6BAAc,GAAE,CAAC;QAE/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAE9C,MAAM,QAAQ,GAAG,IAAA,6BAAc,GAAE,GAAG,KAAK,CAAC;QAC1C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,EAAE,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAChC,SAAS,EAAE,cAAc;gBACzB,IAAI;gBACJ,EAAE;gBACF,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,QAAQ;aACR,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IACjB,CAAC;IAES,KAAK,CAAC,WAAmB,EAAE,OAAe;QACnD,4EAA4E;QAC5E,yCAAyC;QACzC,IAAI,CAAC,KAAK;aACR,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,WAAW,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;aAC5E,KAAK,CAAC,GAAG,EAAE;YACX,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAES,aAAa;QACtB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,gBAAgB,GAAG,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;gBACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC;IAEO,cAAc,CAAC,cAAsB;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAEO,uBAAuB,CAAC,cAAsB;QACrD,OAAO,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;IACxC,CAAC;IAEO,2BAA2B;QAClC,MAAM,SAAS,GAAe,EAAE,CAAC;QACjC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,sCAAsC;QACzE,OAAO,SAAS,CAAC;IAClB,CAAC;CACD;AA3MD,4BA2MC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n\tsequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n\tremainingSlots: number;\n\tbatchData: CacheEntry;\n\t/**\n\t * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n\t */\n\tdirty: boolean;\n}\n\nexport interface ICache {\n\twrite(batchNumber: string, data: string): Promise<void>;\n\tread(batchNumber: string): Promise<string | undefined>;\n\tremove(): void;\n}\n\nexport class OpsCache {\n\tprivate readonly batches: Map<number, null | IBatch> = new Map();\n\tprivate timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconstructor(\n\t\tstartingSequenceNumber: number,\n\t\tprivate readonly logger: TelemetryLoggerExt,\n\t\tprivate readonly cache: ICache,\n\t\tprivate readonly batchSize: number,\n\t\tprivate readonly timerGranularity: number,\n\t\tprivate totalOpsToCache: number,\n\t) {\n\t\t/**\n\t\t * Initial batch is a special case because it will never be full - all ops prior (inclusive) to\n\t\t * `startingSequenceNumber` are never going to show up (undefined)\n\t\t */\n\t\tconst remainingSlots =\n\t\t\tthis.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n\t\tif (remainingSlots !== 0) {\n\t\t\tthis.batches.set(this.getBatchNumber(startingSequenceNumber), {\n\t\t\t\tremainingSlots,\n\t\t\t\tbatchData: this.initializeNewBatchDataArray(),\n\t\t\t\tdirty: false,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.batches.clear();\n\t\tif (this.timer !== undefined) {\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = undefined;\n\t\t}\n\t}\n\n\tpublic flushOps(): void {\n\t\tfor (const [key, value] of this.batches) {\n\t\t\t// Don't flush if the batch has no ops, already flushed or has empty slots at both beginning and end.\n\t\t\tif (\n\t\t\t\tvalue === null ||\n\t\t\t\t!value.dirty ||\n\t\t\t\tvalue.batchData.length === 0 ||\n\t\t\t\t(value.batchData[0] === undefined &&\n\t\t\t\t\tvalue.batchData[value.batchData.length - 1] === undefined)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvalue.dirty = false;\n\t\t\tthis.write(key, value);\n\t\t}\n\t}\n\n\tpublic addOps(ops: IMessage[]): void {\n\t\tif (this.totalOpsToCache <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const op of ops) {\n\t\t\tconst batchNumber = this.getBatchNumber(op.sequenceNumber);\n\t\t\tconst positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n\t\t\tlet currentBatch = this.batches.get(batchNumber);\n\n\t\t\tif (currentBatch === undefined) {\n\t\t\t\tcurrentBatch = {\n\t\t\t\t\tremainingSlots: this.batchSize - 1,\n\t\t\t\t\tbatchData: this.initializeNewBatchDataArray(),\n\t\t\t\t\tdirty: true,\n\t\t\t\t};\n\t\t\t\tcurrentBatch.batchData[positionInBatch] = op;\n\t\t\t\tthis.batches.set(batchNumber, currentBatch);\n\t\t\t} else if (\n\t\t\t\tcurrentBatch !== null &&\n\t\t\t\tcurrentBatch.batchData[positionInBatch] === undefined\n\t\t\t) {\n\t\t\t\tcurrentBatch.batchData[positionInBatch] = op;\n\t\t\t\tcurrentBatch.remainingSlots--;\n\t\t\t\tcurrentBatch.dirty = true;\n\t\t\t} else {\n\t\t\t\t// Either batch was flushed or this op was already there - nothing to do!\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (currentBatch.remainingSlots === 0) {\n\t\t\t\t// batch is full, flush to cache\n\t\t\t\tthis.write(batchNumber, currentBatch);\n\t\t\t\t// eslint-disable-next-line unicorn/no-null\n\t\t\t\tthis.batches.set(batchNumber, null);\n\t\t\t} else {\n\t\t\t\tthis.scheduleTimer();\n\t\t\t}\n\n\t\t\tthis.totalOpsToCache--;\n\t\t\tif (this.totalOpsToCache === 0) {\n\t\t\t\tthis.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\" });\n\t\t\t\tthis.cache.remove();\n\t\t\t\tthis.dispose();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves ops from cache\n\t * @param from - inclusive\n\t * @param to - exclusive\n\t * @returns ops retrieved\n\t */\n\tprivate async getCore(from: number, to?: number): Promise<IMessage[]> {\n\t\tconst messages: IMessage[] = [];\n\t\tlet batchNumber = this.getBatchNumber(from);\n\t\twhile (true) {\n\t\t\tconst res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n\t\t\tif (res === undefined) {\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tconst result: CacheEntry = JSON.parse(res) as CacheEntry;\n\t\t\tconst prevMessagesLength = messages.length;\n\t\t\tfor (const op of result) {\n\t\t\t\t// Note that we write out undefined, but due to JSON.stringify, it turns into null!\n\t\t\t\tif (op) {\n\t\t\t\t\tif (to !== undefined && op.sequenceNumber >= to) {\n\t\t\t\t\t\treturn messages;\n\t\t\t\t\t}\n\t\t\t\t\tif (messages.length === 0) {\n\t\t\t\t\t\tif (op.sequenceNumber > from) {\n\t\t\t\t\t\t\treturn messages;\n\t\t\t\t\t\t} else if (op.sequenceNumber < from) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmessages.push(op);\n\t\t\t\t} else if (messages.length > 0) {\n\t\t\t\t\t// If there is any gap, return the messages till now.\n\t\t\t\t\treturn messages;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we didn't get any op from this batch, then return messages till now. As it tells us that,\n\t\t\t// either the first message \"from\" is not present in cache or a gap will occur from 1 batch to next.\n\t\t\tif (prevMessagesLength === messages.length) {\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tbatchNumber++;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves ops from cache\n\t * @param from - inclusive\n\t * @param to - exclusive\n\t * @returns ops retrieved\n\t */\n\tpublic async get(from: number, to?: number): Promise<IMessage[]> {\n\t\tconst start = performanceNow();\n\n\t\tconst messages = await this.getCore(from, to);\n\n\t\tconst duration = performanceNow() - start;\n\t\tif (messages.length > 0 || duration > 1000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CacheOpsUsed\",\n\t\t\t\tfrom,\n\t\t\t\tto,\n\t\t\t\tlength: messages.length,\n\t\t\t\tduration,\n\t\t\t});\n\t\t}\n\t\treturn messages;\n\t}\n\n\tprotected write(batchNumber: number, payload: IBatch): void {\n\t\t// Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n\t\t// in the adapter chain of cache adapters\n\t\tthis.cache\n\t\t\t.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData))\n\t\t\t.catch(() => {\n\t\t\t\tthis.totalOpsToCache = 0;\n\t\t\t});\n\t}\n\n\tprotected scheduleTimer(): void {\n\t\tif (!this.timer && this.timerGranularity > 0) {\n\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\tthis.timer = undefined;\n\t\t\t\tthis.flushOps();\n\t\t\t}, this.timerGranularity);\n\t\t}\n\t}\n\n\tprivate getBatchNumber(sequenceNumber: number): number {\n\t\treturn Math.floor(sequenceNumber / this.batchSize);\n\t}\n\n\tprivate getPositionInBatchArray(sequenceNumber: number): number {\n\t\treturn sequenceNumber % this.batchSize;\n\t}\n\n\tprivate initializeNewBatchDataArray(): IMessage[] {\n\t\tconst tempArray: IMessage[] = [];\n\t\ttempArray.length = this.batchSize; // fill with empty, undefined elements\n\t\treturn tempArray;\n\t}\n}\n"]}
1
+ {"version":3,"file":"opsCaching.js","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n\tsequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n\tremainingSlots: number;\n\tbatchData: CacheEntry;\n\t/**\n\t * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n\t */\n\tdirty: boolean;\n}\n\nexport interface ICache {\n\twrite(batchNumber: string, data: string): Promise<void>;\n\tread(batchNumber: string): Promise<string | undefined>;\n\tremove(): void;\n}\n\nexport class OpsCache {\n\tprivate readonly batches: Map<number, null | IBatch> = new Map();\n\tprivate timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconstructor(\n\t\tstartingSequenceNumber: number,\n\t\tprivate readonly logger: TelemetryLoggerExt,\n\t\tprivate readonly cache: ICache,\n\t\tprivate readonly batchSize: number,\n\t\tprivate readonly timerGranularity: number,\n\t\tprivate totalOpsToCache: number,\n\t) {\n\t\t/**\n\t\t * Initial batch is a special case because it will never be full - all ops prior (inclusive) to\n\t\t * `startingSequenceNumber` are never going to show up (undefined)\n\t\t */\n\t\tconst remainingSlots =\n\t\t\tthis.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n\t\tif (remainingSlots !== 0) {\n\t\t\tthis.batches.set(this.getBatchNumber(startingSequenceNumber), {\n\t\t\t\tremainingSlots,\n\t\t\t\tbatchData: this.initializeNewBatchDataArray(),\n\t\t\t\tdirty: false,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.batches.clear();\n\t\tif (this.timer !== undefined) {\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = undefined;\n\t\t}\n\t}\n\n\tpublic flushOps(): void {\n\t\tfor (const [key, value] of this.batches) {\n\t\t\t// Don't flush if the batch has no ops, already flushed or has empty slots at both beginning and end.\n\t\t\tif (\n\t\t\t\tvalue === null ||\n\t\t\t\t!value.dirty ||\n\t\t\t\tvalue.batchData.length === 0 ||\n\t\t\t\t(value.batchData[0] === undefined &&\n\t\t\t\t\tvalue.batchData[value.batchData.length - 1] === undefined)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvalue.dirty = false;\n\t\t\tthis.write(key, value);\n\t\t}\n\t}\n\n\tpublic addOps(ops: IMessage[]): void {\n\t\tif (this.totalOpsToCache <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const op of ops) {\n\t\t\tconst batchNumber = this.getBatchNumber(op.sequenceNumber);\n\t\t\tconst positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n\t\t\tlet currentBatch = this.batches.get(batchNumber);\n\n\t\t\tif (currentBatch === undefined) {\n\t\t\t\tcurrentBatch = {\n\t\t\t\t\tremainingSlots: this.batchSize - 1,\n\t\t\t\t\tbatchData: this.initializeNewBatchDataArray(),\n\t\t\t\t\tdirty: true,\n\t\t\t\t};\n\t\t\t\tcurrentBatch.batchData[positionInBatch] = op;\n\t\t\t\tthis.batches.set(batchNumber, currentBatch);\n\t\t\t} else if (\n\t\t\t\tcurrentBatch !== null &&\n\t\t\t\tcurrentBatch.batchData[positionInBatch] === undefined\n\t\t\t) {\n\t\t\t\tcurrentBatch.batchData[positionInBatch] = op;\n\t\t\t\tcurrentBatch.remainingSlots--;\n\t\t\t\tcurrentBatch.dirty = true;\n\t\t\t} else {\n\t\t\t\t// Either batch was flushed or this op was already there - nothing to do!\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (currentBatch.remainingSlots === 0) {\n\t\t\t\t// batch is full, flush to cache\n\t\t\t\tthis.write(batchNumber, currentBatch);\n\t\t\t\t// eslint-disable-next-line unicorn/no-null\n\t\t\t\tthis.batches.set(batchNumber, null);\n\t\t\t} else {\n\t\t\t\tthis.scheduleTimer();\n\t\t\t}\n\n\t\t\tthis.totalOpsToCache--;\n\t\t\tif (this.totalOpsToCache === 0) {\n\t\t\t\tthis.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\" });\n\t\t\t\tthis.cache.remove();\n\t\t\t\tthis.dispose();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves ops from cache\n\t * @param from - inclusive\n\t * @param to - exclusive\n\t * @returns ops retrieved\n\t */\n\tprivate async getCore(from: number, to?: number): Promise<IMessage[]> {\n\t\tconst messages: IMessage[] = [];\n\t\tlet batchNumber = this.getBatchNumber(from);\n\t\twhile (true) {\n\t\t\tconst res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n\t\t\tif (res === undefined) {\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tconst result: CacheEntry = JSON.parse(res) as CacheEntry;\n\t\t\tconst prevMessagesLength = messages.length;\n\t\t\tfor (const op of result) {\n\t\t\t\t// Note that we write out undefined, but due to JSON.stringify, it turns into null!\n\t\t\t\tif (op) {\n\t\t\t\t\tif (to !== undefined && op.sequenceNumber >= to) {\n\t\t\t\t\t\treturn messages;\n\t\t\t\t\t}\n\t\t\t\t\tif (messages.length === 0) {\n\t\t\t\t\t\tif (op.sequenceNumber > from) {\n\t\t\t\t\t\t\treturn messages;\n\t\t\t\t\t\t} else if (op.sequenceNumber < from) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmessages.push(op);\n\t\t\t\t} else if (messages.length > 0) {\n\t\t\t\t\t// If there is any gap, return the messages till now.\n\t\t\t\t\treturn messages;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we didn't get any op from this batch, then return messages till now. As it tells us that,\n\t\t\t// either the first message \"from\" is not present in cache or a gap will occur from 1 batch to next.\n\t\t\tif (prevMessagesLength === messages.length) {\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tbatchNumber++;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves ops from cache\n\t * @param from - inclusive\n\t * @param to - exclusive\n\t * @returns ops retrieved\n\t */\n\tpublic async get(from: number, to?: number): Promise<IMessage[]> {\n\t\tconst start = performanceNow();\n\n\t\tconst messages = await this.getCore(from, to);\n\n\t\tconst duration = performanceNow() - start;\n\t\tif (messages.length > 0 || duration > 1000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CacheOpsUsed\",\n\t\t\t\tfrom,\n\t\t\t\tto,\n\t\t\t\tlength: messages.length,\n\t\t\t\tduration,\n\t\t\t});\n\t\t}\n\t\treturn messages;\n\t}\n\n\tprotected write(batchNumber: number, payload: IBatch): void {\n\t\t// Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n\t\t// in the adapter chain of cache adapters\n\t\tthis.cache\n\t\t\t.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData))\n\t\t\t.catch(() => {\n\t\t\t\tthis.totalOpsToCache = 0;\n\t\t\t});\n\t}\n\n\tprotected scheduleTimer(): void {\n\t\tif (!this.timer && this.timerGranularity > 0) {\n\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\tthis.timer = undefined;\n\t\t\t\tthis.flushOps();\n\t\t\t}, this.timerGranularity);\n\t\t}\n\t}\n\n\tprivate getBatchNumber(sequenceNumber: number): number {\n\t\treturn Math.floor(sequenceNumber / this.batchSize);\n\t}\n\n\tprivate getPositionInBatchArray(sequenceNumber: number): number {\n\t\treturn sequenceNumber % this.batchSize;\n\t}\n\n\tprivate initializeNewBatchDataArray(): IMessage[] {\n\t\tconst tempArray: IMessage[] = [];\n\t\ttempArray.length = this.batchSize; // fill with empty, undefined elements\n\t\treturn tempArray;\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/odsp-driver";
8
- export declare const pkgVersion = "2.117.0";
8
+ export declare const pkgVersion = "3.0.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map