@fluidframework/routerlicious-driver 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/.eslintrc.js +10 -12
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +156 -0
  4. package/README.md +39 -1
  5. package/api-extractor-lint.json +4 -0
  6. package/api-extractor.json +2 -2
  7. package/api-report/routerlicious-driver.api.md +108 -0
  8. package/dist/cache.cjs +30 -0
  9. package/dist/cache.cjs.map +1 -0
  10. package/dist/cache.d.ts +7 -0
  11. package/dist/cache.d.ts.map +1 -1
  12. package/dist/{definitions.js → contracts.cjs} +1 -1
  13. package/dist/contracts.cjs.map +1 -0
  14. package/dist/contracts.d.ts +42 -0
  15. package/dist/contracts.d.ts.map +1 -0
  16. package/dist/{createNewUtils.js → createNewUtils.cjs} +7 -5
  17. package/dist/createNewUtils.cjs.map +1 -0
  18. package/dist/createNewUtils.d.ts +1 -1
  19. package/dist/createNewUtils.d.ts.map +1 -1
  20. package/dist/{defaultTokenProvider.js → defaultTokenProvider.cjs} +2 -1
  21. package/dist/defaultTokenProvider.cjs.map +1 -0
  22. package/dist/defaultTokenProvider.d.ts +1 -0
  23. package/dist/defaultTokenProvider.d.ts.map +1 -1
  24. package/dist/definitions.cjs +7 -0
  25. package/dist/definitions.cjs.map +1 -0
  26. package/dist/definitions.d.ts.map +1 -1
  27. package/dist/deltaStorageService.cjs +122 -0
  28. package/dist/deltaStorageService.cjs.map +1 -0
  29. package/dist/deltaStorageService.d.ts +7 -6
  30. package/dist/deltaStorageService.d.ts.map +1 -1
  31. package/dist/{documentDeltaConnection.js → documentDeltaConnection.cjs} +8 -13
  32. package/dist/documentDeltaConnection.cjs.map +1 -0
  33. package/dist/documentDeltaConnection.d.ts +3 -4
  34. package/dist/documentDeltaConnection.d.ts.map +1 -1
  35. package/dist/{documentService.js → documentService.cjs} +61 -64
  36. package/dist/documentService.cjs.map +1 -0
  37. package/dist/documentService.d.ts +12 -4
  38. package/dist/documentService.d.ts.map +1 -1
  39. package/dist/{documentServiceFactory.js → documentServiceFactory.cjs} +87 -33
  40. package/dist/documentServiceFactory.cjs.map +1 -0
  41. package/dist/documentServiceFactory.d.ts +5 -3
  42. package/dist/documentServiceFactory.d.ts.map +1 -1
  43. package/dist/{documentStorageService.js → documentStorageService.cjs} +18 -16
  44. package/dist/documentStorageService.cjs.map +1 -0
  45. package/dist/documentStorageService.d.ts +4 -3
  46. package/dist/documentStorageService.d.ts.map +1 -1
  47. package/dist/errorUtils.cjs +87 -0
  48. package/dist/errorUtils.cjs.map +1 -0
  49. package/dist/errorUtils.d.ts +56 -10
  50. package/dist/errorUtils.d.ts.map +1 -1
  51. package/dist/gitManager.cjs +46 -0
  52. package/dist/gitManager.cjs.map +1 -0
  53. package/dist/gitManager.d.ts +27 -0
  54. package/dist/gitManager.d.ts.map +1 -0
  55. package/dist/historian.cjs +72 -0
  56. package/dist/historian.cjs.map +1 -0
  57. package/dist/historian.d.ts +34 -0
  58. package/dist/historian.d.ts.map +1 -0
  59. package/dist/index.cjs +19 -0
  60. package/dist/index.cjs.map +1 -0
  61. package/dist/index.d.ts +5 -8
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/mapWithExpiration.cjs +105 -0
  64. package/dist/mapWithExpiration.cjs.map +1 -0
  65. package/dist/mapWithExpiration.d.ts +34 -0
  66. package/dist/mapWithExpiration.d.ts.map +1 -0
  67. package/dist/{nullBlobStorageService.js → nullBlobStorageService.cjs} +5 -5
  68. package/dist/nullBlobStorageService.cjs.map +1 -0
  69. package/dist/nullBlobStorageService.d.ts.map +1 -1
  70. package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
  71. package/dist/packageVersion.cjs.map +1 -0
  72. package/dist/packageVersion.d.ts +1 -1
  73. package/dist/packageVersion.d.ts.map +1 -1
  74. package/dist/{policies.js → policies.cjs} +1 -1
  75. package/dist/policies.cjs.map +1 -0
  76. package/dist/policies.d.ts +22 -9
  77. package/dist/policies.d.ts.map +1 -1
  78. package/dist/r11sSnapshotParser.cjs +73 -0
  79. package/dist/r11sSnapshotParser.cjs.map +1 -0
  80. package/dist/r11sSnapshotParser.d.ts +14 -0
  81. package/dist/r11sSnapshotParser.d.ts.map +1 -0
  82. package/dist/restWrapper.cjs +249 -0
  83. package/dist/restWrapper.cjs.map +1 -0
  84. package/dist/restWrapper.d.ts +32 -12
  85. package/dist/restWrapper.d.ts.map +1 -1
  86. package/dist/restWrapperBase.cjs +89 -0
  87. package/dist/restWrapperBase.cjs.map +1 -0
  88. package/dist/restWrapperBase.d.ts +26 -0
  89. package/dist/restWrapperBase.d.ts.map +1 -0
  90. package/dist/retriableGitManager.cjs +40 -0
  91. package/dist/retriableGitManager.cjs.map +1 -0
  92. package/dist/retriableGitManager.d.ts +13 -23
  93. package/dist/retriableGitManager.d.ts.map +1 -1
  94. package/dist/routerlicious-driver-alpha.d.ts +92 -0
  95. package/dist/routerlicious-driver-beta.d.ts +98 -0
  96. package/dist/routerlicious-driver-public.d.ts +98 -0
  97. package/dist/routerlicious-driver-untrimmed.d.ts +261 -0
  98. package/dist/{shreddedSummaryDocumentStorageService.js → shreddedSummaryDocumentStorageService.cjs} +41 -34
  99. package/dist/shreddedSummaryDocumentStorageService.cjs.map +1 -0
  100. package/dist/shreddedSummaryDocumentStorageService.d.ts +6 -5
  101. package/dist/shreddedSummaryDocumentStorageService.d.ts.map +1 -1
  102. package/dist/storageContracts.cjs +7 -0
  103. package/dist/storageContracts.cjs.map +1 -0
  104. package/dist/storageContracts.d.ts +45 -0
  105. package/dist/storageContracts.d.ts.map +1 -0
  106. package/dist/summaryTreeUploadManager.cjs +110 -0
  107. package/dist/summaryTreeUploadManager.cjs.map +1 -0
  108. package/dist/summaryTreeUploadManager.d.ts +23 -0
  109. package/dist/summaryTreeUploadManager.d.ts.map +1 -0
  110. package/dist/{tokens.js → tokens.cjs} +1 -1
  111. package/dist/tokens.cjs.map +1 -0
  112. package/dist/tokens.d.ts +34 -5
  113. package/dist/tokens.d.ts.map +1 -1
  114. package/dist/treeUtils.cjs +107 -0
  115. package/dist/treeUtils.cjs.map +1 -0
  116. package/dist/treeUtils.d.ts +58 -0
  117. package/dist/treeUtils.d.ts.map +1 -0
  118. package/dist/tsdoc-metadata.json +11 -0
  119. package/dist/{urlUtils.js → urlUtils.cjs} +21 -25
  120. package/dist/urlUtils.cjs.map +1 -0
  121. package/dist/urlUtils.d.ts +2 -2
  122. package/dist/urlUtils.d.ts.map +1 -1
  123. package/dist/wholeSummaryDocumentStorageService.cjs +253 -0
  124. package/dist/wholeSummaryDocumentStorageService.cjs.map +1 -0
  125. package/dist/wholeSummaryDocumentStorageService.d.ts +10 -8
  126. package/dist/wholeSummaryDocumentStorageService.d.ts.map +1 -1
  127. package/dist/wholeSummaryUploadManager.cjs +37 -0
  128. package/dist/wholeSummaryUploadManager.cjs.map +1 -0
  129. package/dist/wholeSummaryUploadManager.d.ts +16 -0
  130. package/dist/wholeSummaryUploadManager.d.ts.map +1 -0
  131. package/lib/{cache.d.ts → cache.d.mts} +7 -0
  132. package/lib/cache.d.mts.map +1 -0
  133. package/lib/cache.mjs +25 -0
  134. package/lib/cache.mjs.map +1 -0
  135. package/lib/contracts.d.mts +42 -0
  136. package/lib/contracts.d.mts.map +1 -0
  137. package/lib/{definitions.js → contracts.mjs} +1 -1
  138. package/lib/contracts.mjs.map +1 -0
  139. package/lib/{createNewUtils.d.ts → createNewUtils.d.mts} +1 -1
  140. package/lib/{createNewUtils.d.ts.map → createNewUtils.d.mts.map} +1 -1
  141. package/lib/{createNewUtils.js → createNewUtils.mjs} +7 -5
  142. package/lib/createNewUtils.mjs.map +1 -0
  143. package/lib/{defaultTokenProvider.d.ts → defaultTokenProvider.d.mts} +2 -1
  144. package/lib/defaultTokenProvider.d.mts.map +1 -0
  145. package/lib/{defaultTokenProvider.js → defaultTokenProvider.mjs} +2 -1
  146. package/lib/defaultTokenProvider.mjs.map +1 -0
  147. package/lib/definitions.d.mts.map +1 -0
  148. package/lib/definitions.mjs +6 -0
  149. package/{dist/definitions.js.map → lib/definitions.mjs.map} +1 -1
  150. package/lib/{deltaStorageService.d.ts → deltaStorageService.d.mts} +8 -7
  151. package/lib/deltaStorageService.d.mts.map +1 -0
  152. package/lib/deltaStorageService.mjs +117 -0
  153. package/lib/deltaStorageService.mjs.map +1 -0
  154. package/lib/{documentDeltaConnection.d.ts → documentDeltaConnection.d.mts} +3 -4
  155. package/lib/documentDeltaConnection.d.mts.map +1 -0
  156. package/lib/{documentDeltaConnection.js → documentDeltaConnection.mjs} +8 -13
  157. package/lib/documentDeltaConnection.mjs.map +1 -0
  158. package/lib/{documentService.d.ts → documentService.d.mts} +16 -8
  159. package/lib/documentService.d.mts.map +1 -0
  160. package/lib/{documentService.js → documentService.mjs} +60 -44
  161. package/lib/documentService.mjs.map +1 -0
  162. package/lib/{documentServiceFactory.d.ts → documentServiceFactory.d.mts} +7 -5
  163. package/lib/documentServiceFactory.d.mts.map +1 -0
  164. package/lib/{documentServiceFactory.js → documentServiceFactory.mjs} +88 -34
  165. package/lib/documentServiceFactory.mjs.map +1 -0
  166. package/lib/{documentStorageService.d.ts → documentStorageService.d.mts} +7 -6
  167. package/lib/documentStorageService.d.mts.map +1 -0
  168. package/lib/{documentStorageService.js → documentStorageService.mjs} +19 -17
  169. package/lib/documentStorageService.mjs.map +1 -0
  170. package/lib/errorUtils.d.mts +93 -0
  171. package/lib/errorUtils.d.mts.map +1 -0
  172. package/lib/errorUtils.mjs +81 -0
  173. package/lib/errorUtils.mjs.map +1 -0
  174. package/lib/gitManager.d.mts +27 -0
  175. package/lib/gitManager.d.mts.map +1 -0
  176. package/lib/gitManager.mjs +42 -0
  177. package/lib/gitManager.mjs.map +1 -0
  178. package/lib/historian.d.mts +34 -0
  179. package/lib/historian.d.mts.map +1 -0
  180. package/lib/historian.mjs +67 -0
  181. package/lib/historian.mjs.map +1 -0
  182. package/lib/index.d.mts +10 -0
  183. package/lib/index.d.mts.map +1 -0
  184. package/lib/index.mjs +8 -0
  185. package/lib/index.mjs.map +1 -0
  186. package/lib/mapWithExpiration.d.mts +34 -0
  187. package/lib/mapWithExpiration.d.mts.map +1 -0
  188. package/lib/mapWithExpiration.mjs +101 -0
  189. package/lib/mapWithExpiration.mjs.map +1 -0
  190. package/lib/nullBlobStorageService.d.mts.map +1 -0
  191. package/lib/{nullBlobStorageService.js → nullBlobStorageService.mjs} +5 -5
  192. package/lib/nullBlobStorageService.mjs.map +1 -0
  193. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
  194. package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
  195. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  196. package/lib/packageVersion.mjs.map +1 -0
  197. package/lib/{policies.d.ts → policies.d.mts} +22 -9
  198. package/lib/policies.d.mts.map +1 -0
  199. package/lib/{policies.js → policies.mjs} +1 -1
  200. package/lib/policies.mjs.map +1 -0
  201. package/lib/r11sSnapshotParser.d.mts +14 -0
  202. package/lib/r11sSnapshotParser.d.mts.map +1 -0
  203. package/lib/r11sSnapshotParser.mjs +69 -0
  204. package/lib/r11sSnapshotParser.mjs.map +1 -0
  205. package/lib/restWrapper.d.mts +53 -0
  206. package/lib/restWrapper.d.mts.map +1 -0
  207. package/lib/restWrapper.mjs +236 -0
  208. package/lib/restWrapper.mjs.map +1 -0
  209. package/lib/restWrapperBase.d.mts +26 -0
  210. package/lib/restWrapperBase.d.mts.map +1 -0
  211. package/lib/restWrapperBase.mjs +84 -0
  212. package/lib/restWrapperBase.mjs.map +1 -0
  213. package/lib/retriableGitManager.d.mts +24 -0
  214. package/lib/retriableGitManager.d.mts.map +1 -0
  215. package/lib/retriableGitManager.mjs +36 -0
  216. package/lib/retriableGitManager.mjs.map +1 -0
  217. package/lib/routerlicious-driver-alpha.d.mts +92 -0
  218. package/lib/routerlicious-driver-beta.d.mts +98 -0
  219. package/lib/routerlicious-driver-public.d.mts +98 -0
  220. package/lib/routerlicious-driver-untrimmed.d.mts +261 -0
  221. package/lib/{shreddedSummaryDocumentStorageService.d.ts → shreddedSummaryDocumentStorageService.d.mts} +9 -8
  222. package/lib/shreddedSummaryDocumentStorageService.d.mts.map +1 -0
  223. package/lib/{shreddedSummaryDocumentStorageService.js → shreddedSummaryDocumentStorageService.mjs} +41 -34
  224. package/lib/shreddedSummaryDocumentStorageService.mjs.map +1 -0
  225. package/lib/storageContracts.d.mts +45 -0
  226. package/lib/storageContracts.d.mts.map +1 -0
  227. package/lib/storageContracts.mjs +6 -0
  228. package/lib/storageContracts.mjs.map +1 -0
  229. package/lib/summaryTreeUploadManager.d.mts +23 -0
  230. package/lib/summaryTreeUploadManager.d.mts.map +1 -0
  231. package/lib/summaryTreeUploadManager.mjs +106 -0
  232. package/lib/summaryTreeUploadManager.mjs.map +1 -0
  233. package/lib/{tokens.d.ts → tokens.d.mts} +34 -5
  234. package/lib/tokens.d.mts.map +1 -0
  235. package/lib/{tokens.js → tokens.mjs} +1 -1
  236. package/lib/tokens.mjs.map +1 -0
  237. package/lib/treeUtils.d.mts +58 -0
  238. package/lib/treeUtils.d.mts.map +1 -0
  239. package/lib/treeUtils.mjs +100 -0
  240. package/lib/treeUtils.mjs.map +1 -0
  241. package/lib/{urlUtils.d.ts → urlUtils.d.mts} +2 -2
  242. package/lib/urlUtils.d.mts.map +1 -0
  243. package/lib/urlUtils.mjs +38 -0
  244. package/lib/urlUtils.mjs.map +1 -0
  245. package/lib/{wholeSummaryDocumentStorageService.d.ts → wholeSummaryDocumentStorageService.d.mts} +12 -10
  246. package/lib/wholeSummaryDocumentStorageService.d.mts.map +1 -0
  247. package/lib/wholeSummaryDocumentStorageService.mjs +249 -0
  248. package/lib/wholeSummaryDocumentStorageService.mjs.map +1 -0
  249. package/lib/wholeSummaryUploadManager.d.mts +16 -0
  250. package/lib/wholeSummaryUploadManager.d.mts.map +1 -0
  251. package/lib/wholeSummaryUploadManager.mjs +33 -0
  252. package/lib/wholeSummaryUploadManager.mjs.map +1 -0
  253. package/package.json +104 -68
  254. package/prettier.config.cjs +8 -0
  255. package/src/cache.ts +25 -9
  256. package/src/contracts.ts +60 -0
  257. package/src/createNewUtils.ts +26 -24
  258. package/src/defaultTokenProvider.ts +14 -15
  259. package/src/definitions.ts +2 -2
  260. package/src/deltaStorageService.ts +145 -91
  261. package/src/documentDeltaConnection.ts +54 -52
  262. package/src/documentService.ts +272 -242
  263. package/src/documentServiceFactory.ts +338 -237
  264. package/src/documentStorageService.ts +92 -84
  265. package/src/errorUtils.ts +117 -79
  266. package/src/gitManager.ts +67 -0
  267. package/src/historian.ts +121 -0
  268. package/src/index.ts +15 -8
  269. package/src/mapWithExpiration.ts +124 -0
  270. package/src/nullBlobStorageService.ts +24 -21
  271. package/src/packageVersion.ts +1 -1
  272. package/src/policies.ts +51 -38
  273. package/src/r11sSnapshotParser.ts +82 -0
  274. package/src/restWrapper.ts +400 -216
  275. package/src/restWrapperBase.ts +146 -0
  276. package/src/retriableGitManager.ts +76 -153
  277. package/src/shreddedSummaryDocumentStorageService.ts +214 -195
  278. package/src/storageContracts.ts +63 -0
  279. package/src/summaryTreeUploadManager.ts +155 -0
  280. package/src/tokens.ts +74 -39
  281. package/src/treeUtils.ts +138 -0
  282. package/src/urlUtils.ts +27 -28
  283. package/src/wholeSummaryDocumentStorageService.ts +352 -252
  284. package/src/wholeSummaryUploadManager.ts +64 -0
  285. package/tsc-multi.test.json +4 -0
  286. package/tsconfig.json +10 -13
  287. package/dist/cache.js +0 -20
  288. package/dist/cache.js.map +0 -1
  289. package/dist/createNewUtils.js.map +0 -1
  290. package/dist/defaultTokenProvider.js.map +0 -1
  291. package/dist/deltaStorageService.js +0 -82
  292. package/dist/deltaStorageService.js.map +0 -1
  293. package/dist/documentDeltaConnection.js.map +0 -1
  294. package/dist/documentService.js.map +0 -1
  295. package/dist/documentServiceFactory.js.map +0 -1
  296. package/dist/documentStorageService.js.map +0 -1
  297. package/dist/errorUtils.js +0 -56
  298. package/dist/errorUtils.js.map +0 -1
  299. package/dist/index.js +0 -25
  300. package/dist/index.js.map +0 -1
  301. package/dist/nullBlobStorageService.js.map +0 -1
  302. package/dist/packageVersion.js.map +0 -1
  303. package/dist/policies.js.map +0 -1
  304. package/dist/restWrapper.js +0 -153
  305. package/dist/restWrapper.js.map +0 -1
  306. package/dist/retriableGitManager.js +0 -76
  307. package/dist/retriableGitManager.js.map +0 -1
  308. package/dist/shreddedSummaryDocumentStorageService.js.map +0 -1
  309. package/dist/tokens.js.map +0 -1
  310. package/dist/urlUtils.js.map +0 -1
  311. package/dist/wholeSummaryDocumentStorageService.js +0 -191
  312. package/dist/wholeSummaryDocumentStorageService.js.map +0 -1
  313. package/lib/cache.d.ts.map +0 -1
  314. package/lib/cache.js +0 -16
  315. package/lib/cache.js.map +0 -1
  316. package/lib/createNewUtils.js.map +0 -1
  317. package/lib/defaultTokenProvider.d.ts.map +0 -1
  318. package/lib/defaultTokenProvider.js.map +0 -1
  319. package/lib/definitions.d.ts.map +0 -1
  320. package/lib/definitions.js.map +0 -1
  321. package/lib/deltaStorageService.d.ts.map +0 -1
  322. package/lib/deltaStorageService.js +0 -77
  323. package/lib/deltaStorageService.js.map +0 -1
  324. package/lib/documentDeltaConnection.d.ts.map +0 -1
  325. package/lib/documentDeltaConnection.js.map +0 -1
  326. package/lib/documentService.d.ts.map +0 -1
  327. package/lib/documentService.js.map +0 -1
  328. package/lib/documentServiceFactory.d.ts.map +0 -1
  329. package/lib/documentServiceFactory.js.map +0 -1
  330. package/lib/documentStorageService.d.ts.map +0 -1
  331. package/lib/documentStorageService.js.map +0 -1
  332. package/lib/errorUtils.d.ts +0 -47
  333. package/lib/errorUtils.d.ts.map +0 -1
  334. package/lib/errorUtils.js +0 -50
  335. package/lib/errorUtils.js.map +0 -1
  336. package/lib/index.d.ts +0 -13
  337. package/lib/index.d.ts.map +0 -1
  338. package/lib/index.js +0 -13
  339. package/lib/index.js.map +0 -1
  340. package/lib/nullBlobStorageService.d.ts.map +0 -1
  341. package/lib/nullBlobStorageService.js.map +0 -1
  342. package/lib/packageVersion.js.map +0 -1
  343. package/lib/policies.d.ts.map +0 -1
  344. package/lib/policies.js.map +0 -1
  345. package/lib/restWrapper.d.ts +0 -33
  346. package/lib/restWrapper.d.ts.map +0 -1
  347. package/lib/restWrapper.js +0 -144
  348. package/lib/restWrapper.js.map +0 -1
  349. package/lib/retriableGitManager.d.ts +0 -34
  350. package/lib/retriableGitManager.d.ts.map +0 -1
  351. package/lib/retriableGitManager.js +0 -72
  352. package/lib/retriableGitManager.js.map +0 -1
  353. package/lib/shreddedSummaryDocumentStorageService.d.ts.map +0 -1
  354. package/lib/shreddedSummaryDocumentStorageService.js.map +0 -1
  355. package/lib/tokens.d.ts.map +0 -1
  356. package/lib/tokens.js.map +0 -1
  357. package/lib/urlUtils.d.ts.map +0 -1
  358. package/lib/urlUtils.js +0 -42
  359. package/lib/urlUtils.js.map +0 -1
  360. package/lib/wholeSummaryDocumentStorageService.d.ts.map +0 -1
  361. package/lib/wholeSummaryDocumentStorageService.js +0 -187
  362. package/lib/wholeSummaryDocumentStorageService.js.map +0 -1
  363. package/tsconfig.esnext.json +0 -7
  364. /package/lib/{definitions.d.ts → definitions.d.mts} +0 -0
  365. /package/lib/{nullBlobStorageService.d.ts → nullBlobStorageService.d.mts} +0 -0
