@fluidframework/odsp-driver 1.3.0 → 2.0.0-dev.1.4.5.105745

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 (252) hide show
  1. package/.mocharc.js +12 -0
  2. package/README.md +19 -0
  3. package/dist/ReadBufferUtils.d.ts.map +1 -1
  4. package/dist/ReadBufferUtils.js +1 -0
  5. package/dist/ReadBufferUtils.js.map +1 -1
  6. package/dist/WriteBufferUtils.d.ts +3 -5
  7. package/dist/WriteBufferUtils.d.ts.map +1 -1
  8. package/dist/WriteBufferUtils.js +59 -55
  9. package/dist/WriteBufferUtils.js.map +1 -1
  10. package/dist/compactSnapshotParser.d.ts +2 -2
  11. package/dist/compactSnapshotParser.d.ts.map +1 -1
  12. package/dist/compactSnapshotParser.js +93 -33
  13. package/dist/compactSnapshotParser.js.map +1 -1
  14. package/dist/compactSnapshotWriter.d.ts +1 -2
  15. package/dist/compactSnapshotWriter.d.ts.map +1 -1
  16. package/dist/compactSnapshotWriter.js +22 -16
  17. package/dist/compactSnapshotWriter.js.map +1 -1
  18. package/dist/contracts.d.ts +1 -18
  19. package/dist/contracts.d.ts.map +1 -1
  20. package/dist/contracts.js.map +1 -1
  21. package/dist/createFile.d.ts +1 -1
  22. package/dist/createFile.d.ts.map +1 -1
  23. package/dist/createFile.js +53 -16
  24. package/dist/createFile.js.map +1 -1
  25. package/dist/createNewUtils.d.ts.map +1 -1
  26. package/dist/createNewUtils.js +0 -1
  27. package/dist/createNewUtils.js.map +1 -1
  28. package/dist/createOdspCreateContainerRequest.d.ts +4 -3
  29. package/dist/createOdspCreateContainerRequest.d.ts.map +1 -1
  30. package/dist/createOdspCreateContainerRequest.js +6 -3
  31. package/dist/createOdspCreateContainerRequest.js.map +1 -1
  32. package/dist/epochTracker.d.ts +1 -0
  33. package/dist/epochTracker.d.ts.map +1 -1
  34. package/dist/epochTracker.js +24 -5
  35. package/dist/epochTracker.js.map +1 -1
  36. package/dist/fetchSnapshot.d.ts.map +1 -1
  37. package/dist/fetchSnapshot.js +14 -19
  38. package/dist/fetchSnapshot.js.map +1 -1
  39. package/dist/getFileLink.d.ts +3 -6
  40. package/dist/getFileLink.d.ts.map +1 -1
  41. package/dist/getFileLink.js +11 -14
  42. package/dist/getFileLink.js.map +1 -1
  43. package/dist/index.d.ts +1 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +3 -3
  46. package/dist/index.js.map +1 -1
  47. package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -1
  48. package/dist/localOdspDriver/localOdspDocumentStorageManager.js +1 -2
  49. package/dist/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  50. package/dist/odspDeltaStorageService.d.ts +1 -1
  51. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  52. package/dist/odspDeltaStorageService.js +4 -8
  53. package/dist/odspDeltaStorageService.js.map +1 -1
  54. package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
  55. package/dist/odspDocumentDeltaConnection.js +3 -6
  56. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  57. package/dist/odspDocumentService.d.ts +1 -0
  58. package/dist/odspDocumentService.d.ts.map +1 -1
  59. package/dist/odspDocumentService.js +10 -5
  60. package/dist/odspDocumentService.js.map +1 -1
  61. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  62. package/dist/odspDocumentServiceFactoryCore.js +29 -6
  63. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  64. package/dist/odspDocumentStorageManager.d.ts +4 -3
  65. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  66. package/dist/odspDocumentStorageManager.js +73 -64
  67. package/dist/odspDocumentStorageManager.js.map +1 -1
  68. package/dist/odspDocumentStorageServiceBase.d.ts +1 -1
  69. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
  70. package/dist/odspDocumentStorageServiceBase.js +4 -2
  71. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  72. package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  73. package/dist/odspDriverUrlResolverForShareLink.js +4 -4
  74. package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
  75. package/dist/odspFluidFileLink.js +1 -1
  76. package/dist/odspFluidFileLink.js.map +1 -1
  77. package/dist/odspLocationRedirection.d.ts +14 -0
  78. package/dist/odspLocationRedirection.d.ts.map +1 -0
  79. package/dist/odspLocationRedirection.js +24 -0
  80. package/dist/odspLocationRedirection.js.map +1 -0
  81. package/dist/odspSnapshotParser.d.ts.map +1 -1
  82. package/dist/odspSnapshotParser.js +1 -2
  83. package/dist/odspSnapshotParser.js.map +1 -1
  84. package/dist/odspSummaryUploadManager.d.ts +6 -3
  85. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  86. package/dist/odspSummaryUploadManager.js +18 -16
  87. package/dist/odspSummaryUploadManager.js.map +1 -1
  88. package/dist/odspUrlHelper.js +2 -1
  89. package/dist/odspUrlHelper.js.map +1 -1
  90. package/dist/odspUtils.d.ts +11 -2
  91. package/dist/odspUtils.d.ts.map +1 -1
  92. package/dist/odspUtils.js +32 -5
  93. package/dist/odspUtils.js.map +1 -1
  94. package/dist/opsCaching.d.ts.map +1 -1
  95. package/dist/opsCaching.js +3 -2
  96. package/dist/opsCaching.js.map +1 -1
  97. package/dist/packageVersion.d.ts +1 -1
  98. package/dist/packageVersion.d.ts.map +1 -1
  99. package/dist/packageVersion.js +1 -1
  100. package/dist/packageVersion.js.map +1 -1
  101. package/dist/prefetchLatestSnapshot.d.ts +6 -4
  102. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  103. package/dist/prefetchLatestSnapshot.js +6 -4
  104. package/dist/prefetchLatestSnapshot.js.map +1 -1
  105. package/dist/retryUtils.d.ts.map +1 -1
  106. package/dist/retryUtils.js +8 -4
  107. package/dist/retryUtils.js.map +1 -1
  108. package/dist/zipItDataRepresentationUtils.d.ts +30 -18
  109. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  110. package/dist/zipItDataRepresentationUtils.js +170 -76
  111. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  112. package/lib/ReadBufferUtils.d.ts.map +1 -1
  113. package/lib/ReadBufferUtils.js +1 -0
  114. package/lib/ReadBufferUtils.js.map +1 -1
  115. package/lib/WriteBufferUtils.d.ts +3 -5
  116. package/lib/WriteBufferUtils.d.ts.map +1 -1
  117. package/lib/WriteBufferUtils.js +61 -57
  118. package/lib/WriteBufferUtils.js.map +1 -1
  119. package/lib/compactSnapshotParser.d.ts +2 -2
  120. package/lib/compactSnapshotParser.d.ts.map +1 -1
  121. package/lib/compactSnapshotParser.js +94 -34
  122. package/lib/compactSnapshotParser.js.map +1 -1
  123. package/lib/compactSnapshotWriter.d.ts +1 -2
  124. package/lib/compactSnapshotWriter.d.ts.map +1 -1
  125. package/lib/compactSnapshotWriter.js +23 -17
  126. package/lib/compactSnapshotWriter.js.map +1 -1
  127. package/lib/contracts.d.ts +1 -18
  128. package/lib/contracts.d.ts.map +1 -1
  129. package/lib/contracts.js.map +1 -1
  130. package/lib/createFile.d.ts +1 -1
  131. package/lib/createFile.d.ts.map +1 -1
  132. package/lib/createFile.js +54 -17
  133. package/lib/createFile.js.map +1 -1
  134. package/lib/createNewUtils.d.ts.map +1 -1
  135. package/lib/createNewUtils.js +0 -1
  136. package/lib/createNewUtils.js.map +1 -1
  137. package/lib/createOdspCreateContainerRequest.d.ts +4 -3
  138. package/lib/createOdspCreateContainerRequest.d.ts.map +1 -1
  139. package/lib/createOdspCreateContainerRequest.js +6 -3
  140. package/lib/createOdspCreateContainerRequest.js.map +1 -1
  141. package/lib/epochTracker.d.ts +1 -0
  142. package/lib/epochTracker.d.ts.map +1 -1
  143. package/lib/epochTracker.js +26 -7
  144. package/lib/epochTracker.js.map +1 -1
  145. package/lib/fetchSnapshot.d.ts.map +1 -1
  146. package/lib/fetchSnapshot.js +14 -19
  147. package/lib/fetchSnapshot.js.map +1 -1
  148. package/lib/getFileLink.d.ts +3 -6
  149. package/lib/getFileLink.d.ts.map +1 -1
  150. package/lib/getFileLink.js +11 -14
  151. package/lib/getFileLink.js.map +1 -1
  152. package/lib/index.d.ts +1 -2
  153. package/lib/index.d.ts.map +1 -1
  154. package/lib/index.js +1 -3
  155. package/lib/index.js.map +1 -1
  156. package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -1
  157. package/lib/localOdspDriver/localOdspDocumentStorageManager.js +1 -2
  158. package/lib/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  159. package/lib/odspDeltaStorageService.d.ts +1 -1
  160. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  161. package/lib/odspDeltaStorageService.js +4 -8
  162. package/lib/odspDeltaStorageService.js.map +1 -1
  163. package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
  164. package/lib/odspDocumentDeltaConnection.js +3 -6
  165. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  166. package/lib/odspDocumentService.d.ts +1 -0
  167. package/lib/odspDocumentService.d.ts.map +1 -1
  168. package/lib/odspDocumentService.js +11 -6
  169. package/lib/odspDocumentService.js.map +1 -1
  170. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  171. package/lib/odspDocumentServiceFactoryCore.js +29 -6
  172. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  173. package/lib/odspDocumentStorageManager.d.ts +4 -3
  174. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  175. package/lib/odspDocumentStorageManager.js +74 -65
  176. package/lib/odspDocumentStorageManager.js.map +1 -1
  177. package/lib/odspDocumentStorageServiceBase.d.ts +1 -1
  178. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
  179. package/lib/odspDocumentStorageServiceBase.js +4 -2
  180. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  181. package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  182. package/lib/odspDriverUrlResolverForShareLink.js +4 -4
  183. package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
  184. package/lib/odspFluidFileLink.js +1 -1
  185. package/lib/odspFluidFileLink.js.map +1 -1
  186. package/lib/odspLocationRedirection.d.ts +14 -0
  187. package/lib/odspLocationRedirection.d.ts.map +1 -0
  188. package/lib/odspLocationRedirection.js +20 -0
  189. package/lib/odspLocationRedirection.js.map +1 -0
  190. package/lib/odspSnapshotParser.d.ts.map +1 -1
  191. package/lib/odspSnapshotParser.js +1 -2
  192. package/lib/odspSnapshotParser.js.map +1 -1
  193. package/lib/odspSummaryUploadManager.d.ts +6 -3
  194. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  195. package/lib/odspSummaryUploadManager.js +18 -16
  196. package/lib/odspSummaryUploadManager.js.map +1 -1
  197. package/lib/odspUrlHelper.js +2 -1
  198. package/lib/odspUrlHelper.js.map +1 -1
  199. package/lib/odspUtils.d.ts +11 -2
  200. package/lib/odspUtils.d.ts.map +1 -1
  201. package/lib/odspUtils.js +31 -5
  202. package/lib/odspUtils.js.map +1 -1
  203. package/lib/opsCaching.d.ts.map +1 -1
  204. package/lib/opsCaching.js +3 -2
  205. package/lib/opsCaching.js.map +1 -1
  206. package/lib/packageVersion.d.ts +1 -1
  207. package/lib/packageVersion.d.ts.map +1 -1
  208. package/lib/packageVersion.js +1 -1
  209. package/lib/packageVersion.js.map +1 -1
  210. package/lib/prefetchLatestSnapshot.d.ts +6 -4
  211. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  212. package/lib/prefetchLatestSnapshot.js +6 -4
  213. package/lib/prefetchLatestSnapshot.js.map +1 -1
  214. package/lib/retryUtils.d.ts.map +1 -1
  215. package/lib/retryUtils.js +9 -5
  216. package/lib/retryUtils.js.map +1 -1
  217. package/lib/zipItDataRepresentationUtils.d.ts +30 -18
  218. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  219. package/lib/zipItDataRepresentationUtils.js +166 -75
  220. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  221. package/package.json +33 -20
  222. package/src/ReadBufferUtils.ts +1 -0
  223. package/src/WriteBufferUtils.ts +67 -58
  224. package/src/compactSnapshotParser.ts +103 -39
  225. package/src/compactSnapshotWriter.ts +30 -19
  226. package/src/contracts.ts +2 -14
  227. package/src/createFile.ts +75 -15
  228. package/src/createNewUtils.ts +2 -4
  229. package/src/createOdspCreateContainerRequest.ts +7 -4
  230. package/src/epochTracker.ts +47 -7
  231. package/src/fetchSnapshot.ts +19 -19
  232. package/src/getFileLink.ts +12 -22
  233. package/src/index.ts +1 -3
  234. package/src/localOdspDriver/localOdspDocumentStorageManager.ts +2 -2
  235. package/src/odspDeltaStorageService.ts +4 -7
  236. package/src/odspDocumentDeltaConnection.ts +3 -5
  237. package/src/odspDocumentService.ts +13 -11
  238. package/src/odspDocumentServiceFactoryCore.ts +40 -4
  239. package/src/odspDocumentStorageManager.ts +55 -45
  240. package/src/odspDocumentStorageServiceBase.ts +4 -2
  241. package/src/odspDriverUrlResolverForShareLink.ts +2 -5
  242. package/src/odspFluidFileLink.ts +1 -1
  243. package/src/odspLocationRedirection.ts +23 -0
  244. package/src/odspSnapshotParser.ts +3 -4
  245. package/src/odspSummaryUploadManager.ts +14 -10
  246. package/src/odspUrlHelper.ts +1 -1
  247. package/src/odspUtils.ts +40 -7
  248. package/src/opsCaching.ts +3 -2
  249. package/src/packageVersion.ts +1 -1
  250. package/src/prefetchLatestSnapshot.ts +6 -4
  251. package/src/retryUtils.ts +8 -5
  252. package/src/zipItDataRepresentationUtils.ts +198 -75