@@ -2,235 +2,419 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import * as querystring from "querystring";
6
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
8
- import { RateLimiter } from "@fluidframework/driver-utils";
5
+
6
+ import { ITelemetryProperties } from "@fluidframework/core-interfaces";
7
+ import {
8
+ ITelemetryLoggerExt,
9
+ PerformanceEvent,
10
+ numberFromString,
11
+ } from "@fluidframework/telemetry-utils";
12
+ import { assert } from "@fluidframework/core-utils";
13
+ import { fromUtf8ToBase64, performance } from "@fluid-internal/client-utils";
14
+ import { GenericNetworkError, NonRetryableError, RateLimiter } from "@fluidframework/driver-utils";
9
15
  import {
10
- getAuthorizationTokenFromCredentials,
11
- RestLessClient,
12
- RestWrapper,
16
+ CorrelationIdHeaderName,
17
+ DriverVersionHeaderName,
18
+ getAuthorizationTokenFromCredentials,
19
+ RestLessClient,
13
20
  } from "@fluidframework/server-services-client";
14
- import { PerformanceEvent } from "@fluidframework/telemetry-utils";
15
21
  import fetch from "cross-fetch";
16
- import type { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
22
+ import type { AxiosRequestConfig, RawAxiosRequestHeaders } from "axios";
17
23
  import safeStringify from "json-stringify-safe";
18
- import { v4 as uuid } from "uuid";
19
- import { throwR11sNetworkError } from "./errorUtils";
20
- import { ITokenProvider } from "./tokens";
24
+ import { RouterliciousErrorTypes, throwR11sNetworkError } from "./errorUtils";
25
+ import { ITokenProvider, ITokenResponse } from "./tokens";
21
26
  import { pkgVersion as driverVersion } from "./packageVersion";
27
+ import { QueryStringType, RestWrapper } from "./restWrapperBase";
28
+
29
+ type AuthorizationHeaderGetter = (token: ITokenResponse) => string;
30
+ export type TokenFetcher = (refresh?: boolean) => Promise<ITokenResponse>;
22
31
 
23
- type AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;
24
-
25
- const axiosRequestConfigToFetchRequestConfig = (requestConfig: AxiosRequestConfig): [RequestInfo, RequestInit] => {
26
- const requestInfo: string = requestConfig.baseURL !== undefined
27
- ? `${requestConfig.baseURL}${requestConfig.url ?? ""}`
28
- : requestConfig.url ?? "";
29
- const requestInit: RequestInit = {
30
- method: requestConfig.method,
31
- // NOTE: I believe that although the Axios type permits non-string values in the header, here we are
32
- // guaranteed the requestConfig only has string values in its header.
33
- headers: requestConfig.headers as Record<string, string>,
34
- body: requestConfig.data,
35
- };
36
- return [requestInfo, requestInit];
32
+ const axiosRequestConfigToFetchRequestConfig = (
33
+ requestConfig: AxiosRequestConfig,
34
+ ): [RequestInfo, RequestInit] => {
35
+ const requestInfo: string =
36
+ requestConfig.baseURL !== undefined
37
+ ? `${requestConfig.baseURL}${requestConfig.url ?? ""}`
38
+ : requestConfig.url ?? "";
39
+ const requestInit: RequestInit = {
40
+ method: requestConfig.method,
41
+ // NOTE: I believe that although the Axios type permits non-string values in the header, here we are
42
+ // guaranteed the requestConfig only has string values in its header.
43
+ headers: requestConfig.headers as Record<string, string>,
44
+ body: requestConfig.data,
45
+ };
46
+ return [requestInfo, requestInit];
37
47
  };
38
48
 
49
+ export interface IR11sResponse<T> {
50
+ content: T;
51
+ headers: Map<string, string>;
52
+ propsToLog: ITelemetryProperties;
53
+ requestUrl: string;
54
+ }
55
+
56
+ /**
57
+ * A utility function to create a Routerlicious response without any additional props as we might not have them always.
58
+ * @param content - Response which is equivalent to content.
59
+ * @returns A Routerlicious response without any extra props.
60
+ */
61
+ export function createR11sResponseFromContent<T>(content: T): IR11sResponse<T> {
62
+ return {
63
+ content,
64
+ headers: new Map(),
65
+ propsToLog: {},
66
+ requestUrl: "",
67
+ };
68
+ }
69
+
70
+ function headersToMap(headers: Headers) {
71
+ const newHeaders = new Map<string, string>();
72
+ for (const [key, value] of headers.entries()) {
73
+ newHeaders.set(key, value);
74
+ }
75
+ return newHeaders;
76
+ }
77
+
78
+ export function getPropsToLogFromResponse(headers: {
79
+ // eslint-disable-next-line @rushstack/no-new-null
80
+ get: (id: string) => string | undefined | null;
81
+ }) {
82
+ interface LoggingHeader {
83
+ headerName: string;
84
+ logName: string;
85
+ }
86
+
87
+ // We rename headers so that otel doesn't scrub them away. Otel doesn't allow
88
+ // certain characters in headers including '-'
89
+ const headersToLog: LoggingHeader[] = [
90
+ { headerName: CorrelationIdHeaderName, logName: "requestCorrelationId" },
91
+ { headerName: "content-encoding", logName: "contentEncoding" },
92
+ { headerName: "content-type", logName: "contentType" },
93
+ ];
94
+ const additionalProps: ITelemetryProperties = {
95
+ contentsize: numberFromString(headers.get("content-length")),
96
+ };
97
+ headersToLog.forEach((header) => {
98
+ const headerValue = headers.get(header.headerName);
99
+ if (headerValue !== undefined && headerValue !== null) {
100
+ additionalProps[header.logName] = headerValue;
101
+ }
102
+ });
103
+
104
+ return additionalProps;
105
+ }
106
+
39
107
  export class RouterliciousRestWrapper extends RestWrapper {
40
- private authorizationHeader: string | undefined;
41
- private readonly restLess = new RestLessClient();
42
-
43
- constructor(
44
- logger: ITelemetryLogger,
45
- private readonly rateLimiter: RateLimiter,
46
- private readonly getAuthorizationHeader: AuthorizationHeaderGetter,
47
- private readonly useRestLess: boolean,
48
- baseurl?: string,
49
- defaultQueryString: querystring.ParsedUrlQueryInput = {},
50
- ) {
51
- super(baseurl, defaultQueryString);
52
- }
53
-
54
- public async load() {
55
- this.authorizationHeader = await this.getAuthorizationHeader();
56
- }
57
-
58
- protected async request<T>(requestConfig: AxiosRequestConfig, statusCode: number, canRetry = true): Promise<T> {
59
- const config = {
60
- ...requestConfig,
61
- headers: this.generateHeaders(requestConfig.headers),
62
- };
63
-
64
- const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
65
- const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
66
-
67
- const response: Response = await this.rateLimiter.schedule(async () => fetch(...fetchRequestConfig)
68
- .catch(async (error) => {
69
- // Browser Fetch throws a TypeError on network error, `node-fetch` throws a FetchError
70
- const isNetworkError = ["TypeError", "FetchError"].includes(error?.name);
71
- throwR11sNetworkError(
72
- isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error));
73
- }));
74
-
75
- const responseBody: any = response.headers.get("content-type")?.includes("application/json")
76
- ? await response.json()
77
- : await response.text();
78
-
79
- // Success
80
- if (response.ok || response.status === statusCode) {
81
- const result: T = responseBody;
82
- return result;
83
- }
84
- // Failure
85
- if (response.status === 401 && canRetry) {
86
- // Refresh Authorization header and retry once
87
- this.authorizationHeader = await this.getAuthorizationHeader(true /* refreshToken */);
88
- return this.request<T>(config, statusCode, false);
89
- }
90
- if (response.status === 429 && responseBody?.retryAfter > 0) {
91
- // Retry based on retryAfter[Seconds]
92
- return new Promise<T>((resolve, reject) => setTimeout(() => {
93
- this.request<T>(config, statusCode)
94
- .then(resolve)
95
- .catch(reject);
96
- }, responseBody.retryAfter * 1000));
97
- }
98
-
99
- const responseSummary = responseBody !== undefined
100
- ? typeof responseBody === "string" ? responseBody : safeStringify(responseBody)
101
- : response.statusText;
102
- throwR11sNetworkError(
103
- `R11s fetch error: ${responseSummary}`,
104
- response.status,
105
- responseBody?.retryAfter,
106
- );
107
- }
108
-
109
- private generateHeaders(requestHeaders?: AxiosRequestHeaders | undefined): Record<string, string> {
110
- const correlationId = requestHeaders?.["x-correlation-id"] || uuid();
111
-
112
- return {
113
- ...requestHeaders,
114
- // TODO: replace header names with CorrelationIdHeaderName and DriverVersionHeaderName from services-client
115
- // NOTE: Can correlationId actually be number | true?
116
- "x-correlation-id": correlationId as string,
117
- "x-driver-version": driverVersion,
118
- // NOTE: If this.authorizationHeader is undefined, should "Authorization" be removed entirely?
119
- "Authorization": this.authorizationHeader!,
120
- };
121
- }
108
+ private readonly restLess = new RestLessClient();
109
+ private token: ITokenResponse | undefined;
110
+
111
+ constructor(
112
+ logger: ITelemetryLoggerExt,
113
+ private readonly rateLimiter: RateLimiter,
114
+ private readonly fetchRefreshedToken: TokenFetcher,
115
+ private readonly getAuthorizationHeader: AuthorizationHeaderGetter,
116
+ private readonly useRestLess: boolean,
117
+ baseurl?: string,
118
+ private tokenP?: Promise<ITokenResponse>,
119
+ defaultQueryString: QueryStringType = {},
120
+ ) {
121
+ super(baseurl, defaultQueryString);
122
+ }
123
+
124
+ protected async request<T>(
125
+ requestConfig: AxiosRequestConfig,
126
+ statusCode: number,
127
+ canRetry = true,
128
+ ): Promise<IR11sResponse<T>> {
129
+ const config = {
130
+ ...requestConfig,
131
+ headers: await this.generateHeaders(requestConfig.headers),
132
+ };
133
+
134
+ const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
135
+ const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
136
+
137
+ const res = await this.rateLimiter.schedule(async () => {
138
+ const perfStart = performance.now();
139
+ const result = await fetch(...fetchRequestConfig).catch(async (error) => {
140
+ // Browser Fetch throws a TypeError on network error, `node-fetch` throws a FetchError
141
+ const isNetworkError = ["TypeError", "FetchError"].includes(error?.name);
142
+ const errorMessage = isNetworkError
143
+ ? `NetworkError: ${error.message}`
144
+ : safeStringify(error);
145
+ // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw
146
+ // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and
147
+ // the error message will start with NetworkError as defined in restWrapper.ts
148
+ // If there exists a self-signed SSL certificates error, throw a NonRetryableError
149
+ // TODO: instead of relying on string matching, filter error based on the error code like we do for websocket connections
150
+ const err = errorMessage.includes("failed, reason: self signed certificate")
151
+ ? new NonRetryableError(errorMessage, RouterliciousErrorTypes.sslCertError, {
152
+ driverVersion,
153
+ })
154
+ : new GenericNetworkError(
155
+ errorMessage,
156
+ errorMessage.startsWith("NetworkError"),
157
+ { driverVersion },
158
+ );
159
+ throw err;
160
+ });
161
+ return {
162
+ response: result,
163
+ duration: performance.now() - perfStart,
164
+ };
165
+ });
166
+
167
+ const response = res.response;
168
+
169
+ let start = performance.now();
170
+ const text = await response.text();
171
+ const receiveContentTime = performance.now() - start;
172
+
173
+ const bodySize = text.length;
174
+ start = performance.now();
175
+ const responseBody: any = response.headers.get("content-type")?.includes("application/json")
176
+ ? JSON.parse(text)
177
+ : text;
178
+ const parseTime = performance.now() - start;
179
+
180
+ // Success
181
+ if (response.ok || response.status === statusCode) {
182
+ const result = responseBody as T;
183
+ const headers = headersToMap(response.headers);
184
+ return {
185
+ content: result,
186
+ headers,
187
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
188
+ requestUrl: fetchRequestConfig[0].toString(),
189
+ propsToLog: {
190
+ ...getPropsToLogFromResponse(headers),
191
+ bodySize,
192
+ receiveContentTime,
193
+ parseTime,
194
+ fetchTime: res.duration,
195
+ },
196
+ };
197
+ }
198
+ // Failure
199
+ if (response.status === 401 && canRetry) {
200
+ // Refresh Authorization header and retry once
201
+ this.token = await this.fetchRefreshedToken(true /* refreshToken */);
202
+ return this.request<T>(config, statusCode, false);
203
+ }
204
+ if (response.status === 429 && responseBody?.retryAfter > 0) {
205
+ // Retry based on retryAfter[Seconds]
206
+ return new Promise<IR11sResponse<T>>((resolve, reject) =>
207
+ setTimeout(() => {
208
+ this.request<T>(config, statusCode).then(resolve).catch(reject);
209
+ }, responseBody.retryAfter * 1000),
210
+ );
211
+ }
212
+
213
+ const responseSummary =
214
+ responseBody !== undefined
215
+ ? typeof responseBody === "string"
216
+ ? responseBody
217
+ : safeStringify(responseBody)
218
+ : response.statusText;
219
+ throwR11sNetworkError(
220
+ `R11s fetch error: ${responseSummary}`,
221
+ response.status,
222
+ responseBody?.retryAfter,
223
+ );
224
+ }
225
+
226
+ private async generateHeaders(
227
+ requestHeaders?: RawAxiosRequestHeaders | undefined,
228
+ ): Promise<RawAxiosRequestHeaders> {
229
+ const token = await this.getToken();
230
+ assert(token !== undefined, 0x679 /* token should be present */);
231
+ const headers: RawAxiosRequestHeaders = {
232
+ ...requestHeaders,
233
+ [DriverVersionHeaderName]: driverVersion,
234
+ // NOTE: If this.authorizationHeader is undefined, should "Authorization" be removed entirely?
235
+ Authorization: this.getAuthorizationHeader(token),
236
+ };
237
+ return headers;
238
+ }
239
+
240
+ public async getToken(): Promise<ITokenResponse> {
241
+ if (this.token !== undefined) {
242
+ return this.token;
243
+ }
244
+ const token = await (this.tokenP ?? this.fetchRefreshedToken());
245
+ this.setToken(token);
246
+ this.tokenP = undefined;
247
+ return token;
248
+ }
249
+
250
+ public setToken(token: ITokenResponse) {
251
+ this.token = token;
252
+ }
122
253
  }
123
254
 
124
255
  export class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {
125
- private constructor(
126
- logger: ITelemetryLogger,
127
- rateLimiter: RateLimiter,
128
- getAuthorizationHeader: AuthorizationHeaderGetter,
129
- useRestLess: boolean,
130
- baseurl?: string,
131
- defaultQueryString: querystring.ParsedUrlQueryInput = {},
132
- ) {
133
- super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);
134
- }
135
-
136
- public static async load(
137
- tenantId: string,
138
- documentId: string,
139
- tokenProvider: ITokenProvider,
140
- logger: ITelemetryLogger,
141
- rateLimiter: RateLimiter,
142
- useRestLess: boolean,
143
- baseurl?: string,
144
- ): Promise<RouterliciousStorageRestWrapper> {
145
- const defaultQueryString = {
146
- token: `${fromUtf8ToBase64(tenantId)}`,
147
- };
148
- const getAuthorizationHeader: AuthorizationHeaderGetter = async (refreshToken?: boolean): Promise<string> => {
149
- return PerformanceEvent.timedExecAsync(
150
- logger,
151
- {
152
- eventName: "FetchStorageToken",
153
- docId: documentId,
154
- },
155
- async () => {
156
- // Craft credentials using tenant id and token
157
- const storageToken = await tokenProvider.fetchStorageToken(
158
- tenantId,
159
- documentId,
160
- refreshToken,
161
- );
162
- const credentials = {
163
- password: storageToken.jwt,
164
- user: tenantId,
165
- };
166
- return getAuthorizationTokenFromCredentials(credentials);
167
- },
168
- );
169
- };
170
-
171
- const restWrapper = new RouterliciousStorageRestWrapper(
172
- logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);
173
- try {
174
- await restWrapper.load();
175
- } catch (e) {
176
- logger.sendErrorEvent({
177
- eventName: "R11sRestWrapperLoadFailure",
178
- }, e);
179
- await restWrapper.load();
180
- }
181
- return restWrapper;
182
- }
256
+ private constructor(
257
+ logger: ITelemetryLoggerExt,
258
+ rateLimiter: RateLimiter,
259
+ fetchToken: TokenFetcher,
260
+ getAuthorizationHeader: AuthorizationHeaderGetter,
261
+ useRestLess: boolean,
262
+ baseurl?: string,
263
+ initialTokenP?: Promise<ITokenResponse>,
264
+ defaultQueryString: QueryStringType = {},
265
+ ) {
266
+ super(
267
+ logger,
268
+ rateLimiter,
269
+ fetchToken,
270
+ getAuthorizationHeader,
271
+ useRestLess,
272
+ baseurl,
273
+ initialTokenP,
274
+ defaultQueryString,
275
+ );
276
+ }
277
+
278
+ public static async load(
279
+ tenantId: string,
280
+ tokenFetcher: TokenFetcher,
281
+ logger: ITelemetryLoggerExt,
282
+ rateLimiter: RateLimiter,
283
+ useRestLess: boolean,
284
+ baseurl?: string,
285
+ initialTokenP?: Promise<ITokenResponse>,
286
+ ): Promise<RouterliciousStorageRestWrapper> {
287
+ const defaultQueryString = {
288
+ token: `${fromUtf8ToBase64(tenantId)}`,
289
+ };
290
+
291
+ const getAuthorizationHeader: AuthorizationHeaderGetter = (
292
+ token: ITokenResponse,
293
+ ): string => {
294
+ const credentials = {
295
+ password: token.jwt,
296
+ user: tenantId,
297
+ };
298
+ return getAuthorizationTokenFromCredentials(credentials);
299
+ };
300
+
301
+ const restWrapper = new RouterliciousStorageRestWrapper(
302
+ logger,
303
+ rateLimiter,
304
+ tokenFetcher,
305
+ getAuthorizationHeader,
306
+ useRestLess,
307
+ baseurl,
308
+ initialTokenP,
309
+ defaultQueryString,
310
+ );
311
+
312
+ return restWrapper;
313
+ }
183
314
  }
184
315
 
185
316
  export class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {
186
- private constructor(
187
- logger: ITelemetryLogger,
188
- rateLimiter: RateLimiter,
189
- getAuthorizationHeader: AuthorizationHeaderGetter,
190
- useRestLess: boolean,
191
- baseurl?: string,
192
- defaultQueryString: querystring.ParsedUrlQueryInput = {},
193
- ) {
194
- super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);
195
- }
196
-
197
- public static async load(
198
- tenantId: string,
199
- documentId: string | undefined,
200
- tokenProvider: ITokenProvider,
201
- logger: ITelemetryLogger,
202
- rateLimiter: RateLimiter,
203
- useRestLess: boolean,
204
- baseurl?: string,
205
- ): Promise<RouterliciousOrdererRestWrapper> {
206
- const getAuthorizationHeader: AuthorizationHeaderGetter = async (refreshToken?: boolean): Promise<string> => {
207
- return PerformanceEvent.timedExecAsync(
208
- logger,
209
- {
210
- eventName: "FetchOrdererToken",
211
- docId: documentId,
212
- },
213
- async () => {
214
- const ordererToken = await tokenProvider.fetchOrdererToken(
215
- tenantId,
216
- documentId,
217
- refreshToken,
218
- );
219
- return `Basic ${ordererToken.jwt}`;
220
- },
221
- );
222
- };
223
-
224
- const restWrapper = new RouterliciousOrdererRestWrapper(
225
- logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl);
226
- try {
227
- await restWrapper.load();
228
- } catch (e) {
229
- logger.sendErrorEvent({
230
- eventName: "R11sRestWrapperLoadFailure",
231
- }, e);
232
- await restWrapper.load();
233
- }
234
- return restWrapper;
235
- }
317
+ private constructor(
318
+ logger: ITelemetryLoggerExt,
319
+ rateLimiter: RateLimiter,
320
+ fetchToken: TokenFetcher,
321
+ getAuthorizationHeader: AuthorizationHeaderGetter,
322
+ useRestLess: boolean,
323
+ baseurl?: string,
324
+ initialTokenP?: Promise<ITokenResponse>,
325
+ defaultQueryString: QueryStringType = {},
326
+ ) {
327
+ super(
328
+ logger,
329
+ rateLimiter,
330
+ fetchToken,
331
+ getAuthorizationHeader,
332
+ useRestLess,
333
+ baseurl,
334
+ initialTokenP,
335
+ defaultQueryString,
336
+ );
337
+ }
338
+
339
+ public static async load(
340
+ tokenFetcher: TokenFetcher,
341
+ logger: ITelemetryLoggerExt,
342
+ rateLimiter: RateLimiter,
343
+ useRestLess: boolean,
344
+ baseurl?: string,
345
+ initialTokenP?: Promise<ITokenResponse>,
346
+ ): Promise<RouterliciousOrdererRestWrapper> {
347
+ const getAuthorizationHeader: AuthorizationHeaderGetter = (
348
+ token: ITokenResponse,
349
+ ): string => {
350
+ return `Basic ${token.jwt}`;
351
+ };
352
+
353
+ const restWrapper = new RouterliciousOrdererRestWrapper(
354
+ logger,
355
+ rateLimiter,
356
+ tokenFetcher,
357
+ getAuthorizationHeader,
358
+ useRestLess,
359
+ baseurl,
360
+ initialTokenP,
361
+ );
362
+
363
+ return restWrapper;
364
+ }
365
+ }
366
+
367
+ export function toInstrumentedR11sOrdererTokenFetcher(
368
+ tenantId: string,
369
+ documentId: string | undefined,
370
+ tokenProvider: ITokenProvider,
371
+ logger: ITelemetryLoggerExt,
372
+ ): TokenFetcher {
373
+ const fetchOrdererToken = async (refreshToken?: boolean): Promise<ITokenResponse> => {
374
+ return PerformanceEvent.timedExecAsync(
375
+ logger,
376
+ {
377
+ eventName: "FetchOrdererToken",
378
+ docId: documentId,
379
+ },
380
+ async () => {
381
+ const ordererToken = await tokenProvider.fetchOrdererToken(
382
+ tenantId,
383
+ documentId,
384
+ refreshToken,
385
+ );
386
+
387
+ return ordererToken;
388
+ },
389
+ );
390
+ };
391
+ return fetchOrdererToken;
392
+ }
393
+
394
+ export function toInstrumentedR11sStorageTokenFetcher(
395
+ tenantId: string,
396
+ documentId: string,
397
+ tokenProvider: ITokenProvider,
398
+ logger: ITelemetryLoggerExt,
399
+ ): TokenFetcher {
400
+ const fetchStorageToken = async (refreshToken?: boolean): Promise<ITokenResponse> => {
401
+ return PerformanceEvent.timedExecAsync(
402
+ logger,
403
+ {
404
+ eventName: "FetchStorageToken",
405
+ docId: documentId,
406
+ },
407
+ async () => {
408
+ // Craft credentials using tenant id and token
409
+ const storageToken = await tokenProvider.fetchStorageToken(
410
+ tenantId,
411
+ documentId,
412
+ refreshToken,
413
+ );
414
+
415
+ return storageToken;
416
+ },
417
+ );
418
+ };
419
+ return fetchStorageToken;
236
420
  }