@@ -1 +1 @@
1
- {"version":3,"file":"odspUrlHelper.js","sourceRoot":"","sources":["../src/odspUrlHelper.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 { IOdspUrlParts } from \"@fluidframework/odsp-driver-definitions\";\n\n// Centralized store for all ODC/SPO logic\n\n/**\n * Checks whether or not the given URL origin is an ODC origin\n * @param origin - The URL origin to check\n */\nexport function isOdcOrigin(origin: string): boolean {\n return (\n // Primary API endpoint and several test endpoints\n origin.includes(\"onedrive.com\") ||\n // *storage.live.com hostnames\n origin.includes(\"storage.live.com\") ||\n // live-int\n origin.includes(\"storage.live-int.com\") ||\n // Test endpoints\n origin.includes(\"onedrive-tst.com\")\n );\n}\n\n/**\n * Gets the correct API root for the given ODSP url, e.g. 'https://foo-my.sharepoint.com/_api/v2.1'\n * @param origin - The URL origin\n */\nexport function getApiRoot(origin: string): string {\n let prefix = \"_api/\";\n if (isOdcOrigin(origin)) {\n prefix = \"\";\n }\n\n return `${origin}/${prefix}v2.1`;\n}\n\n/**\n * Whether or not the given URL is a valid SPO/ODB URL\n * @param url - The URL to check\n */\nexport function isSpoUrl(url: string): boolean {\n const urlLower = url.toLowerCase();\n\n // Format: foo.sharepoint.com/_api/v2.1./drives/bar/items/baz and foo.sharepoint-df.com/...\n const spoRegex = /(.*\\.sharepoint(-df)*\\.com)\\/_api\\/v2.1\\/drives\\/([^/]*)\\/items\\/([^/]*)/;\n return !!spoRegex.exec(urlLower);\n}\n\n/**\n * Whether or not the given URL is a valid ODC URL\n * @param url - The URL to check\n */\nexport function isOdcUrl(url: string | URL): boolean {\n const urlObj = typeof url === \"string\" ? new URL(url) : url;\n\n if (!isOdcOrigin(urlObj.origin)) {\n return false;\n }\n\n const path = urlObj.pathname.toLowerCase();\n\n // Splitting the regexes so we don't have regex soup\n // Format: /v2.1/drive/items/ABC123!123 and /v2.1/drives/ABC123/items/ABC123!123\n const odcRegex = /\\/v2.1\\/(drive|drives\\/[^/]+)\\/items\\/([\\da-z]+)!(\\d+)/;\n\n // Format: /v2.1/drives('ABC123')/items('ABC123!123')\n const odcODataRegex = /\\/v2.1\\/drives\\('[^/]+'\\)\\/items\\('[\\da-z]+!\\d+'\\)/;\n\n return !!(odcRegex.exec(path) || odcODataRegex.exec(path));\n}\n\n/**\n * Breaks an ODSP URL into its parts, extracting the site, drive ID, and item ID.\n * Returns undefined for invalid/malformed URLs.\n * @param url - The (raw) URL to parse\n */\nexport async function getOdspUrlParts(url: URL): Promise<IOdspUrlParts | undefined> {\n const pathname = url.pathname;\n\n // Joinsession like URL\n // Pick a regex based on the hostname\n // TODO This will only support ODC using api.onedrive.com, update to handle the future (share links etc)\n let joinSessionMatch;\n if (isOdcOrigin(url.origin)) {\n // Capture groups:\n // 0: match\n // 1: origin\n // 2: optional `drives` capture (the `/drives/<DRIVEID>` API format vs `/drive`)\n // 3: optional captured drive ID\n // 4: Item ID\n // 5: Drive ID portion of Item ID\n // eslint-disable-next-line unicorn/no-unsafe-regex\n joinSessionMatch = /(.*)\\/v2\\.1\\/drive(s\\/([\\dA-Za-z]+))?\\/items\\/(([\\dA-Za-z]+)!\\d+)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n // Try again but with the OData format ( `/drives('ABC123')/items('ABC123!456')` )\n joinSessionMatch = /(.*)\\/v2\\.1\\/drives\\('([\\dA-Za-z]+)'\\)\\/items\\('(([\\dA-Za-z]+)!\\d+)'\\)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n return undefined;\n }\n }\n\n const driveId = joinSessionMatch[3] || joinSessionMatch[5];\n const itemId = joinSessionMatch[4];\n\n return { siteUrl: `${url.origin}${url.pathname}`, driveId, itemId };\n } else {\n joinSessionMatch = /(.*)\\/_api\\/v2.1\\/drives\\/([^/]*)\\/items\\/([^/]*)(.*)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n return undefined;\n }\n const driveId = joinSessionMatch[2];\n const itemId = joinSessionMatch[3];\n\n return { siteUrl: `${url.origin}${url.pathname}`, driveId, itemId };\n }\n}\n"]}
1
+ {"version":3,"file":"odspUrlHelper.js","sourceRoot":"","sources":["../src/odspUrlHelper.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 { IOdspUrlParts } from \"@fluidframework/odsp-driver-definitions\";\n\n// Centralized store for all ODC/SPO logic\n\n/**\n * Checks whether or not the given URL origin is an ODC origin\n * @param origin - The URL origin to check\n */\nexport function isOdcOrigin(origin: string): boolean {\n return (\n // Primary API endpoint and several test endpoints\n origin.includes(\"onedrive.com\") ||\n // *storage.live.com hostnames\n origin.includes(\"storage.live.com\") ||\n // live-int\n origin.includes(\"storage.live-int.com\") ||\n // Test endpoints\n origin.includes(\"onedrive-tst.com\")\n );\n}\n\n/**\n * Gets the correct API root for the given ODSP url, e.g. 'https://foo-my.sharepoint.com/_api/v2.1'\n * @param origin - The URL origin\n */\nexport function getApiRoot(origin: string): string {\n let prefix = \"_api/\";\n if (isOdcOrigin(origin)) {\n prefix = \"\";\n }\n\n return `${origin}/${prefix}v2.1`;\n}\n\n/**\n * Whether or not the given URL is a valid SPO/ODB URL\n * @param url - The URL to check\n */\nexport function isSpoUrl(url: string): boolean {\n const urlLower = url.toLowerCase();\n\n // Format: foo.sharepoint.com/_api/v2.1./drives/bar/items/baz and foo.sharepoint-df.com/...\n const spoRegex = /(.*\\.sharepoint(-df)*\\.com)\\/_api\\/v2.1\\/drives\\/([^/]*)\\/items\\/([^/]*)/;\n return !!spoRegex.exec(urlLower);\n}\n\n/**\n * Whether or not the given URL is a valid ODC URL\n * @param url - The URL to check\n */\nexport function isOdcUrl(url: string | URL): boolean {\n const urlObj = typeof url === \"string\" ? new URL(url) : url;\n\n if (!isOdcOrigin(urlObj.origin)) {\n return false;\n }\n\n const path = urlObj.pathname.toLowerCase();\n\n // Splitting the regexes so we don't have regex soup\n // Format: /v2.1/drive/items/ABC123!123 and /v2.1/drives/ABC123/items/ABC123!123\n const odcRegex = /\\/v2.1\\/(drive|drives\\/[^/]+)\\/items\\/([\\da-z]+)!(\\d+)/;\n\n // Format: /v2.1/drives('ABC123')/items('ABC123!123')\n const odcODataRegex = /\\/v2.1\\/drives\\('[^/]+'\\)\\/items\\('[\\da-z]+!\\d+'\\)/;\n\n return !!(odcRegex.exec(path) ?? odcODataRegex.exec(path));\n}\n\n/**\n * Breaks an ODSP URL into its parts, extracting the site, drive ID, and item ID.\n * Returns undefined for invalid/malformed URLs.\n * @param url - The (raw) URL to parse\n */\nexport async function getOdspUrlParts(url: URL): Promise<IOdspUrlParts | undefined> {\n const pathname = url.pathname;\n\n // Joinsession like URL\n // Pick a regex based on the hostname\n // TODO This will only support ODC using api.onedrive.com, update to handle the future (share links etc)\n let joinSessionMatch;\n if (isOdcOrigin(url.origin)) {\n // Capture groups:\n // 0: match\n // 1: origin\n // 2: optional `drives` capture (the `/drives/<DRIVEID>` API format vs `/drive`)\n // 3: optional captured drive ID\n // 4: Item ID\n // 5: Drive ID portion of Item ID\n // eslint-disable-next-line unicorn/no-unsafe-regex\n joinSessionMatch = /(.*)\\/v2\\.1\\/drive(s\\/([\\dA-Za-z]+))?\\/items\\/(([\\dA-Za-z]+)!\\d+)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n // Try again but with the OData format ( `/drives('ABC123')/items('ABC123!456')` )\n joinSessionMatch = /(.*)\\/v2\\.1\\/drives\\('([\\dA-Za-z]+)'\\)\\/items\\('(([\\dA-Za-z]+)!\\d+)'\\)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n return undefined;\n }\n }\n\n const driveId = joinSessionMatch[3] || joinSessionMatch[5];\n const itemId = joinSessionMatch[4];\n\n return { siteUrl: `${url.origin}${url.pathname}`, driveId, itemId };\n } else {\n joinSessionMatch = /(.*)\\/_api\\/v2.1\\/drives\\/([^/]*)\\/items\\/([^/]*)(.*)/.exec(pathname);\n\n if (joinSessionMatch === null) {\n return undefined;\n }\n const driveId = joinSessionMatch[2];\n const itemId = joinSessionMatch[3];\n\n return { siteUrl: `${url.origin}${url.pathname}`, driveId, itemId };\n }\n}\n"]}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from "@fluidframework/common-definitions";
6
6
  import { IResolvedUrl } from "@fluidframework/driver-definitions";
7
- import { IOdspResolvedUrl, TokenFetchOptions, OdspResourceTokenFetchOptions, ShareLinkTypes, TokenFetcher, ICacheEntry, InstrumentedStorageTokenFetcher, IOdspUrlParts } from "@fluidframework/odsp-driver-definitions";
7
+ import { IOdspResolvedUrl, TokenFetchOptions, OdspResourceTokenFetchOptions, ShareLinkTypes, ISharingLinkKind, TokenFetcher, ICacheEntry, InstrumentedStorageTokenFetcher, IOdspUrlParts } from "@fluidframework/odsp-driver-definitions";
8
8
  import { IOdspSnapshot } from "./contracts";
9
9
  export declare const getWithRetryForTokenRefreshRepeat = "getWithRetryForTokenRefreshRepeat";
10
10
  /** Parse the given url and return the origin (host name) */
@@ -48,8 +48,10 @@ export interface INewFileInfo {
48
48
  * application can request creation of a share link along with the creation of a new file
49
49
  * by passing in an optional param to specify the kind of sharing link
50
50
  * (at the time of adding this comment Sept/2021), odsp only supports csl
51
+ * ShareLinkTypes will deprecated in future. Use ISharingLinkKind instead which specifies both
52
+ * share link type and the role type.
51
53
  */
52
- createLinkType?: ShareLinkTypes;
54
+ createLinkType?: ShareLinkTypes | ISharingLinkKind;
53
55
  }
54
56
  export declare function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl;
55
57
  export declare const createOdspLogger: (logger?: ITelemetryBaseLogger | undefined) => import("@fluidframework/telemetry-utils").TelemetryLogger;
@@ -62,4 +64,11 @@ export declare function evalBlobsAndTrees(snapshot: IOdspSnapshot): {
62
64
  export declare function toInstrumentedOdspTokenFetcher(logger: ITelemetryLogger, resolvedUrlParts: IOdspUrlParts, tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>, throwOnNullToken: boolean): InstrumentedStorageTokenFetcher;
63
65
  export declare function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry;
64
66
  export declare const maxUmpPostBodySize = 79872;
67
+ /**
68
+ * Build request parameters to request for the creation of a sharing link along with the creation of the file
69
+ * through the /snapshot api call.
70
+ * @param shareLinkType - Kind of sharing link requested
71
+ * @returns A string of request parameters that can be concatenated with the base URI
72
+ */
73
+ export declare function buildOdspShareLinkReqParams(shareLinkType: ShareLinkTypes | ISharingLinkKind | undefined): string | undefined;
65
74
  //# sourceMappingURL=odspUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;AAenF,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EAIjB,6BAA6B,EAC7B,cAAc,EACd,YAAY,EACZ,WAAW,EAEX,+BAA+B,EAC/B,aAAa,EAChB,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,iCAAiC,sCAAsC,CAAC;AAErF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,QAAS,MAAM,WAAwB,CAAC;AAE9D,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,oBAAoB,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC1D,2DAA2D;IAC3D,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB;AAUD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,cAoBrG;AAED,wBAAsB,WAAW,CAC7B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CA2DlC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC5B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAWrC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC7C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA4B3B;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,gBAAgB,CAG9E;AAED,eAAO,MAAM,gBAAgB,0GASnB,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa;;;;;EAqBxD;AAED,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,gBAAgB,EACxB,gBAAgB,EAAE,aAAa,EAC/B,YAAY,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACzD,gBAAgB,EAAE,OAAO,GAC1B,+BAA+B,CAkDjC;AAED,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,gBAAgB,GAAG,WAAW,CAUrF;AAID,eAAO,MAAM,kBAAkB,QAAQ,CAAC"}
1
+ {"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;AAenF,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EAIjB,6BAA6B,EAC7B,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,WAAW,EAEX,+BAA+B,EAC/B,aAAa,EAChB,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,iCAAiC,sCAAsC,CAAC;AAErF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,QAAS,MAAM,WAAwB,CAAC;AAE9D,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,oBAAoB,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC1D,2DAA2D;IAC3D,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB;AAUD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,cAoBrG;AAED,wBAAsB,WAAW,CAC7B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAqElC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC5B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAWrC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC7C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA4B3B;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,cAAc,GAAG,gBAAgB,CAAC;CACtD;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,gBAAgB,CAG9E;AAED,eAAO,MAAM,gBAAgB,0GASnB,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa;;;;;EAqBxD;AAED,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,gBAAgB,EACxB,gBAAgB,EAAE,aAAa,EAC/B,YAAY,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACzD,gBAAgB,EAAE,OAAO,GAC1B,+BAA+B,CAkDjC;AAED,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,gBAAgB,GAAG,WAAW,CAUrF;AAID,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,cAAc,GAAG,gBAAgB,GAAG,SAAS,sBAYvG"}
package/dist/odspUtils.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.maxUmpPostBodySize = exports.createCacheSnapshotKey = exports.toInstrumentedOdspTokenFetcher = exports.evalBlobsAndTrees = exports.createOdspLogger = exports.getOdspResolvedUrl = exports.fetchAndParseAsJSONHelper = exports.fetchArray = exports.fetchHelper = exports.getWithRetryForTokenRefresh = exports.getOrigin = exports.getWithRetryForTokenRefreshRepeat = void 0;
7
+ exports.buildOdspShareLinkReqParams = exports.maxUmpPostBodySize = exports.createCacheSnapshotKey = exports.toInstrumentedOdspTokenFetcher = exports.evalBlobsAndTrees = exports.createOdspLogger = exports.getOdspResolvedUrl = exports.fetchAndParseAsJSONHelper = exports.fetchArray = exports.fetchHelper = exports.getWithRetryForTokenRefresh = exports.getOrigin = exports.getWithRetryForTokenRefreshRepeat = void 0;
8
8
  const driver_definitions_1 = require("@fluidframework/driver-definitions");
9
9
  const driver_utils_1 = require("@fluidframework/driver-utils");
10
10
  const common_utils_1 = require("@fluidframework/common-utils");
@@ -76,6 +76,8 @@ async function fetchHelper(requestInfo, requestInit) {
76
76
  }, (error) => {
77
77
  const online = (0, driver_utils_1.isOnline)();
78
78
  const errorText = `${error}`;
79
+ const urlRegex = /((http|https):\/\/(\S*))/i;
80
+ const redactedErrorText = errorText.replace(urlRegex, "REDACTED_URL");
79
81
  // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled
80
82
  if (error.name === "AbortError") {
81
83
  throw new driver_utils_1.RetryableError("Fetch Timeout (AbortError)", odsp_driver_definitions_1.OdspErrorType.fetchTimeout, { driverVersion: packageVersion_1.pkgVersion });
@@ -84,22 +86,27 @@ async function fetchHelper(requestInfo, requestInit) {
84
86
  if (errorText.includes("ETIMEDOUT")) {
85
87
  throw new driver_utils_1.RetryableError("Fetch Timeout (ETIMEDOUT)", odsp_driver_definitions_1.OdspErrorType.fetchTimeout, { driverVersion: packageVersion_1.pkgVersion });
86
88
  }
87
- //
88
89
  // WARNING: Do not log error object itself or any of its properties!
89
90
  // It could contain PII, like URI in message itself, or token in properties.
90
91
  // It is also non-serializable object due to circular references.
91
- //
92
+ // eslint-disable-next-line unicorn/prefer-ternary
92
93
  if (online === driver_utils_1.OnlineStatus.Offline) {
93
94
  throw new driver_utils_1.RetryableError(
94
95
  // pre-0.58 error message prefix: Offline
95
- `ODSP fetch failure (Offline): ${errorText}`, driver_definitions_1.DriverErrorType.offlineError, { driverVersion: packageVersion_1.pkgVersion });
96
+ `ODSP fetch failure (Offline): ${redactedErrorText}`, driver_definitions_1.DriverErrorType.offlineError, {
97
+ driverVersion: packageVersion_1.pkgVersion,
98
+ rawErrorMessage: { value: errorText, tag: telemetry_utils_1.TelemetryDataTag.UserData },
99
+ });
96
100
  }
97
101
  else {
98
102
  // It is perhaps still possible that this is due to being offline, the error does not reveal enough
99
103
  // information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.
100
104
  throw new driver_utils_1.RetryableError(
101
105
  // pre-0.58 error message prefix: Fetch error
102
- `ODSP fetch failure: ${errorText}`, driver_definitions_1.DriverErrorType.fetchFailure, { driverVersion: packageVersion_1.pkgVersion });
106
+ `ODSP fetch failure: ${redactedErrorText}`, driver_definitions_1.DriverErrorType.fetchFailure, {
107
+ driverVersion: packageVersion_1.pkgVersion,
108
+ rawErrorMessage: { value: errorText, tag: telemetry_utils_1.TelemetryDataTag.UserData },
109
+ });
103
110
  }
104
111
  });
105
112
  }
@@ -240,4 +247,24 @@ exports.createCacheSnapshotKey = createCacheSnapshotKey;
240
247
  // 80KB is the max body size that we can put in ump post body for server to be able to accept it.
241
248
  // Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.
242
249
  exports.maxUmpPostBodySize = 79872;
250
+ /**
251
+ * Build request parameters to request for the creation of a sharing link along with the creation of the file
252
+ * through the /snapshot api call.
253
+ * @param shareLinkType - Kind of sharing link requested
254
+ * @returns A string of request parameters that can be concatenated with the base URI
255
+ */
256
+ function buildOdspShareLinkReqParams(shareLinkType) {
257
+ if (!shareLinkType) {
258
+ return;
259
+ }
260
+ const scope = shareLinkType.scope;
261
+ if (!scope) {
262
+ return `createLinkType=${shareLinkType}`;
263
+ }
264
+ let shareLinkRequestParams = `createLinkScope=${scope}`;
265
+ const role = shareLinkType.role;
266
+ shareLinkRequestParams = role ? `${shareLinkRequestParams}&createLinkRole=${role}` : shareLinkRequestParams;
267
+ return shareLinkRequestParams;
268
+ }
269
+ exports.buildOdspShareLinkReqParams = buildOdspShareLinkReqParams;
243
270
  //# 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 { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n propsToLog: ITelemetryProperties;\n duration: number;\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return 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>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n // pre-0.58 error message: No response from fetch call\n \"No response from ODSP fetch call\",\n DriverErrorType.incorrectServerResponse,\n { driverVersion });\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n // pre-0.58 error message prefix: odspFetchError\n `ODSP fetch error [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n propsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n const online = isOnline();\n const errorText = `${error}`;\n\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\n \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n // TCP/IP timeout\n if (errorText.includes(\"ETIMEDOUT\")) {\n throw new RetryableError(\n \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could contain PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\n // pre-0.58 error message prefix: Offline\n `ODSP fetch failure (Offline): ${errorText}`, DriverErrorType.offlineError, { driverVersion });\n } else {\n // It is perhaps still possible that this is due to being offline, the error does not reveal enough\n // information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n throw new RetryableError(\n // pre-0.58 error message prefix: Fetch error\n `ODSP fetch failure: ${errorText}`, DriverErrorType.fetchFailure, { driverVersion });\n }\n });\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 fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n propsToLog.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n propsToLog,\n duration,\n };\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 requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n // pre-0.58 error message: errorWhileParsingFetchResponse\n \"Error while parsing fetch response\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n propsToLog.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n propsToLog,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n {\n all:\n {\n driverVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrlParts: IOdspUrlParts,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n ...resolvedUrlParts,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n // pre-0.58 error message: Token is null for ${name} call\n `The Host-provided token fetcher returned null`,\n OdspErrorType.fetchTokenError,\n { method: name, driverVersion });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n `The Host-provided token fetcher threw an error: ${errorMessage}`,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\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"]}
1
+ {"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,2EAAmF;AACnF,+DAMsC;AACtC,+DAAmE;AACnE,qEAA6G;AAC7G,yEAI2C;AAC3C,qFAciD;AACjD,mCAAgC;AAChC,qDAA+D;AAGlD,QAAA,iCAAiC,GAAG,mCAAmC,CAAC;AAErF,4DAA4D;AACrD,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;AAAjD,QAAA,SAAS,aAAwC;AAc9D,SAAS,YAAY,CAAC,OAAgB;IAClC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE;QAC1C,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;KAC9B;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,2BAA2B,CAAI,GAAiD;IAClG,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7C,MAAM,OAAO,GAAwB,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QACzE,QAAQ,CAAC,CAAC,SAAS,EAAE;YACjB,kEAAkE;YAClE,KAAK,oCAAe,CAAC,kBAAkB;gBACnC,OAAO,GAAG,iCAAM,OAAO,KAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAG,CAAC;YAEvE,KAAK,oCAAe,CAAC,uBAAuB,CAAC,CAAC,qCAAqC;YACnF,KAAK,uCAAa,CAAC,eAAe,EAAE,0CAA0C;gBAC1E,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;YAExB;gBACI,+CAA+C;gBAC/C,IAAI,CAAC,CAAC,yCAAiC,CAAC,KAAK,IAAI,EAAE;oBAC/C,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;iBACvB;gBACD,MAAM,CAAC,CAAC;SACf;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AApBD,kEAoBC;AAEM,KAAK,UAAU,WAAW,CAC7B,WAAwB,EACxB,WAAoC;IAEpC,MAAM,KAAK,GAAG,0BAAW,CAAC,GAAG,EAAE,CAAC;IAEhC,oFAAoF;IACpF,OAAO,IAAA,aAAK,EAAC,WAAW,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE;QAChE,MAAM,QAAQ,GAAG,aAAgC,CAAC;QAClD,6BAA6B;QAC7B,IAAI,CAAC,QAAQ,EAAE;YACX,MAAM,IAAI,gCAAiB;YACvB,sDAAsD;YACtD,kCAAkC,EAClC,oCAAe,CAAC,uBAAuB,EACvC,EAAE,aAAa,EAAb,2BAAa,EAAE,CAAC,CAAC;SAC1B;QACD,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE;YACjE,IAAA,yCAAqB;YACjB,gDAAgD;YAChD,qBAAqB,QAAQ,CAAC,MAAM,GAAG,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;SAClG;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/C,OAAO;YACH,OAAO,EAAE,QAAQ;YACjB,OAAO;YACP,UAAU,EAAE,IAAA,wDAAoC,EAAC,OAAO,CAAC;YACzD,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,KAAK;SACtC,CAAC;IACN,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACT,MAAM,MAAM,GAAG,IAAA,uBAAQ,GAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,GAAG,KAAK,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,2BAA2B,CAAC;QAC7C,MAAM,iBAAiB,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACtE,qFAAqF;QACrF,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE;YAC7B,MAAM,IAAI,6BAAc,CACpB,4BAA4B,EAAE,uCAAa,CAAC,YAAY,EAAE,EAAE,aAAa,EAAb,2BAAa,EAAE,CAAC,CAAC;SACpF;QACD,iBAAiB;QACjB,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE;YACjC,MAAM,IAAI,6BAAc,CACpB,2BAA2B,EAAE,uCAAa,CAAC,YAAY,EAAE,EAAE,aAAa,EAAb,2BAAa,EAAE,CAAC,CAAC;SACnF;QAED,oEAAoE;QACpE,4EAA4E;QAC5E,iEAAiE;QACjE,kDAAkD;QAClD,IAAI,MAAM,KAAK,2BAAY,CAAC,OAAO,EAAE;YACjC,MAAM,IAAI,6BAAc;YACpB,yCAAyC;YACzC,iCAAiC,iBAAiB,EAAE,EACpD,oCAAe,CAAC,YAAY,EAC5B;gBACI,aAAa,EAAb,2BAAa;gBACb,eAAe,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,kCAAgB,CAAC,QAAQ,EAAE;aACxE,CAAC,CAAC;SACV;aAAM;YACH,mGAAmG;YACnG,mGAAmG;YACnG,MAAM,IAAI,6BAAc;YACpB,6CAA6C;YAC7C,uBAAuB,iBAAiB,EAAE,EAC1C,oCAAe,CAAC,YAAY,EAC5B;gBACI,aAAa,EAAb,2BAAa;gBACb,eAAe,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,kCAAgB,CAAC,QAAQ,EAAE;aACxE,CAAC,CAAC;SACV;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAxED,kCAwEC;AAED;;;;GAIG;AACI,KAAK,UAAU,UAAU,CAC5B,WAAwB,EACxB,WAAoC;IAEpC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAE/F,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;IAChD,UAAU,CAAC,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC;IAC7C,OAAO;QACH,OAAO;QACP,OAAO,EAAE,WAAW;QACpB,UAAU;QACV,QAAQ;KACX,CAAC;AACN,CAAC;AAdD,gCAcC;AAED;;;;GAIG;AACI,KAAK,UAAU,yBAAyB,CAC3C,WAAwB,EACxB,WAAoC;IAEpC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC/F,IAAI,IAAwB,CAAC;IAC7B,IAAI;QACA,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;KAC/B;IAAC,OAAO,CAAC,EAAE;QACR,gFAAgF;QAChF,8GAA8G;QAC9G,yGAAyG;QACzG,qBAAqB;QACrB,qCAAqC;QACrC,IAAA,yCAAqB;QACjB,yDAAyD;QACzD,oCAAoC,EACpC,0CAAsB,EACtB,OAAO,EAAE,WAAW;QACpB,IAAI,CACP,CAAC;KACL;IAED,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;IAClC,MAAM,GAAG,GAAG;QACR,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QACzB,UAAU;QACV,QAAQ;KACX,CAAC;IACF,OAAO,GAAG,CAAC;AACf,CAAC;AA/BD,8DA+BC;AAiBD,SAAgB,kBAAkB,CAAC,WAAyB;IACxD,IAAA,qBAAM,EAAE,WAAgC,CAAC,eAAe,KAAK,IAAI,EAAE,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC3G,OAAO,WAA+B,CAAC;AAC3C,CAAC;AAHD,gDAGC;AAEM,MAAM,gBAAgB,GAAG,CAAC,MAA6B,EAAE,EAAE,CAC9D,6BAAW,CAAC,MAAM,CACd,MAAM,EACN,YAAY,EACZ;IACI,GAAG,EACH;QACI,aAAa,EAAb,2BAAa;KAChB;CACJ,CAAC,CAAC;AATE,QAAA,gBAAgB,oBASlB;AAEX,SAAgB,iBAAiB,CAAC,QAAuB;IACrD,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE;QAC/B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE;YAClC,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE;gBAC3B,QAAQ,EAAE,CAAC;aACd;iBAAM,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE;gBAClC,QAAQ,EAAE,CAAC;aACd;SACJ;KACJ;IACD,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE;QAC9B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE;YAC/B,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC;YAC9B,gBAAgB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;SAC3C;KACJ;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AACtE,CAAC;AArBD,8CAqBC;AAED,SAAgB,8BAA8B,CAC1C,MAAwB,EACxB,gBAA+B,EAC/B,YAAyD,EACzD,gBAAyB;IAEzB,OAAO,KAAK,EAAE,OAA0B,EAAE,IAAY,EAAE,kCAA2C,KAAK,EAAE,EAAE;QACxG,+EAA+E;QAC/E,6EAA6E;QAC7E,yFAAyF;QACzF,kBAAkB;QAClB,OAAO,kCAAgB,CAAC,cAAc,CAClC,MAAM,EACN;YACI,SAAS,EAAE,GAAG,IAAI,WAAW;YAC7B,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;YAC3B,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ;SAClC,EACD,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,iCACtB,OAAO,GACP,gBAAgB,EACrB,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE;YACtB,MAAM,KAAK,GAAG,IAAA,2CAAiB,EAAC,aAAa,CAAC,CAAC;YAC/C,yFAAyF;YACzF,2DAA2D;YAC3D,oFAAoF;YACpF,6FAA6F;YAC7F,oCAAoC;YACpC,IAAI,+BAA+B,IAAI,KAAK,CAAC,QAAQ,IAAI,EAAE,EAAE;gBACzD,KAAK,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,IAAA,0CAAgB,EAAC,aAAa,CAAC,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC;aACrF;YACD,IAAI,KAAK,KAAK,IAAI,IAAI,gBAAgB,EAAE;gBACpC,MAAM,IAAI,gCAAiB;gBACvB,yDAAyD;gBACzD,+CAA+C,EAC/C,uCAAa,CAAC,eAAe,EAC7B,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAb,2BAAa,EAAE,CAAC,CAAC;aACxC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;YACT,kGAAkG;YAClG,2FAA2F;YAC3F,MAAM,WAAW,GAAG,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,QAAQ,CAAC;YACpC,MAAM,UAAU,GAAG,IAAA,2BAAS,EACxB,KAAK,EACL,CAAC,YAAY,EAAE,EAAE,CAAC,IAAI,gCAAiB,CACnC,mDAAmD,YAAY,EAAE,EACjE,uCAAa,CAAC,eAAe,EAC7B,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,EACrE,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAb,2BAAa,EAAE,CAAC,CAAC,CAAC;YAC1C,MAAM,UAAU,CAAC;QACrB,CAAC,CAAC,EACF,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC;AACN,CAAC;AAvDD,wEAuDC;AAED,SAAgB,sBAAsB,CAAC,eAAiC;IACpE,MAAM,UAAU,GAAgB;QAC5B,IAAI,EAAE,qCAAW;QACjB,GAAG,EAAE,EAAE;QACP,IAAI,EAAE;YACF,WAAW,EAAE,eAAe;YAC5B,KAAK,EAAE,eAAe,CAAC,gBAAgB;SAC1C;KACJ,CAAC;IACF,OAAO,UAAU,CAAC;AACtB,CAAC;AAVD,wDAUC;AAED,iGAAiG;AACjG,uFAAuF;AAC1E,QAAA,kBAAkB,GAAG,KAAK,CAAC;AAExC;;;;;GAKG;AACH,SAAgB,2BAA2B,CAAC,aAA4D;IACpG,IAAI,CAAC,aAAa,EAAE;QAChB,OAAO;KACV;IACD,MAAM,KAAK,GAAI,aAAkC,CAAC,KAAK,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE;QACR,OAAO,kBAAkB,aAAa,EAAE,CAAC;KAC5C;IACD,IAAI,sBAAsB,GAAG,mBAAmB,KAAK,EAAE,CAAC;IACxD,MAAM,IAAI,GAAI,aAAkC,CAAC,IAAI,CAAC;IACtD,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,sBAAsB,mBAAmB,IAAI,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAC5G,OAAO,sBAAsB,CAAC;AAClC,CAAC;AAZD,kEAYC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent, TelemetryDataTag, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n ISharingLinkKind,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n propsToLog: ITelemetryProperties;\n duration: number;\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return 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>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n // pre-0.58 error message: No response from fetch call\n \"No response from ODSP fetch call\",\n DriverErrorType.incorrectServerResponse,\n { driverVersion });\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n // pre-0.58 error message prefix: odspFetchError\n `ODSP fetch error [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n propsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n const online = isOnline();\n const errorText = `${error}`;\n const urlRegex = /((http|https):\\/\\/(\\S*))/i;\n const redactedErrorText = errorText.replace(urlRegex, \"REDACTED_URL\");\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\n \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n // TCP/IP timeout\n if (errorText.includes(\"ETIMEDOUT\")) {\n throw new RetryableError(\n \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n\n // WARNING: Do not log error object itself or any of its properties!\n // It could contain PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n // eslint-disable-next-line unicorn/prefer-ternary\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\n // pre-0.58 error message prefix: Offline\n `ODSP fetch failure (Offline): ${redactedErrorText}`,\n DriverErrorType.offlineError,\n {\n driverVersion,\n rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },\n });\n } else {\n // It is perhaps still possible that this is due to being offline, the error does not reveal enough\n // information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n throw new RetryableError(\n // pre-0.58 error message prefix: Fetch error\n `ODSP fetch failure: ${redactedErrorText}`,\n DriverErrorType.fetchFailure,\n {\n driverVersion,\n rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },\n });\n }\n });\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 fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n propsToLog.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n propsToLog,\n duration,\n };\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 requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n // pre-0.58 error message: errorWhileParsingFetchResponse\n \"Error while parsing fetch response\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n propsToLog.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n propsToLog,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n * ShareLinkTypes will deprecated in future. Use ISharingLinkKind instead which specifies both\n * share link type and the role type.\n */\n createLinkType?: ShareLinkTypes | ISharingLinkKind;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n {\n all:\n {\n driverVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrlParts: IOdspUrlParts,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n ...resolvedUrlParts,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n // pre-0.58 error message: Token is null for ${name} call\n `The Host-provided token fetcher returned null`,\n OdspErrorType.fetchTokenError,\n { method: name, driverVersion });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n `The Host-provided token fetcher threw an error: ${errorMessage}`,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\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(shareLinkType: ShareLinkTypes | ISharingLinkKind | undefined) {\n if (!shareLinkType) {\n return;\n }\n const scope = (shareLinkType as ISharingLinkKind).scope;\n if (!scope) {\n return `createLinkType=${shareLinkType}`;\n }\n let shareLinkRequestParams = `createLinkScope=${scope}`;\n const role = (shareLinkType as ISharingLinkKind).role;\n shareLinkRequestParams = role ? `${shareLinkRequestParams}&createLinkRole=${role}` : shareLinkRequestParams;\n return shareLinkRequestParams;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"opsCaching.d.ts","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAItE,MAAM,WAAW,QAAQ;IACrB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,oBAAY,UAAU,GAAG,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC;AAElD,MAAM,WAAW,MAAM;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,qBAAa,QAAQ;IAMjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,eAAe;IATzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IACjE,OAAO,CAAC,KAAK,CAA4C;gBAGvD,sBAAsB,EAAE,MAAM,EACb,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,KAAA,EACzB,eAAe,KAAA;IAehB,OAAO;IAQP,QAAQ;IAUR,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE;IA8C7B;;;;;OAKG;YACY,OAAO;IAiCtB;;;;;OAKG;IACU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkBhE,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQpD,SAAS,CAAC,aAAa;IASvB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,2BAA2B;CAKtC"}
1
+ {"version":3,"file":"opsCaching.d.ts","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAItE,MAAM,WAAW,QAAQ;IACrB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,oBAAY,UAAU,GAAG,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC;AAElD,MAAM,WAAW,MAAM;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,qBAAa,QAAQ;IAMjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,eAAe;IATzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IACjE,OAAO,CAAC,KAAK,CAA4C;gBAGvD,sBAAsB,EAAE,MAAM,EACb,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,KAAA,EACzB,eAAe,KAAA;IAgBhB,OAAO;IAQP,QAAQ;IAUR,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE;IA8C7B;;;;;OAKG;YACY,OAAO;IAiCtB;;;;;OAKG;IACU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkBhE,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQpD,SAAS,CAAC,aAAa;IASvB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,2BAA2B;CAKtC"}
@@ -14,8 +14,9 @@ class OpsCache {
14
14
  this.timerGranularity = timerGranularity;
15
15
  this.totalOpsToCache = totalOpsToCache;
16
16
  this.batches = new Map();
17
- /** initial batch is a special case because it will never be full - all ops prior (inclusive) to
18
- * startingSequenceNumber are never going to show up (undefined)
17
+ /**
18
+ * Initial batch is a special case because it will never be full - all ops prior (inclusive) to
19
+ * `startingSequenceNumber` are never going to show up (undefined)
19
20
  */
20
21
  const remainingSlots = this.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;
21
22
  if (remainingSlots !== 0) {
@@ -1 +1 @@
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 { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { performance } from \"@fluidframework/common-utils\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n sequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n remainingSlots: number;\n batchData: CacheEntry;\n /**\n * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n */\n dirty: boolean;\n}\n\nexport interface ICache {\n write(batchNumber: string, data: string): Promise<void>;\n read(batchNumber: string): Promise<string | undefined>;\n remove(): void;\n}\n\nexport class OpsCache {\n private readonly batches: Map<number, null | IBatch> = new Map();\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(\n startingSequenceNumber: number,\n private readonly logger: ITelemetryLogger,\n private readonly cache: ICache,\n private readonly batchSize: number,\n private readonly timerGranularity,\n private totalOpsToCache,\n ) {\n /** initial batch is a special case because it will never be full - all ops prior (inclusive) to\n * startingSequenceNumber are never going to show up (undefined)\n */\n const remainingSlots = this.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n if (remainingSlots !== 0) {\n this.batches.set(this.getBatchNumber(startingSequenceNumber), {\n remainingSlots,\n batchData: this.initializeNewBatchDataArray(),\n dirty: false,\n });\n }\n }\n\n public dispose() {\n this.batches.clear();\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n public flushOps() {\n for (const [key, value] of this.batches) {\n if (value === null || !value.dirty) {\n continue;\n }\n value.dirty = false;\n this.write(key, value);\n }\n }\n\n public addOps(ops: IMessage[]) {\n if (this.totalOpsToCache <= 0) {\n return;\n }\n\n for (const op of ops) {\n const batchNumber = this.getBatchNumber(op.sequenceNumber);\n const positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n let currentBatch = this.batches.get(batchNumber);\n\n if (currentBatch === undefined) {\n currentBatch = {\n remainingSlots: this.batchSize - 1,\n batchData: this.initializeNewBatchDataArray(),\n dirty: true,\n };\n currentBatch.batchData[positionInBatch] = op;\n this.batches.set(batchNumber, currentBatch);\n } else if (currentBatch !== null && currentBatch.batchData[positionInBatch] === undefined) {\n currentBatch.batchData[positionInBatch] = op;\n currentBatch.remainingSlots--;\n currentBatch.dirty = true;\n } else {\n // Either batch was flushed or this op was already there - nothing to do!\n return;\n }\n\n if (currentBatch.remainingSlots === 0) {\n // batch is full, flush to cache\n this.write(batchNumber, currentBatch);\n this.batches.set(batchNumber, null);\n } else {\n this.scheduleTimer();\n }\n\n this.totalOpsToCache--;\n if (this.totalOpsToCache === 0) {\n this.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\" });\n this.cache.remove();\n this.dispose();\n break;\n }\n }\n }\n\n /**\n * Retrieves ops from cache\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n private async getCore(from: number, to?: number): Promise<IMessage[]> {\n const messages: IMessage[] = [];\n let batchNumber = this.getBatchNumber(from);\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n if (res === undefined) {\n return messages;\n }\n const result: CacheEntry = JSON.parse(res);\n for (const op of result) {\n // Note that we write out undefined, but due to JSON.stringify, it turns into null!\n if (op) {\n if (to !== undefined && op.sequenceNumber >= to) {\n return messages;\n }\n if (messages.length === 0) {\n if (op.sequenceNumber > from) {\n return messages;\n } else if (op.sequenceNumber < from) {\n continue;\n }\n }\n messages.push(op);\n } else if (messages.length !== 0) {\n return messages;\n }\n }\n\n batchNumber++;\n }\n }\n\n /**\n * Retrieves ops from cache\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n public async get(from: number, to?: number): Promise<IMessage[]> {\n const start = performance.now();\n\n const messages = await this.getCore(from, to);\n\n const duration = performance.now() - start;\n if (messages.length > 0 || duration > 1000) {\n this.logger.sendPerformanceEvent({\n eventName: \"CacheOpsUsed\",\n from,\n to,\n length: messages.length,\n duration,\n });\n }\n return messages;\n }\n\n protected write(batchNumber: number, payload: IBatch) {\n // Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n // in the adapter chain of cache adapters\n this.cache.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData)).catch(() => {\n this.totalOpsToCache = 0;\n });\n }\n\n protected scheduleTimer() {\n if (!this.timer && this.timerGranularity > 0) {\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flushOps();\n }, this.timerGranularity);\n }\n }\n\n private getBatchNumber(sequenceNumber: number) {\n return Math.floor(sequenceNumber / this.batchSize);\n }\n\n private getPositionInBatchArray(sequenceNumber: number) {\n return sequenceNumber % this.batchSize;\n }\n\n private initializeNewBatchDataArray() {\n const tempArray: IMessage[] = [];\n tempArray.length = this.batchSize; // fill with empty, undefined elements\n return tempArray;\n }\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 { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { performance } from \"@fluidframework/common-utils\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n sequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n remainingSlots: number;\n batchData: CacheEntry;\n /**\n * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n */\n dirty: boolean;\n}\n\nexport interface ICache {\n write(batchNumber: string, data: string): Promise<void>;\n read(batchNumber: string): Promise<string | undefined>;\n remove(): void;\n}\n\nexport class OpsCache {\n private readonly batches: Map<number, null | IBatch> = new Map();\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(\n startingSequenceNumber: number,\n private readonly logger: ITelemetryLogger,\n private readonly cache: ICache,\n private readonly batchSize: number,\n private readonly timerGranularity,\n private totalOpsToCache,\n ) {\n /**\n * Initial batch is a special case because it will never be full - all ops prior (inclusive) to\n * `startingSequenceNumber` are never going to show up (undefined)\n */\n const remainingSlots = this.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n if (remainingSlots !== 0) {\n this.batches.set(this.getBatchNumber(startingSequenceNumber), {\n remainingSlots,\n batchData: this.initializeNewBatchDataArray(),\n dirty: false,\n });\n }\n }\n\n public dispose() {\n this.batches.clear();\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n public flushOps() {\n for (const [key, value] of this.batches) {\n if (value === null || !value.dirty) {\n continue;\n }\n value.dirty = false;\n this.write(key, value);\n }\n }\n\n public addOps(ops: IMessage[]) {\n if (this.totalOpsToCache <= 0) {\n return;\n }\n\n for (const op of ops) {\n const batchNumber = this.getBatchNumber(op.sequenceNumber);\n const positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n let currentBatch = this.batches.get(batchNumber);\n\n if (currentBatch === undefined) {\n currentBatch = {\n remainingSlots: this.batchSize - 1,\n batchData: this.initializeNewBatchDataArray(),\n dirty: true,\n };\n currentBatch.batchData[positionInBatch] = op;\n this.batches.set(batchNumber, currentBatch);\n } else if (currentBatch !== null && currentBatch.batchData[positionInBatch] === undefined) {\n currentBatch.batchData[positionInBatch] = op;\n currentBatch.remainingSlots--;\n currentBatch.dirty = true;\n } else {\n // Either batch was flushed or this op was already there - nothing to do!\n return;\n }\n\n if (currentBatch.remainingSlots === 0) {\n // batch is full, flush to cache\n this.write(batchNumber, currentBatch);\n this.batches.set(batchNumber, null);\n } else {\n this.scheduleTimer();\n }\n\n this.totalOpsToCache--;\n if (this.totalOpsToCache === 0) {\n this.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\" });\n this.cache.remove();\n this.dispose();\n break;\n }\n }\n }\n\n /**\n * Retrieves ops from cache\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n private async getCore(from: number, to?: number): Promise<IMessage[]> {\n const messages: IMessage[] = [];\n let batchNumber = this.getBatchNumber(from);\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n if (res === undefined) {\n return messages;\n }\n const result: CacheEntry = JSON.parse(res);\n for (const op of result) {\n // Note that we write out undefined, but due to JSON.stringify, it turns into null!\n if (op) {\n if (to !== undefined && op.sequenceNumber >= to) {\n return messages;\n }\n if (messages.length === 0) {\n if (op.sequenceNumber > from) {\n return messages;\n } else if (op.sequenceNumber < from) {\n continue;\n }\n }\n messages.push(op);\n } else if (messages.length !== 0) {\n return messages;\n }\n }\n\n batchNumber++;\n }\n }\n\n /**\n * Retrieves ops from cache\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n public async get(from: number, to?: number): Promise<IMessage[]> {\n const start = performance.now();\n\n const messages = await this.getCore(from, to);\n\n const duration = performance.now() - start;\n if (messages.length > 0 || duration > 1000) {\n this.logger.sendPerformanceEvent({\n eventName: \"CacheOpsUsed\",\n from,\n to,\n length: messages.length,\n duration,\n });\n }\n return messages;\n }\n\n protected write(batchNumber: number, payload: IBatch) {\n // Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n // in the adapter chain of cache adapters\n this.cache.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData)).catch(() => {\n this.totalOpsToCache = 0;\n });\n }\n\n protected scheduleTimer() {\n if (!this.timer && this.timerGranularity > 0) {\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flushOps();\n }, this.timerGranularity);\n }\n }\n\n private getBatchNumber(sequenceNumber: number) {\n return Math.floor(sequenceNumber / this.batchSize);\n }\n\n private getPositionInBatchArray(sequenceNumber: number) {\n return sequenceNumber % this.batchSize;\n }\n\n private initializeNewBatchDataArray() {\n const tempArray: IMessage[] = [];\n tempArray.length = this.batchSize; // fill with empty, undefined elements\n return tempArray;\n }\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 = "1.3.0";
8
+ export declare const pkgVersion = "2.0.0-dev.1.4.5.105745";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,UAAU,CAAC"}
1
+ {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,2BAA2B,CAAC"}
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/odsp-driver";
11
- exports.pkgVersion = "1.3.0";
11
+ exports.pkgVersion = "2.0.0-dev.1.4.5.105745";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,OAAO,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"1.3.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"2.0.0-dev.1.4.5.105745\";\n"]}
@@ -9,19 +9,21 @@ import { SnapshotFormatSupportType } from "./fetchSnapshot";
9
9
  /**
10
10
  * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded
11
11
  * the cached latest snapshot could be used and removes the network call from the critical path.
12
+ *
12
13
  * @param resolvedUrl - Resolved url to fetch the snapshot.
13
14
  * @param getStorageToken - function that can provide the storage token for a given site. This is
14
- * is also referred to as the "VROOM" token in SPO.
15
+ * is also referred to as the "VROOM" token in SPO.
15
16
  * @param persistedCache - Cache to store the fetched snapshot.
16
17
  * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.
17
18
  * @param logger - Logger to have telemetry events.
18
19
  * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.
19
20
  * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem
20
- * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make
21
- * the Trees latest call.
22
- * @deprecated - This will be replaced with snapshotFormatFetchType.
21
+ * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make
22
+ * the Trees latest call.
23
+ * Note: this can be considered deprecated - it will be replaced with `snapshotFormatFetchType`.
23
24
  * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.
24
25
  * @param snapshotFormatFetchType - Snapshot format to fetch.
26
+ *
25
27
  * @returns - True if the snapshot is cached, false otherwise.
26
28
  */
27
29
  export declare function prefetchLatestSnapshot(resolvedUrl: IResolvedUrl, getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>, persistedCache: IPersistedCache, forceAccessTokenViaAuthorizationHeader: boolean, logger: ITelemetryBaseLogger, hostSnapshotFetchOptions: ISnapshotOptions | undefined, enableRedeemFallback?: boolean, fetchBinarySnapshotFormat?: boolean, snapshotFormatFetchType?: SnapshotFormatSupportType): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"prefetchLatestSnapshot.d.ts","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAEH,eAAe,EACf,gBAAgB,EAChB,6BAA6B,EAC7B,YAAY,EAEf,MAAM,yCAAyC,CAAC;AAQjD,OAAO,EAA6C,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAGvG;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,sBAAsB,CACxC,WAAW,EAAE,YAAY,EACzB,eAAe,EAAE,YAAY,CAAC,6BAA6B,CAAC,EAC5D,cAAc,EAAE,eAAe,EAC/B,sCAAsC,EAAE,OAAO,EAC/C,MAAM,EAAE,oBAAoB,EAC5B,wBAAwB,EAAE,gBAAgB,GAAG,SAAS,EACtD,oBAAoB,GAAE,OAAc,EACpC,yBAAyB,CAAC,EAAE,OAAO,EACnC,uBAAuB,CAAC,EAAE,yBAAyB,GACpD,OAAO,CAAC,OAAO,CAAC,CAsDlB"}
1
+ {"version":3,"file":"prefetchLatestSnapshot.d.ts","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAEH,eAAe,EACf,gBAAgB,EAChB,6BAA6B,EAC7B,YAAY,EAEf,MAAM,yCAAyC,CAAC;AAQjD,OAAO,EAA6C,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAGvG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,sBAAsB,CACxC,WAAW,EAAE,YAAY,EACzB,eAAe,EAAE,YAAY,CAAC,6BAA6B,CAAC,EAC5D,cAAc,EAAE,eAAe,EAC/B,sCAAsC,EAAE,OAAO,EAC/C,MAAM,EAAE,oBAAoB,EAC5B,wBAAwB,EAAE,gBAAgB,GAAG,SAAS,EACtD,oBAAoB,GAAE,OAAc,EACpC,yBAAyB,CAAC,EAAE,OAAO,EACnC,uBAAuB,CAAC,EAAE,yBAAyB,GACpD,OAAO,CAAC,OAAO,CAAC,CAsDlB"}
@@ -12,19 +12,21 @@ const fetchSnapshot_1 = require("./fetchSnapshot");
12
12
  /**
13
13
  * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded
14
14
  * the cached latest snapshot could be used and removes the network call from the critical path.
15
+ *
15
16
  * @param resolvedUrl - Resolved url to fetch the snapshot.
16
17
  * @param getStorageToken - function that can provide the storage token for a given site. This is
17
- * is also referred to as the "VROOM" token in SPO.
18
+ * is also referred to as the "VROOM" token in SPO.
18
19
  * @param persistedCache - Cache to store the fetched snapshot.
19
20
  * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.
20
21
  * @param logger - Logger to have telemetry events.
21
22
  * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.
22
23
  * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem
23
- * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make
24
- * the Trees latest call.
25
- * @deprecated - This will be replaced with snapshotFormatFetchType.
24
+ * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make
25
+ * the Trees latest call.
26
+ * Note: this can be considered deprecated - it will be replaced with `snapshotFormatFetchType`.
26
27
  * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.
27
28
  * @param snapshotFormatFetchType - Snapshot format to fetch.
29
+ *
28
30
  * @returns - True if the snapshot is cached, false otherwise.
29
31
  */
30
32
  async function prefetchLatestSnapshot(resolvedUrl, getStorageToken, persistedCache, forceAccessTokenViaAuthorizationHeader, logger, hostSnapshotFetchOptions, enableRedeemFallback = true, fetchBinarySnapshotFormat, snapshotFormatFetchType) {
@@ -1 +1 @@
1
- {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIH,+DAAsD;AAUtD,qEAAgF;AAChF,2CAKqB;AACrB,mDAAuG;AAGvG;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,sBAAsB,CACxC,WAAyB,EACzB,eAA4D,EAC5D,cAA+B,EAC/B,sCAA+C,EAC/C,MAA4B,EAC5B,wBAAsD,EACtD,uBAAgC,IAAI,EACpC,yBAAmC,EACnC,uBAAmD;IAEnD,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,6BAAW,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,IAAA,8BAAkB,EAAC,WAAW,CAAC,CAAC;IAExD,MAAM,eAAe,GAAkB;QACnC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,MAAM,EAAE,eAAe,CAAC,MAAM;KACjC,CAAC;IACF,MAAM,mBAAmB,GAAG,IAAA,0CAA8B,EACtD,UAAU,EACV,eAAe,EACf,eAAe,EACf,IAAI,CAAC,sBAAsB,CAC9B,CAAC;IAEF,MAAM,kBAAkB,GAAG,KAAK,EAC5B,oBAAsC,EACtC,YAAoB,EACpB,eAA6C,EAC7C,UAA4B,EAC9B,EAAE;QACA,OAAO,IAAA,gCAAgB,EACnB,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;IAC9G,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,IAAA,kCAAsB,EAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,MAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,EAAE,cAAwC,EAAE,EAAE;QAClE,MAAM,GAAG,cAAc,CAAC,GAAG,CACvB,WAAW,EACX,cAAc,CACjB,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC,cAAc,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO,kCAAgB,CAAC,cAAc,CAClC,UAAU,EACV,EAAE,SAAS,EAAE,wBAAwB,EAAE,EACvC,KAAK,IAAI,EAAE;QACP,MAAM,IAAA,uCAAuB,EACrB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,sCAAsC,EACtC,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,oBAAoB,CACvB,CAAC;QACN,IAAA,qBAAM,EAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC;QACb,OAAO,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAhED,wDAgEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { default as AbortController } from \"abort-controller\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IResolvedUrl } from \"@fluidframework/driver-definitions\";\nimport {\n IOdspResolvedUrl,\n IPersistedCache,\n ISnapshotOptions,\n OdspResourceTokenFetchOptions,\n TokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport {\n createCacheSnapshotKey,\n createOdspLogger,\n getOdspResolvedUrl,\n toInstrumentedOdspTokenFetcher,\n} from \"./odspUtils\";\nimport { downloadSnapshot, fetchSnapshotWithRedeem, SnapshotFormatSupportType } from \"./fetchSnapshot\";\nimport { IVersionedValueWithEpoch } from \"./contracts\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * @deprecated - This will be replaced with snapshotFormatFetchType.\n * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.\n * @param snapshotFormatFetchType - Snapshot format to fetch.\n * @returns - True if the snapshot is cached, false otherwise.\n */\nexport async function prefetchLatestSnapshot(\n resolvedUrl: IResolvedUrl,\n getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n persistedCache: IPersistedCache,\n forceAccessTokenViaAuthorizationHeader: boolean,\n logger: ITelemetryBaseLogger,\n hostSnapshotFetchOptions: ISnapshotOptions | undefined,\n enableRedeemFallback: boolean = true,\n fetchBinarySnapshotFormat?: boolean,\n snapshotFormatFetchType?: SnapshotFormatSupportType,\n): Promise<boolean> {\n const odspLogger = createOdspLogger(ChildLogger.create(logger, \"PrefetchSnapshot\"));\n const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n const resolvedUrlData: IOdspUrlParts = {\n siteUrl: odspResolvedUrl.siteUrl,\n driveId: odspResolvedUrl.driveId,\n itemId: odspResolvedUrl.itemId,\n };\n const storageTokenFetcher = toInstrumentedOdspTokenFetcher(\n odspLogger,\n resolvedUrlData,\n getStorageToken,\n true /* throwOnNullToken */,\n );\n\n const snapshotDownloader = async (\n finalOdspResolvedUrl: IOdspResolvedUrl,\n storageToken: string,\n snapshotOptions: ISnapshotOptions | undefined,\n controller?: AbortController,\n ) => {\n return downloadSnapshot(\n finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, snapshotFormatFetchType, controller);\n };\n const snapshotKey = createCacheSnapshotKey(odspResolvedUrl);\n let cacheP: Promise<void> | undefined;\n const putInCache = async (valueWithEpoch: IVersionedValueWithEpoch) => {\n cacheP = persistedCache.put(\n snapshotKey,\n valueWithEpoch,\n );\n return cacheP;\n };\n const removeEntries = async () => persistedCache.removeEntries(snapshotKey.file);\n return PerformanceEvent.timedExecAsync(\n odspLogger,\n { eventName: \"PrefetchLatestSnapshot\" },\n async () => {\n await fetchSnapshotWithRedeem(\n odspResolvedUrl,\n storageTokenFetcher,\n hostSnapshotFetchOptions,\n forceAccessTokenViaAuthorizationHeader,\n odspLogger,\n snapshotDownloader,\n putInCache,\n removeEntries,\n enableRedeemFallback,\n );\n assert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n await cacheP;\n return true;\n }).catch(async (error) => false);\n}\n"]}
1
+ {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIH,+DAAsD;AAUtD,qEAAgF;AAChF,2CAKqB;AACrB,mDAAuG;AAGvG;;;;;;;;;;;;;;;;;;;GAmBG;AACI,KAAK,UAAU,sBAAsB,CACxC,WAAyB,EACzB,eAA4D,EAC5D,cAA+B,EAC/B,sCAA+C,EAC/C,MAA4B,EAC5B,wBAAsD,EACtD,uBAAgC,IAAI,EACpC,yBAAmC,EACnC,uBAAmD;IAEnD,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,6BAAW,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,IAAA,8BAAkB,EAAC,WAAW,CAAC,CAAC;IAExD,MAAM,eAAe,GAAkB;QACnC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,MAAM,EAAE,eAAe,CAAC,MAAM;KACjC,CAAC;IACF,MAAM,mBAAmB,GAAG,IAAA,0CAA8B,EACtD,UAAU,EACV,eAAe,EACf,eAAe,EACf,IAAI,CAAC,sBAAsB,CAC9B,CAAC;IAEF,MAAM,kBAAkB,GAAG,KAAK,EAC5B,oBAAsC,EACtC,YAAoB,EACpB,eAA6C,EAC7C,UAA4B,EAC9B,EAAE;QACA,OAAO,IAAA,gCAAgB,EACnB,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;IAC9G,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,IAAA,kCAAsB,EAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,MAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,EAAE,cAAwC,EAAE,EAAE;QAClE,MAAM,GAAG,cAAc,CAAC,GAAG,CACvB,WAAW,EACX,cAAc,CACjB,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC,cAAc,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO,kCAAgB,CAAC,cAAc,CAClC,UAAU,EACV,EAAE,SAAS,EAAE,wBAAwB,EAAE,EACvC,KAAK,IAAI,EAAE;QACP,MAAM,IAAA,uCAAuB,EACrB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,sCAAsC,EACtC,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,oBAAoB,CACvB,CAAC;QACN,IAAA,qBAAM,EAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC;QACb,OAAO,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAhED,wDAgEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { default as AbortController } from \"abort-controller\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IResolvedUrl } from \"@fluidframework/driver-definitions\";\nimport {\n IOdspResolvedUrl,\n IPersistedCache,\n ISnapshotOptions,\n OdspResourceTokenFetchOptions,\n TokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport {\n createCacheSnapshotKey,\n createOdspLogger,\n getOdspResolvedUrl,\n toInstrumentedOdspTokenFetcher,\n} from \"./odspUtils\";\nimport { downloadSnapshot, fetchSnapshotWithRedeem, SnapshotFormatSupportType } from \"./fetchSnapshot\";\nimport { IVersionedValueWithEpoch } from \"./contracts\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n *\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * Note: this can be considered deprecated - it will be replaced with `snapshotFormatFetchType`.\n * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.\n * @param snapshotFormatFetchType - Snapshot format to fetch.\n *\n * @returns - True if the snapshot is cached, false otherwise.\n */\nexport async function prefetchLatestSnapshot(\n resolvedUrl: IResolvedUrl,\n getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n persistedCache: IPersistedCache,\n forceAccessTokenViaAuthorizationHeader: boolean,\n logger: ITelemetryBaseLogger,\n hostSnapshotFetchOptions: ISnapshotOptions | undefined,\n enableRedeemFallback: boolean = true,\n fetchBinarySnapshotFormat?: boolean,\n snapshotFormatFetchType?: SnapshotFormatSupportType,\n): Promise<boolean> {\n const odspLogger = createOdspLogger(ChildLogger.create(logger, \"PrefetchSnapshot\"));\n const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n const resolvedUrlData: IOdspUrlParts = {\n siteUrl: odspResolvedUrl.siteUrl,\n driveId: odspResolvedUrl.driveId,\n itemId: odspResolvedUrl.itemId,\n };\n const storageTokenFetcher = toInstrumentedOdspTokenFetcher(\n odspLogger,\n resolvedUrlData,\n getStorageToken,\n true /* throwOnNullToken */,\n );\n\n const snapshotDownloader = async (\n finalOdspResolvedUrl: IOdspResolvedUrl,\n storageToken: string,\n snapshotOptions: ISnapshotOptions | undefined,\n controller?: AbortController,\n ) => {\n return downloadSnapshot(\n finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, snapshotFormatFetchType, controller);\n };\n const snapshotKey = createCacheSnapshotKey(odspResolvedUrl);\n let cacheP: Promise<void> | undefined;\n const putInCache = async (valueWithEpoch: IVersionedValueWithEpoch) => {\n cacheP = persistedCache.put(\n snapshotKey,\n valueWithEpoch,\n );\n return cacheP;\n };\n const removeEntries = async () => persistedCache.removeEntries(snapshotKey.file);\n return PerformanceEvent.timedExecAsync(\n odspLogger,\n { eventName: \"PrefetchLatestSnapshot\" },\n async () => {\n await fetchSnapshotWithRedeem(\n odspResolvedUrl,\n storageTokenFetcher,\n hostSnapshotFetchOptions,\n forceAccessTokenViaAuthorizationHeader,\n odspLogger,\n snapshotDownloader,\n putInCache,\n removeEntries,\n enableRedeemFallback,\n );\n assert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n await cacheP;\n return true;\n }).catch(async (error) => false);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"retryUtils.d.ts","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAMtE;;GAEG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAChC,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,IAAI,GAC3B,OAAO,CAAC,CAAC,CAAC,CAsDZ"}
1
+ {"version":3,"file":"retryUtils.d.ts","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAMtE;;GAEG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAChC,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,IAAI,GAC3B,OAAO,CAAC,CAAC,CAAC,CAyDZ"}
@@ -13,6 +13,7 @@ const epochTracker_1 = require("./epochTracker");
13
13
  * This method retries only for retriable coherency and service read only errors.
14
14
  */
15
15
  async function runWithRetry(api, callName, logger, checkDisposed) {
16
+ var _a;
16
17
  let retryAfter = 1000;
17
18
  const start = common_utils_1.performance.now();
18
19
  let lastError;
@@ -36,8 +37,10 @@ async function runWithRetry(api, callName, logger, checkDisposed) {
36
37
  const canRetry = (0, driver_utils_1.canRetryOnError)(error);
37
38
  const coherencyError = (error === null || error === void 0 ? void 0 : error[epochTracker_1.Odsp409Error]) === true;
38
39
  const serviceReadonlyError = (error === null || error === void 0 ? void 0 : error.errorType) === odsp_driver_definitions_1.OdspErrorType.serviceReadOnly;
39
- // Retry for retriable 409 coherency errors or serviceReadOnly errors.
40
- if (!(coherencyError || serviceReadonlyError || canRetry)) {
40
+ // Retry for retriable 409 coherency errors or serviceReadOnly errors. These errors are always retriable
41
+ // unless someone specifically set canRetry = false on the error like in fetchSnapshot() flow. So in
42
+ // that case don't retry.
43
+ if (!((coherencyError || serviceReadonlyError) && canRetry)) {
41
44
  throw error;
42
45
  }
43
46
  // SPO itself does number of retries internally before returning 409 to client.
@@ -45,8 +48,8 @@ async function runWithRetry(api, callName, logger, checkDisposed) {
45
48
  // too much time / bandwidth doing the same thing without any progress.
46
49
  if (attempts === 5) {
47
50
  logger.sendErrorEvent({
48
- eventName: coherencyError ?
49
- "CoherencyErrorTooManyRetries" : "ServiceReadonlyErrorTooManyRetries",
51
+ eventName: coherencyError ? "CoherencyErrorTooManyRetries" :
52
+ "ServiceReadonlyErrorTooManyRetries",
50
53
  callName,
51
54
  attempts,
52
55
  duration: common_utils_1.performance.now() - start, // record total wait time.
@@ -55,6 +58,7 @@ async function runWithRetry(api, callName, logger, checkDisposed) {
55
58
  error.canRetry = false;
56
59
  throw error;
57
60
  }
61
+ retryAfter = (_a = (0, driver_utils_1.getRetryDelayFromError)(error)) !== null && _a !== void 0 ? _a : retryAfter;
58
62
  await (0, common_utils_1.delay)(Math.floor(retryAfter));
59
63
  retryAfter += retryAfter / 4 * (1 + Math.random());
60
64
  lastError = error;
@@ -1 +1 @@
1
- {"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAAkE;AAClE,+DAA+D;AAC/D,qFAAwE;AACxE,iDAA8C;AAE9C;;GAEG;AACI,KAAK,UAAU,YAAY,CAC9B,GAAqB,EACrB,QAAgB,EAChB,MAAwB,EACxB,aAA0B;IAE1B,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,0BAAW,CAAC,GAAG,EAAE,CAAC;IAChC,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAI,QAAQ,EAAE,EAAE;QACjC,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,aAAa,EAAE,CAAC;SACnB;QACD,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACd,MAAM,CAAC,kBAAkB,CACrB;oBACI,SAAS,EAAE,iBAAiB;oBAC5B,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,SAAS,CAAC,CAAC;aAClB;YACD,OAAO,MAAM,CAAC;SACjB;QAAC,OAAO,KAAU,EAAE;YACjB,MAAM,QAAQ,GAAG,IAAA,8BAAe,EAAC,KAAK,CAAC,CAAC;YAExC,MAAM,cAAc,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,2BAAY,CAAC,MAAK,IAAI,CAAC;YACtD,MAAM,oBAAoB,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,SAAS,MAAK,uCAAa,CAAC,eAAe,CAAC;YAChF,sEAAsE;YACtE,IAAI,CAAC,CAAC,cAAc,IAAI,oBAAoB,IAAI,QAAQ,CAAC,EAAE;gBACvD,MAAM,KAAK,CAAC;aACf;YAED,+EAA+E;YAC/E,oFAAoF;YACpF,uEAAuE;YACvE,IAAI,QAAQ,KAAK,CAAC,EAAE;gBAChB,MAAM,CAAC,cAAc,CACjB;oBACI,SAAS,EAAE,cAAc,CAAC,CAAC;wBACvB,8BAA8B,CAAC,CAAC,CAAC,oCAAoC;oBACzE,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,0BAA0B;iBAClE,EACD,KAAK,CAAC,CAAC;gBACX,aAAa;gBACb,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACvB,MAAM,KAAK,CAAC;aACf;YAED,MAAM,IAAA,oBAAK,EAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,UAAU,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,SAAS,GAAG,KAAK,CAAC;SACrB;KACJ;AACL,CAAC;AA3DD,oCA2DC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { delay, performance } from \"@fluidframework/common-utils\";\nimport { canRetryOnError } from \"@fluidframework/driver-utils\";\nimport { OdspErrorType } from \"@fluidframework/odsp-driver-definitions\";\nimport { Odsp409Error } from \"./epochTracker\";\n\n/**\n * This method retries only for retriable coherency and service read only errors.\n */\nexport async function runWithRetry<T>(\n api: () => Promise<T>,\n callName: string,\n logger: ITelemetryLogger,\n checkDisposed?: () => void,\n): Promise<T> {\n let retryAfter = 1000;\n const start = performance.now();\n let lastError: any;\n for (let attempts = 1; ; attempts++) {\n if (checkDisposed !== undefined) {\n checkDisposed();\n }\n try {\n const result = await api();\n if (attempts > 1) {\n logger.sendTelemetryEvent(\n {\n eventName: \"MultipleRetries\",\n callName,\n attempts,\n duration: performance.now() - start,\n },\n lastError);\n }\n return result;\n } catch (error: any) {\n const canRetry = canRetryOnError(error);\n\n const coherencyError = error?.[Odsp409Error] === true;\n const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;\n // Retry for retriable 409 coherency errors or serviceReadOnly errors.\n if (!(coherencyError || serviceReadonlyError || canRetry)) {\n throw error;\n }\n\n // SPO itself does number of retries internally before returning 409 to client.\n // That multiplied to 5 suggests need to reconsider current design, as client spends\n // too much time / bandwidth doing the same thing without any progress.\n if (attempts === 5) {\n logger.sendErrorEvent(\n {\n eventName: coherencyError ?\n \"CoherencyErrorTooManyRetries\" : \"ServiceReadonlyErrorTooManyRetries\",\n callName,\n attempts,\n duration: performance.now() - start, // record total wait time.\n },\n error);\n // Fail hard.\n error.canRetry = false;\n throw error;\n }\n\n await delay(Math.floor(retryAfter));\n retryAfter += retryAfter / 4 * (1 + Math.random());\n lastError = error;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAAkE;AAClE,+DAAuF;AACvF,qFAAwE;AACxE,iDAA8C;AAE9C;;GAEG;AACI,KAAK,UAAU,YAAY,CAC9B,GAAqB,EACrB,QAAgB,EAChB,MAAwB,EACxB,aAA0B;;IAE1B,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,0BAAW,CAAC,GAAG,EAAE,CAAC;IAChC,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAI,QAAQ,EAAE,EAAE;QACjC,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,aAAa,EAAE,CAAC;SACnB;QACD,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACd,MAAM,CAAC,kBAAkB,CACrB;oBACI,SAAS,EAAE,iBAAiB;oBAC5B,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,SAAS,CAAC,CAAC;aAClB;YACD,OAAO,MAAM,CAAC;SACjB;QAAC,OAAO,KAAU,EAAE;YACjB,MAAM,QAAQ,GAAG,IAAA,8BAAe,EAAC,KAAK,CAAC,CAAC;YAExC,MAAM,cAAc,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,2BAAY,CAAC,MAAK,IAAI,CAAC;YACtD,MAAM,oBAAoB,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,SAAS,MAAK,uCAAa,CAAC,eAAe,CAAC;YAChF,wGAAwG;YACxG,oGAAoG;YACpG,yBAAyB;YACzB,IAAI,CAAC,CAAC,CAAC,cAAc,IAAI,oBAAoB,CAAC,IAAI,QAAQ,CAAC,EAAE;gBACzD,MAAM,KAAK,CAAC;aACf;YAED,+EAA+E;YAC/E,oFAAoF;YACpF,uEAAuE;YACvE,IAAI,QAAQ,KAAK,CAAC,EAAE;gBAChB,MAAM,CAAC,cAAc,CACjB;oBACI,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC;wBACxD,oCAAoC;oBACxC,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,0BAA0B;iBAClE,EACD,KAAK,CAAC,CAAC;gBACX,aAAa;gBACb,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACvB,MAAM,KAAK,CAAC;aACf;YAED,UAAU,GAAG,MAAA,IAAA,qCAAsB,EAAC,KAAK,CAAC,mCAAI,UAAU,CAAC;YACzD,MAAM,IAAA,oBAAK,EAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,UAAU,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,SAAS,GAAG,KAAK,CAAC;SACrB;KACJ;AACL,CAAC;AA9DD,oCA8DC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { delay, performance } from \"@fluidframework/common-utils\";\nimport { canRetryOnError, getRetryDelayFromError } from \"@fluidframework/driver-utils\";\nimport { OdspErrorType } from \"@fluidframework/odsp-driver-definitions\";\nimport { Odsp409Error } from \"./epochTracker\";\n\n/**\n * This method retries only for retriable coherency and service read only errors.\n */\nexport async function runWithRetry<T>(\n api: () => Promise<T>,\n callName: string,\n logger: ITelemetryLogger,\n checkDisposed?: () => void,\n): Promise<T> {\n let retryAfter = 1000;\n const start = performance.now();\n let lastError: any;\n for (let attempts = 1; ; attempts++) {\n if (checkDisposed !== undefined) {\n checkDisposed();\n }\n try {\n const result = await api();\n if (attempts > 1) {\n logger.sendTelemetryEvent(\n {\n eventName: \"MultipleRetries\",\n callName,\n attempts,\n duration: performance.now() - start,\n },\n lastError);\n }\n return result;\n } catch (error: any) {\n const canRetry = canRetryOnError(error);\n\n const coherencyError = error?.[Odsp409Error] === true;\n const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;\n // Retry for retriable 409 coherency errors or serviceReadOnly errors. These errors are always retriable\n // unless someone specifically set canRetry = false on the error like in fetchSnapshot() flow. So in\n // that case don't retry.\n if (!((coherencyError || serviceReadonlyError) && canRetry)) {\n throw error;\n }\n\n // SPO itself does number of retries internally before returning 409 to client.\n // That multiplied to 5 suggests need to reconsider current design, as client spends\n // too much time / bandwidth doing the same thing without any progress.\n if (attempts === 5) {\n logger.sendErrorEvent(\n {\n eventName: coherencyError ? \"CoherencyErrorTooManyRetries\" :\n \"ServiceReadonlyErrorTooManyRetries\",\n callName,\n attempts,\n duration: performance.now() - start, // record total wait time.\n },\n error);\n // Fail hard.\n error.canRetry = false;\n throw error;\n }\n\n retryAfter = getRetryDelayFromError(error) ?? retryAfter;\n await delay(Math.floor(retryAfter));\n retryAfter += retryAfter / 4 * (1 + Math.random());\n lastError = error;\n }\n }\n}\n"]}